Progress on order progress

This commit is contained in:
Deon George
2020-04-19 08:33:41 +10:00
parent e6f823da39
commit 6480f40b22
16 changed files with 505 additions and 224 deletions

View File

@@ -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: <b>%s</b><br>',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;
}
/**