diff --git a/app/Console/Commands/RecurringCheck.php b/app/Console/Commands/RecurringCheck.php index 54edd5ddb..a02dd0a38 100644 --- a/app/Console/Commands/RecurringCheck.php +++ b/app/Console/Commands/RecurringCheck.php @@ -12,7 +12,6 @@ use App\Models\Common\Recurring; use App\Models\Document\Document; use App\Utilities\Date; use Illuminate\Console\Command; -use Illuminate\Database\Eloquent\Builder; use Illuminate\Support\Facades\DB; use Illuminate\Support\Str; @@ -39,6 +38,8 @@ class RecurringCheck extends Command */ public function handle() { + $this->info('Checking for recurring...'); + // Bind to container app()->instance(static::class, $this); @@ -56,8 +57,6 @@ class RecurringCheck extends Command //$this->info('Total recurring: ' . $recurring->count()); - $today = Date::today(); - foreach ($recurring as $recur) { if (empty($recur->company)) { $this->info('Missing company.'); @@ -103,7 +102,7 @@ class RecurringCheck extends Command company($recur->company_id)->makeCurrent(); - if (! $model = $recur->recurable) { + if (! $template = $recur->recurable) { $this->info('Missing model.'); $recur->delete(); @@ -111,41 +110,28 @@ class RecurringCheck extends Command continue; } - $children_count = $this->getChildrenCount($model); + $this->info('Template ID: ' . $template->id); $schedules = $recur->getRecurringSchedule(); - $schedule_count = $schedules->count(); - // All recurring created, including today - if ($children_count > ($schedule_count - 1)) { - $this->info('All recurring created.'); + // Check if all schedules created + if ($this->getChildrenCount($template) == $schedules->count()) { + $this->info('All schedules created.'); $recur->update(['status' => Recurring::COMPLETE_STATUS]); continue; } - // Recur only today - if ($children_count == ($schedule_count - 1)) { - $this->info('Recur only today.'); + // Recur remaining schedules, including the previously failed ones + $schedules = $this->getRemainingSchedules($recur, $template, $schedules); - $this->recur($model, $recur->recurable_type, $today); - - $recur->update(['status' => Recurring::COMPLETE_STATUS]); - - continue; - } - - // Don't create records for the future - $schedules = $schedules->endsBefore($recur->getRecurringRuleTomorrowDate()); - - // Recur all schedules, including the previously failed ones foreach ($schedules as $schedule) { - $this->info('Recur all schedules.'); - $schedule_date = Date::parse($schedule->getStart()->format('Y-m-d')); - $this->recur($model, $recur->recurable_type, $schedule_date); + $this->info('Schedule date: ' . $schedule_date); + + $this->recur($template, $schedule_date); } } @@ -153,27 +139,31 @@ class RecurringCheck extends Command // Remove from container app()->forgetInstance(static::class); + + $this->info('Recurring check done!'); } - protected function recur($model, $type, $schedule_date) + protected function recur($template, $schedule_date) { - DB::transaction(function () use ($model, $type, $schedule_date) { - /** @var Document|Transaction $clone */ - if (! $clone = $this->getClone($model, $schedule_date)) { + DB::transaction(function () use ($template, $schedule_date) { + /** @var Document|Transaction $model */ + if (! $model = $this->getModel($template, $schedule_date)) { return; } - switch ($type) { - case 'App\Models\Document\Document': - event(new DocumentCreated($clone, request())); + $this->info('Model created: ' . $model->id); - event(new DocumentRecurring($clone)); + switch ($template::class) { + case Document::class: + event(new DocumentCreated($model, request())); + + event(new DocumentRecurring($model)); break; - case 'App\Models\Banking\Transaction': - event(new TransactionCreated($clone)); + case Transaction::class: + event(new TransactionCreated($model)); - event(new TransactionRecurring($clone)); + event(new TransactionRecurring($model)); break; } @@ -181,23 +171,19 @@ class RecurringCheck extends Command } /** - * Clone the model and return it. + * Clone the template and return the real model. * * @param $model * @param $schedule_date * * @return boolean|object */ - protected function getClone($model, $schedule_date) + protected function getModel($template, $schedule_date) { - if ($this->skipThisClone($model, $schedule_date)) { - return false; - } - - $function = ($model instanceof Transaction) ? 'getTransactionClone' : 'getDocumentClone'; + $function = ($template instanceof Transaction) ? 'getTransactionModel' : 'getDocumentModel'; try { - return $this->$function($model, $schedule_date); + return $this->$function($template, $schedule_date); } catch (\Throwable $e) { $this->error($e->getMessage()); @@ -207,92 +193,79 @@ class RecurringCheck extends Command } } - /** - * Clone the document and return it. - * - * @param $model - * @param $schedule_date - * - * @return boolean|object - */ - protected function getDocumentClone($model, $schedule_date) + protected function getDocumentModel(Document $template, $schedule_date): Document { - $model->cloneable_relations = ['items', 'totals']; + $template->cloneable_relations = ['items', 'totals']; - $clone = $model->duplicate(); + $model = $template->duplicate(); // Days between issued and due date - $diff_days = Date::parse($model->due_at)->diffInDays(Date::parse($model->issued_at)); + $diff_days = Date::parse($template->due_at)->diffInDays(Date::parse($template->issued_at)); - $clone->type = $this->getRealType($clone->type); - $clone->parent_id = $model->id; - $clone->issued_at = $schedule_date->format('Y-m-d'); - $clone->due_at = $schedule_date->copy()->addDays($diff_days)->format('Y-m-d'); - $clone->created_from = 'core::recurring'; - $clone->save(); + $model->type = $this->getRealType($template->type); + $model->parent_id = $template->id; + $model->issued_at = $schedule_date->format('Y-m-d'); + $model->due_at = $schedule_date->copy()->addDays($diff_days)->format('Y-m-d'); + $model->created_from = 'core::recurring'; + $model->save(); - return $clone; + return $model; } - /** - * Clone the transaction and return it. - * - * @param $model - * @param $schedule_date - * - * @return boolean|object - */ - protected function getTransactionClone($model, $schedule_date) + protected function getTransactionModel(Transaction $template, $schedule_date): Transaction { - $model->cloneable_relations = []; + $template->cloneable_relations = []; - $clone = $model->duplicate(); + $model = $template->duplicate(); - $clone->type = $this->getRealType($clone->type); - $clone->parent_id = $model->id; - $clone->paid_at = $schedule_date->format('Y-m-d'); - $clone->created_from = 'core::recurring'; - $clone->save(); + $model->type = $this->getRealType($template->type); + $model->parent_id = $template->id; + $model->paid_at = $schedule_date->format('Y-m-d'); + $model->created_from = 'core::recurring'; + $model->save(); - return $clone; + return $model; } - protected function skipThisClone($model, $schedule_date) + protected function getRemainingSchedules($recur, $template, $schedules) { - $date_field = $this->getDateField($model); + // Don't create schedules for the future + $schedules = $schedules->endsBefore($recur->getRecurringRuleTomorrowDate()); - // Skip model created on the same day, but scheduler hasn't run yet - if ($schedule_date->equalTo(Date::parse($model->$date_field->format('Y-m-d')))) { - return true; - } + $date_field = $this->getDateField($template); - $already_cloned = DB::table($model->getTable()) - ->where('parent_id', $model->id) - ->whereDate($date_field, $schedule_date) - ->value('id'); + $created_schedules = DB::table($template->getTable()) + ->where('type', $this->getRealType($template->type)) + ->where('parent_id', $template->id) + ->get($date_field) + ->transform(function ($item, $key) use ($date_field) { + return Date::parse($item->$date_field)->format('Y-m-d'); + }) + ->toArray(); - // Skip if already cloned - if ($already_cloned) { - return true; - } + // Skip already created schedules + $schedules = $schedules->filter(function ($recurrence) use ($created_schedules) { + return ! in_array($recurrence->getStart()->format('Y-m-d'), $created_schedules); + }); - return false; + return $schedules; } - protected function getChildrenCount($model) + protected function getChildrenCount($template) { - return DB::table($model->getTable()) - ->where('parent_id', $model->id) - ->count(); + return DB::table($template->getTable()) + ->where('type', $this->getRealType($template->type)) + ->where('parent_id', $template->id) + ->count(); } - protected function getDateField($model) + protected function getDateField($template) { - if ($model instanceof Transaction) { + if ($template instanceof Transaction) { return 'paid_at'; } - if ($model instanceof Document) { + if ($template instanceof Document) { return 'issued_at'; } } diff --git a/app/Traits/Recurring.php b/app/Traits/Recurring.php index 1ae78117c..ad347f6b2 100644 --- a/app/Traits/Recurring.php +++ b/app/Traits/Recurring.php @@ -148,8 +148,7 @@ trait Recurring public function getRecurringRuleCount() { - // Fix for humans - return $this->limit_count + 1; + return $this->limit_count; } public function getRecurringRuleFrequency()