diff --git a/.gitignore b/.gitignore
index f808462..ad6c226 100644
--- a/.gitignore
+++ b/.gitignore
@@ -4,6 +4,7 @@
/storage/*.key
/vendor
/.idea
+.editorconfig
/.vscode
/.vagrant
Homestead.json
diff --git a/app/Http/Controllers/AdminHomeController.php b/app/Http/Controllers/AdminHomeController.php
index 59344f5..f5ff194 100644
--- a/app/Http/Controllers/AdminHomeController.php
+++ b/app/Http/Controllers/AdminHomeController.php
@@ -17,55 +17,6 @@ class AdminHomeController extends Controller
return View('a.service',['o'=>$o]);
}
- public function service_update(Request $request, Service $o)
- {
- if (! $o->validStatus(strtolower($request->input('action'))))
- return $this->service($o);
-
- $action = strtolower($request->input('action'));
-
- switch ($action)
- {
- case 'approve':
- // Send an email to the supplier.
- // @todo Change to address to suppliers email address.
- Mail::to('help@graytech.net.au')
- ->queue((new OrderRequestApprove($o,$request->input('order_notes') ?: 'NONE'))->onQueue('high'));
-
- // Send an email to the client.
- // @todo Your order has been submitted to supplier.
-
- // Update the service to "ORDER-SENT"
- $o->nextStatus($action);
-
- break;
-
- case 'reject':
- $o->order_info = array_merge($o->order_info ? $o->order_info : [],['reason'=>$request->input('notes')]);
-
- // Send mail to user
- Mail::to($o->orderby->email)->queue((new OrderRequestReject($o,$request->input('notes')))->onQueue('email'));
-
- $o->nextStatus($action);
- break;
-
- case 'hold':
- case 'release':
- $o->nextStatus($action);
- break;
-
- case 'update_reference':
- $o->order_info = array_merge($o->order_info ? $o->order_info : [],['order_reference'=>$request->input('notes')]);
- $o->save();
-
- // No action specified.
- default:
- return $this->service($o);
- }
-
- return redirect(url('/a/service',[$o->id]));
- }
-
public function setup()
{
return view('a.setup');
diff --git a/app/Http/Controllers/ServiceController.php b/app/Http/Controllers/ServiceController.php
new file mode 100644
index 0000000..4a6a87f
--- /dev/null
+++ b/app/Http/Controllers/ServiceController.php
@@ -0,0 +1,50 @@
+order_status) {
+ case 'ORDER-SENT':
+ if ($request->post()) {
+ foreach (['reference','notes'] as $key) {
+ $o->setOrderInfo($key,$request->post($key));
+ }
+
+ $o->save();
+
+ foreach ($request->post($o->stype) as $k=>$v) {
+ $o->type->{$k} = $v;
+ }
+
+ $o->type->save();
+
+ return redirect()->to(url('u/service',$o->id))->with('updated','Order sent notes updated.');
+ }
+
+ return $this->update_order_status($o);
+
+ default:
+ abort(499,'Not yet implemented');
+ }
+ }
+
+ private function update_order_status(Service $o)
+ {
+ return View('r.service.order.sent',['o'=>$o]);
+ }
+}
\ No newline at end of file
diff --git a/app/Http/Controllers/UserHomeController.php b/app/Http/Controllers/UserHomeController.php
index 88d9604..2e7278b 100644
--- a/app/Http/Controllers/UserHomeController.php
+++ b/app/Http/Controllers/UserHomeController.php
@@ -87,6 +87,19 @@ class UserHomeController extends Controller
*/
public function service(Service $o): View
{
- return View('u.service',['o'=>$o]);
+ return View('u.service.home',['o'=>$o]);
+ }
+
+ /**
+ * Progress the order to the next stage
+ *
+ * @note: Route Middleware protects this path
+ * @param Service $o
+ * @param string $status
+ * @return \Illuminate\Http\RedirectResponse
+ */
+ public function service_progress(Service $o,string $status)
+ {
+ return redirect()->to($o->action($status) ?: url('u/service',$o->id));
}
}
\ No newline at end of file
diff --git a/app/Models/Policies/ServicePolicy.php b/app/Models/Policies/ServicePolicy.php
index 5ea586a..64c0596 100644
--- a/app/Models/Policies/ServicePolicy.php
+++ b/app/Models/Policies/ServicePolicy.php
@@ -23,10 +23,10 @@ class ServicePolicy
// If this is a service for an account managed by a user.
return ($user->services->pluck('id')->search($o->id))
- // The user is the wholesaler
+ // The user is the wholesaler
OR $user->isWholesaler()
- // The user is the reseller
+ // The user is the reseller
OR $user->all_accounts()->pluck('id')->search($o->account_id);
}
@@ -41,6 +41,18 @@ class ServicePolicy
return TRUE;
}
+ /**
+ * Can the user progress an order status
+ *
+ * @param User $user
+ * @param Service $o
+ * @return bool
+ */
+ public function progress(User $user, Service $o,string $next)
+ {
+ return $o->actions()->has($next);
+ }
+
/**
* Determine whether the user can update the service.
*
diff --git a/app/Models/Service.php b/app/Models/Service.php
index 09bc25d..14cfacc 100644
--- a/app/Models/Service.php
+++ b/app/Models/Service.php
@@ -11,9 +11,12 @@ use Illuminate\Database\Eloquent\Relations\HasOne;
use Illuminate\Database\Eloquent\Relations\MorphTo;
use Illuminate\Support\Arr;
use Illuminate\Support\Collection;
+use Illuminate\Support\Facades\Auth;
+use Symfony\Component\HttpKernel\Exception\HttpException;
-use App\Traits\NextKey;
use Leenooks\Carbon;
+use App\User;
+use App\Traits\NextKey;
class Service extends Model
{
@@ -45,7 +48,7 @@ class Service extends Model
];
protected $casts = [
- 'order_info'=>'array',
+ 'order_info'=>'collection',
];
public $dateFormat = 'U';
@@ -92,9 +95,58 @@ class Service extends Model
*
* @var array
*/
- private $valid_status = [
+ public static $action_progress = [
// Order Submitted
- 'ORDER-SUBMIT' => ['approve'=>'ORDER-SENT','hold'=>'ORDER-HOLD','reject'=>'ORDER-REJECTED','cancel'=>'ORDER-CANCELLED'],
+ 'ORDER-SUBMIT' => [
+ 'fail'=>FALSE,
+ // Progress to next stages by who
+ 'next'=>[
+ 'ORDER-ACCEPT'=>['customer'],
+ 'SETUP-PAYMENT-WAIT'=>['reseller','wholesaler']
+ ],
+ // Manual or System moves to the next stage
+ 'system'=>TRUE,
+ 'method'=>'action_order_submit',
+ 'title'=>'Order Submit',
+ ],
+ // Client accepts order, if performed by RW
+ 'ORDER-ACCEPT' => [
+ 'fail'=>FALSE,
+ 'next'=>[
+ 'SETUP-PAYMENT-WAIT'=>['customer'],
+ ],
+ 'system'=>FALSE,
+ 'method'=>'action_order_accept',
+ 'title'=>'Client Accept Order',
+ ],
+ // If the product has a setup, collect payment information
+ 'SETUP-PAYMENT-WAIT' => [
+ 'fail'=>FALSE,
+ 'next'=>[
+ 'PAYMENT-WAIT'=>['customer'],
+ ],
+ 'system'=>FALSE,
+ 'method'=>'action_setup_payment_wait',
+ 'title'=>'Setup Payment',
+ ],
+ 'PAYMENT-WAIT' => [
+ 'fail'=>FALSE,
+ 'next'=>[
+ 'PAYMENT-CHECK'=>['reseller','wholesaler'],
+ ],
+ 'system'=>FALSE,
+ 'method'=>'action_payment_wait',
+ 'title'=>'Service Payment',
+ ],
+ 'PAYMENT-CHECK' => [
+ 'fail'=>'ORDER-HOLD',
+ 'next'=>[
+ 'ORDER-SENT'=>[],
+ ],
+ 'system'=>TRUE,
+ 'method'=>'action_payment_check',
+ 'title'=>'Validate Payment Method',
+ ],
// Order On Hold (Reason)
'ORDER-HOLD' => ['release'=>'ORDER-SUBMIT','update_reference'=>'ORDER-SENT'],
// Order Rejected (Reason)
@@ -102,7 +154,15 @@ class Service extends Model
// Order Cancelled
'ORDER-CANCELLED' => [],
// Order Sent to Supplier
- 'ORDER-SENT' => ['update_reference'=>'ORDER-SENT','confirm'=>'ORDERED'],
+ 'ORDER-SENT' => [
+ 'fail'=>'ORDER-HOLD',
+ 'next'=>[
+ 'ORDERED'=>['wholesaler'],
+ ],
+ 'system'=>FALSE,
+ 'method'=>'action_order_sent',
+ 'title'=>'Send Order',
+ ],
// Order Confirmed by Supplier
'ORDERED' => ['update_reference'=>'ORDER-SENT'],
];
@@ -493,7 +553,7 @@ class Service extends Model
*/
public function getNameShortAttribute()
{
- return $this->type ? $this->type->service_name : $this->id;
+ return $this->type ? $this->type->name : $this->id;
}
/**
@@ -504,25 +564,14 @@ class Service extends Model
return $this->getInvoiceNextAttribute();
}
- /**
- * This function will present the Order Info Details
- */
- public function getOrderInfoDetailsAttribute(): string
+ public function getOrderInfoNotesAttribute(): ?string
{
- if (! $this->order_info)
- return '';
+ return $this->getOrderInfoValue('notes');
+ }
- $result = '';
-
- foreach ($this->order_info as $k=>$v)
- {
- if (in_array($k,['order_reference']))
- continue;
-
- $result .= sprintf('%s: %s
',ucfirst($k),$v);
- }
-
- return $result;
+ public function getOrderInfoReferenceAttribute(): ?string
+ {
+ return $this->getOrderInfoValue('reference');
}
/**
@@ -738,6 +787,218 @@ class Service extends Model
/** FUNCTIONS **/
+ // The action methods will return: NULL for no progress|FALSE for a failed status|next stage name.
+
+ /**
+ * Process for an order when status ORDER-ACCEPT stage.
+ * This method should have the client confirm/accept the order, if it was placed by a reseller/wholesaler.
+ *
+ * @return bool
+ */
+ private function action_order_accept(): ?bool
+ {
+ // @todo TO IMPLEMENT
+ return TRUE;
+ }
+
+ /**
+ * Action method when status ORDER_SENT
+ * This method redirects to a form, where updating the form will progress to the next stage.
+ */
+ private function action_order_sent()
+ {
+ throw new HttpException(301,url('r/service/update',$this->id));
+ }
+
+ /**
+ * Action method when status ORDER_SUBMIT
+ *
+ * @return bool
+ */
+ private function action_order_submit(): ?bool
+ {
+ // @todo TO IMPLEMENT
+ return TRUE;
+ }
+
+ /**
+ * Process for an order when status SETUP-PAYMENT-WAIT stage.
+ * This method should collect any setup fees payment.
+ *
+ * @return bool
+ */
+ private function action_setup_payment_wait(): ?bool
+ {
+ // @todo TO IMPLEMENT
+ return TRUE;
+ }
+
+ /**
+ * Process for an order when status PAYMENT-CHECK stage.
+ * This method should validate any payment details.
+ *
+ * @return bool
+ */
+ private function action_payment_check(): ?bool
+ {
+ // @todo TO IMPLEMENT
+ return TRUE;
+ }
+
+ /**
+ * Process for an order when status PAYMENT-WAIT stage.
+ * This method should collect any service payment details.
+ *
+ * @return bool
+ */
+ private function action_payment_wait(): ?bool
+ {
+ // @todo TO IMPLEMENT
+ return TRUE;
+ }
+
+ private function getOrderInfoValue(string $key): ?string
+ {
+ return $this->order_info ? $this->order_info->get($key) : NULL;
+ }
+
+ /**
+ * Get the current stage parameters
+ *
+ * @param string $stage
+ * @return array
+ */
+ private function getStageParameters(string $stage): Collection
+ {
+ $result = collect(Arr::get(self::$action_progress,$stage));
+ $myrole = array_search(Auth::user()->role(),User::$role_order);
+
+ // If we have no valid next stage, return an empty collection.
+ if (! $result->count() OR $myrole===FALSE)
+ return $result;
+
+ // Filter the result based on who we are
+ $next = collect();
+
+ foreach ($result->get('next') as $action=>$roles) {
+
+ // Can the current user do this role?
+ $cando = FALSE;
+ foreach ($roles as $role) {
+ if ($myrole < array_search($role,User::$role_order)) {
+ $cando = TRUE;
+ break;
+ }
+ }
+
+ //dd($action,$roles,$result);
+ if ($cando OR $result->get('system')) {
+ $next->put($action,$roles);
+ }
+ }
+
+ $result->put('next',$next);
+
+ return $result;
+ }
+
+ /**
+ * @notes
+ * + When progressing stages, we know who the user is that initiated the stage,
+ * + If no user, then we perform stages SYSTEM=TRUE
+ * + We need to validate that the current stage is complete, before progressing to the next stage
+ * + The current stage may require input from a user, or automation process to progress
+ * + Before leaving this method, we update the service with the stage that it is currently on.
+ *
+ * @param string $stage
+ * @return bool|int|string|null
+ */
+ public function action(string $stage)
+ {
+ // While stage has a string value, that indicates the next stage we want to go to
+ // If stage is NULL, the current stage hasnt been completed
+ // If stage is FALSE, then the current stage failed, and may optionally be directed to another stage.
+
+ while ($stage) {
+ // Check that stage is a valid next action for the user currently performing it
+ $current = $this->getStageParameters($this->order_status);
+ $next = $this->getStageParameters($stage);
+
+ // If valid, call the method to confirm that the current stage is complete
+ if (method_exists($this,$current['method'])) {
+ try {
+ $result = $this->{$current['method']}();
+
+ // If we have a form to complete, we need to return with a URL, so we can catch that with an Exception
+ } catch (HttpException $e) {
+ if ($e->getStatusCode() == 301)
+ return ($e->getMessage());
+ }
+
+ // @todo Implement a status message
+ if (is_null($result)) {
+ $stage = NULL;
+ abort(500,'Current Method Cannot Proceed: '.$result);
+
+ // @todo Implement a status message
+ } elseif (! $result) {
+ $stage = NULL;
+ abort(500,'Current Method FAILED: '.$result);
+
+ } else {
+ $this->order_status = $stage;
+ $this->save();
+
+ // If we have more than 1 next step for the next stage, we'll have to end.
+ if ($this->actions()->count() > 1) {
+ $stage = NULL;
+
+ } else {
+ $stage = $this->actions()->keys()->first();
+ }
+ }
+
+ // @todo Implement a status message
+ } else {
+ // Cant do anything, dont have a method to check if we can leave
+ $stage = NULL;
+ abort(500,'NO Method Cannot Proceed to leave this stage: '.$next['method']);
+ }
+
+ // If valid, call the method to start the next stage
+ }
+ }
+
+ /**
+ * Work out the next applicable actions for this service status
+ *
+ * @notes
+ * + Clients can only progress 1 step, if they are in the next step.
+ * + Resellers/Wholesales can progress to the next Reseller/Wholesaler and any steps in between.
+ *
+ * @param bool $next Only show next actions
+ * @return Collection
+ */
+ public function actions(): Collection
+ {
+ $result = collect();
+ $action = $this->getStageParameters($this->order_status);
+
+ if (! $action->count())
+ return $result;
+
+ // Next Action
+ foreach ($this->getStageParameters($this->order_status)->get('next') as $k=>$v) {
+ $result->put($k,Arr::get(self::$action_progress,$k.'.title'));
+ }
+
+ // No next actions, that will mean the current action hasnt completed.
+ if (! $result->count())
+ $result->put($this->order_status,Arr::get($action,'title'));
+
+ return $result;
+ }
+
/**
* Add applicable tax to the cost
*
@@ -802,7 +1063,7 @@ class Service extends Model
*/
public function next_invoice_items(bool $future): Collection
{
- if ($this->wasCancelled() OR $this->suspend_billing OR ! $this->active)
+ if ($this->wasCancelled() OR $this->suspend_billing OR $this->external_billing OR (! $future AND ! $this->active))
return collect();
// If pending, add any connection charges
@@ -879,23 +1140,6 @@ class Service extends Model
return $this->invoice_items->filter(function($item) { return ! $item->exists; });
}
- /**
- * @todo
- * @param string $status
- * @return $this
- */
- public function nextStatus(string $status) {
- if ($x=$this->validStatus($status))
- {
- $this->order_status = $x;
- $this->save();
-
- return $this;
- }
-
- abort(500,'Next Status not set up for:'.$this->order_status);
- }
-
/**
* This function will return the associated service model for the product type
* @deprecated use $this->type
@@ -919,22 +1163,17 @@ class Service extends Model
}
/**
- * Return if the proposed status is valid.
+ * Store order info details
*
- * @param string $status
- * @return string | NULL
+ * @param string $key
+ * @param string $value
*/
- private function testNextStatusValid(string $status)
+ public function setOrderInfo(string $key,string $value): void
{
- return Arr::get(Arr::get($this->valid_status,$this->order_status,[]),$status,NULL);
- }
+ $x = is_null($this->order_info) ? collect() : $this->order_info;
+ $x->put($key,$value);
- /**
- * @deprecated use testNextStatusValid()
- */
- public function validStatus(string $status)
- {
- return $this->testNextStatusValid($status);
+ $this->order_info = $x;
}
/**
diff --git a/app/User.php b/app/User.php
index 06ee618..4d3fc55 100644
--- a/app/User.php
+++ b/app/User.php
@@ -64,6 +64,16 @@ class User extends Authenticatable
protected $with = ['accounts'];
+ /**
+ * Role hierarchy order
+ * @var array
+ */
+ public static $role_order = [
+ 'wholesaler',
+ 'reseller',
+ 'customer',
+ ];
+
/**
* The accounts that this user manages
*
@@ -452,7 +462,7 @@ class User extends Authenticatable
public function client_service_movements(): DatabaseCollection
{
return Service::active()
- ->select(['id','account_id','product_id','order_status'])
+ ->select(['id','account_id','product_id','order_status','model'])
->where('order_status','!=','ACTIVE')
->whereIN('account_id',$this->all_accounts()->pluck('id')->unique()->toArray())
->with(['account','product'])
diff --git a/config/snappy.php b/config/snappy.php
index a20201d..61cf8a0 100644
--- a/config/snappy.php
+++ b/config/snappy.php
@@ -3,7 +3,7 @@
return [
'pdf' => array(
'enabled' => true,
- 'binary' => '/usr/bin/wkhtmltopdf',
+ 'binary' => '/usr/local/bin/wkhtmltopdf',
'timeout' => false,
'options' => array('print-media-type' => true),
'env' => array(),
@@ -11,7 +11,7 @@ return [
'image' => array(
'enabled' => false,
- 'binary' => '/var/www/html/vendor/bin/wkhtmltoimage',
+ 'binary' => '/usr/local/bin/wkhtmltoimage',
'timeout' => false,
'options' => array(),
'env' => array(),
diff --git a/resources/views/theme/backend/adminlte/a/widgets/service/order/sent.blade.php b/resources/views/theme/backend/adminlte/a/widgets/service/order/sent.blade.php
deleted file mode 100644
index 74b4b81..0000000
--- a/resources/views/theme/backend/adminlte/a/widgets/service/order/sent.blade.php
+++ /dev/null
@@ -1,52 +0,0 @@
-
-
-
-
-
- {{--
-
- --}}
-
\ No newline at end of file
diff --git a/resources/views/theme/backend/adminlte/a/widgets/service/order/submit.blade.php b/resources/views/theme/backend/adminlte/a/widgets/service/order/submit.blade.php
deleted file mode 100644
index 865d2ee..0000000
--- a/resources/views/theme/backend/adminlte/a/widgets/service/order/submit.blade.php
+++ /dev/null
@@ -1,52 +0,0 @@
-
-
-
-
-
- {{--
-
- --}}
-
\ No newline at end of file
diff --git a/resources/views/theme/backend/adminlte/r/service/order/sent.blade.php b/resources/views/theme/backend/adminlte/r/service/order/sent.blade.php
new file mode 100644
index 0000000..3eea5aa
--- /dev/null
+++ b/resources/views/theme/backend/adminlte/r/service/order/sent.blade.php
@@ -0,0 +1,81 @@
+@extends('adminlte::layouts.app')
+
+@section('htmlheader_title')
+ {{ $o->sid }}
+@endsection
+@section('page_title')
+ {{ $o->sid }}
+@endsection
+
+@section('contentheader_title')
+ Service: {{ $o->sid }} {{ $o->product->name }}
+@endsection
+@section('contentheader_description')
+ {{ $o->sname }}: {{ $o->sdesc }}
+@endsection
+
+@section('main-content')
+
+@endsection
+
+@section('page-scripts')
+ @css('//cdnjs.cloudflare.com/ajax/libs/summernote/0.8.12/summernote-bs4.css','summernote-css')
+ @js('//cdnjs.cloudflare.com/ajax/libs/summernote/0.8.12/summernote-bs4.js','summernote-js')
+
+
+@append
\ No newline at end of file
diff --git a/resources/views/theme/backend/adminlte/r/service/widget/movement.blade.php b/resources/views/theme/backend/adminlte/r/service/widget/movement.blade.php
index 75dff1b..80f3575 100644
--- a/resources/views/theme/backend/adminlte/r/service/widget/movement.blade.php
+++ b/resources/views/theme/backend/adminlte/r/service/widget/movement.blade.php
@@ -26,7 +26,7 @@
{{ $o->id }} |
{{ $o->account->name }} |
- {{ $o->name }} |
+ {{ $o->name_short }} |
{{ $o->status }} |
{{ $o->product->name }} |
diff --git a/resources/views/theme/backend/adminlte/u/service.blade.php b/resources/views/theme/backend/adminlte/u/service/home.blade.php
similarity index 80%
rename from resources/views/theme/backend/adminlte/u/service.blade.php
rename to resources/views/theme/backend/adminlte/u/service/home.blade.php
index 52367ca..c20bb60 100644
--- a/resources/views/theme/backend/adminlte/u/service.blade.php
+++ b/resources/views/theme/backend/adminlte/u/service/home.blade.php
@@ -31,7 +31,9 @@
Product
Traffic
--}}
- Pending Items
+ @if (! $o->suspend_billing AND ! $o->external_billing)
+ Pending Items
+ @endif
{{--
Invoices
Emails
@@ -48,11 +50,10 @@
ACTION
@@ -67,9 +68,11 @@
Product.
-
- @include('common.service.widget.invoice')
-
+ @if (! $o->suspend_billing AND ! $o->external_billing)
+
+ @include('common.service.widget.invoice')
+
+ @endif
Invoices.
diff --git a/resources/views/theme/backend/adminlte/u/service/widgets/broadband/order.blade.php b/resources/views/theme/backend/adminlte/u/service/widgets/broadband/order.blade.php
new file mode 100644
index 0000000..2ea73f9
--- /dev/null
+++ b/resources/views/theme/backend/adminlte/u/service/widgets/broadband/order.blade.php
@@ -0,0 +1,11 @@
+
\ No newline at end of file
diff --git a/resources/views/theme/backend/adminlte/u/service/widgets/information.blade.php b/resources/views/theme/backend/adminlte/u/service/widgets/information.blade.php
index d0a93d6..5d0827f 100644
--- a/resources/views/theme/backend/adminlte/u/service/widgets/information.blade.php
+++ b/resources/views/theme/backend/adminlte/u/service/widgets/information.blade.php
@@ -1,4 +1,12 @@
+ @if($o->external_billing)
+
+ @endif
+
@@ -13,7 +21,7 @@
Status |
{!! $o->status_html !!} |
- @if ($o->active or $o->isPending())
+ @if (($o->active OR $o->isPending()) AND ! $o->external_billing)
Billed |
{{ $o->billing_period }} |
@@ -43,7 +51,7 @@
@if ($o->autopay)Direct Debit @else Invoice @endif |
- @elseif(! $o->wasCancelled())
+ @elseif($o->wasCancelled())
Cancelled |
{!! $o->date_end ? $o->date_end->format('Y-m-d') : $o->paid_to->format('Y-m-d').'*' !!} |
diff --git a/routes/web.php b/routes/web.php
index 9179a8d..2daa03e 100644
--- a/routes/web.php
+++ b/routes/web.php
@@ -43,6 +43,9 @@ Route::group(['middleware'=>['theme:adminlte-be','auth','role:reseller'],'prefix
Route::get('supplier/create','SuppliersController@create');
Route::post('supplier/store','SuppliersController@store');
Route::get('switch/start/{id}','\Leenooks\Controllers\AdminController@user_switch_start')->name('switch.user.stop');
+ Route::match(['get','post'],'service/update/{o}','ServiceController@update')
+ ->where('o','[0-9]+')
+ ->middleware('can:update,o');
});
// Our User Routes
@@ -63,6 +66,9 @@ Route::group(['middleware'=>['theme:adminlte-be','auth'],'prefix'=>'u'],function
Route::get('service/{o}','UserHomeController@service')
->where('o','[0-9]+')
->middleware('can:view,o');
+ Route::get('service/progress/{o}/{status}','UserHomeController@service_progress')
+ ->where('o','[0-9]+')
+ ->middleware('can:progress,o,status');
});
// Frontend Routes (Non-Authed Users)