diff --git a/app/Console/Commands/RecurringCheck.php b/app/Console/Commands/RecurringCheck.php index 5e403d6e8..9f3565c15 100644 --- a/app/Console/Commands/RecurringCheck.php +++ b/app/Console/Commands/RecurringCheck.php @@ -11,8 +11,8 @@ use App\Events\Sale\InvoiceRecurring; use App\Models\Banking\Transaction; use App\Models\Common\Company; use App\Models\Sale\Invoice; +use App\Utilities\Date; use App\Utilities\Overrider; -use Date; use Illuminate\Console\Command; class RecurringCheck extends Command @@ -31,13 +31,6 @@ class RecurringCheck extends Command */ protected $description = 'Check for recurring'; - /** - * The current day. - * - * @var \Carbon\Carbon - */ - protected $today; - /** * Execute the console command. * @@ -61,19 +54,35 @@ class RecurringCheck extends Command Overrider::load('settings'); Overrider::load('currencies'); - $this->today = Date::today(); + $today = Date::today(); foreach ($company->recurring as $recurring) { if (!$model = $recurring->recurable) { continue; } - foreach ($recurring->getRecurringSchedule() as $schedule) { + $schedules = $recurring->getRecurringSchedule(); + + $children_count = $this->getChildrenCount($model); + $schedule_count = $schedules->count(); + + // All recurring created, including today + if ($children_count > ($schedule_count - 1)) { + continue; + } + + // Recur only today + if ($children_count == ($schedule_count - 1)) { + $this->recur($model, $recurring->recurable_type, $today); + + continue; + } + + // Recur all schedules, previously failed + foreach ($schedules as $schedule) { $schedule_date = Date::parse($schedule->getStart()->format('Y-m-d')); - \DB::transaction(function () use ($model, $recurring, $schedule_date) { - $this->recur($model, $recurring->recurable_type, $schedule_date); - }); + $this->recur($model, $recurring->recurable_type, $schedule_date); } } } @@ -85,35 +94,32 @@ class RecurringCheck extends Command protected function recur($model, $type, $schedule_date) { - // Don't recur the future - if ($schedule_date->greaterThan($this->today)) { - return; - } + \DB::transaction(function () use ($model, $type, $schedule_date) { + if (!$clone = $this->getClone($model, $schedule_date)) { + return; + } - if (!$clone = $this->getClone($model, $schedule_date)) { - return; - } + switch ($type) { + case 'App\Models\Purchase\Bill': + event(new BillCreated($clone)); - switch ($type) { - case 'App\Models\Purchase\Bill': - event(new BillCreated($clone)); + event(new BillRecurring($clone)); - event(new BillRecurring($clone)); + break; + case 'App\Models\Sale\Invoice': + event(new InvoiceCreated($clone)); - break; - case 'App\Models\Sale\Invoice': - event(new InvoiceCreated($clone)); + event(new InvoiceRecurring($clone)); - event(new InvoiceRecurring($clone)); + break; + case 'App\Models\Banking\Transaction': + event(new TransactionCreated($clone)); - break; - case 'App\Models\Banking\Transaction': - event(new TransactionCreated($clone)); + event(new TransactionRecurring($clone)); - event(new TransactionRecurring($clone)); - - break; - } + break; + } + }); } /** @@ -215,6 +221,15 @@ class RecurringCheck extends Command return false; } + protected function getChildrenCount($model) + { + $table = $this->getTable($model); + + return \DB::table($table) + ->where('parent_id', $model->id) + ->count(); + } + protected function getDateField($model) { if ($model instanceof Transaction) { diff --git a/app/Traits/Recurring.php b/app/Traits/Recurring.php index 5c56dbc56..67f69af5f 100644 --- a/app/Traits/Recurring.php +++ b/app/Traits/Recurring.php @@ -2,6 +2,7 @@ namespace App\Traits; +use App\Utilities\Date; use Recurr\Rule; use Recurr\Transformer\ArrayTransformer; use Recurr\Transformer\ArrayTransformerConfig; @@ -59,18 +60,19 @@ trait Recurring ]); } - public function getRecurringSchedule() + public function getRecurringSchedule($set_until_date = true) { $config = new ArrayTransformerConfig(); $config->enableLastDayOfMonthFix(); + $config->setVirtualLimit($this->getRecurringVirtualLimit()); $transformer = new ArrayTransformer(); $transformer->setConfig($config); - return $transformer->transform($this->getRecurringRule()); + return $transformer->transform($this->getRecurringRule($set_until_date)); } - public function getRecurringRule() + public function getRecurringRule($set_until_date = true) { $rule = (new Rule()) ->setStartDate($this->getRecurringRuleStartDate()) @@ -78,6 +80,10 @@ trait Recurring ->setFreq($this->getRecurringRuleFrequency()) ->setInterval($this->getRecurringRuleInterval()); + if ($set_until_date) { + $rule->setUntil($this->getRecurringRuleUntilDate()); + } + // 0 means infinite if ($this->count != 0) { $rule->setCount($this->getRecurringRuleCount()); @@ -91,6 +97,11 @@ trait Recurring return new \DateTime($this->started_at, new \DateTimeZone($this->getRecurringRuleTimeZone())); } + public function getRecurringRuleUntilDate() + { + return new \DateTime(Date::today()->toDateTimeString(), new \DateTimeZone($this->getRecurringRuleTimeZone())); + } + public function getRecurringRuleTimeZone() { return setting('localisation.timezone'); @@ -112,6 +123,27 @@ trait Recurring return $this->interval; } + public function getRecurringVirtualLimit() + { + switch ($this->frequency) { + case 'yearly': + $limit = '2'; + break; + case 'monthly': + $limit = '24'; + break; + case 'weekly': + $limit = '104'; + break; + case 'daily': + default; + $limit = '732'; + break; + } + + return $limit; + } + public function getCurrentRecurring() { if (!$schedule = $this->getRecurringSchedule()) { diff --git a/app/Utilities/Recurring.php b/app/Utilities/Recurring.php index 2f2562cf4..9863969f7 100644 --- a/app/Utilities/Recurring.php +++ b/app/Utilities/Recurring.php @@ -16,7 +16,7 @@ class Recurring continue; } - foreach ($item->recurring->getRecurringSchedule() as $schedule) { + foreach ($item->recurring->getRecurringSchedule(false) as $schedule) { $issued = Date::parse($item->$issued_date_field); $start = $schedule->getStart();