diff --git a/app/Jobs/CreateRecurringTransactions.php b/app/Jobs/CreateRecurringTransactions.php index 65a42a6ea9..b358e9bc39 100644 --- a/app/Jobs/CreateRecurringTransactions.php +++ b/app/Jobs/CreateRecurringTransactions.php @@ -7,10 +7,8 @@ use FireflyIII\Events\RequestedReportOnJournals; use FireflyIII\Models\Recurrence; use FireflyIII\Models\RecurrenceRepetition; use FireflyIII\Models\RecurrenceTransaction; -use FireflyIII\Models\TransactionJournal; use FireflyIII\Repositories\Journal\JournalRepositoryInterface; use FireflyIII\Repositories\Recurring\RecurringRepositoryInterface; -use FireflyIII\Repositories\User\UserRepositoryInterface; use Illuminate\Bus\Queueable; use Illuminate\Contracts\Queue\ShouldQueue; use Illuminate\Foundation\Bus\Dispatchable; @@ -32,8 +30,6 @@ class CreateRecurringTransactions implements ShouldQueue private $journalRepository; /** @var RecurringRepositoryInterface */ private $repository; - /** @var UserRepositoryInterface */ - private $userRepository; /** * Create a new job instance. @@ -46,14 +42,11 @@ class CreateRecurringTransactions implements ShouldQueue $this->date = $date; $this->repository = app(RecurringRepositoryInterface::class); $this->journalRepository = app(JournalRepositoryInterface::class); - $this->userRepository = app(UserRepositoryInterface::class); } /** * Execute the job. * - * TODO check number of repetitions. - * * @throws \FireflyIII\Exceptions\FireflyException */ public function handle(): void @@ -198,6 +191,13 @@ class CreateRecurringTransactions implements ShouldQueue } Log::debug(sprintf('%s IS today (%s)', $date->format('Y-m-d'), $this->date->format('Y-m-d'))); + // count created journals on THIS day. + $created = $this->repository->getJournals($recurrence, $date, $date); + if ($created->count() > 0) { + Log::info(sprintf('Already created %d journal(s) for date %s', $created->count(), $date->format('Y-m-d'))); + continue; + } + // create transaction array and send to factory. $array = [ 'type' => $recurrence->transactionType->type, @@ -212,7 +212,7 @@ class CreateRecurringTransactions implements ShouldQueue 'piggy_bank_name' => null, 'bill_id' => null, 'bill_name' => null, - 'recurrence_id' => $recurrence->id, + 'recurrence_id' => (int)$recurrence->id, // transaction data: 'transactions' => $this->getTransactionData($recurrence), @@ -315,6 +315,14 @@ class CreateRecurringTransactions implements ShouldQueue return false; } + // has repeated X times. + $journals = $this->repository->getJournals($recurrence, null, null); + if ($recurrence->repetitions !== 0 && $journals->count() >= $recurrence->repetitions) { + Log::info(sprintf('Recurrence #%d has run %d times, so will run no longer.', $recurrence->id, $recurrence->repetitions)); + + return false; + } + // is no longer running if ($this->repeatUntilHasPassed($recurrence)) { Log::info( diff --git a/app/Models/Recurrence.php b/app/Models/Recurrence.php index f168f34afb..bc60c3bad2 100644 --- a/app/Models/Recurrence.php +++ b/app/Models/Recurrence.php @@ -72,6 +72,7 @@ class Recurrence extends Model 'updated_at' => 'datetime', 'deleted_at' => 'datetime', 'title' => 'string', + 'id' => 'int', 'description' => 'string', 'first_date' => 'date', 'repeat_until' => 'date', diff --git a/app/Repositories/Recurring/RecurringRepository.php b/app/Repositories/Recurring/RecurringRepository.php index 024f3fb14a..0ae942e8f0 100644 --- a/app/Repositories/Recurring/RecurringRepository.php +++ b/app/Repositories/Recurring/RecurringRepository.php @@ -33,6 +33,7 @@ use FireflyIII\Models\RecurrenceMeta; use FireflyIII\Models\RecurrenceRepetition; use FireflyIII\Models\RecurrenceTransaction; use FireflyIII\Models\RecurrenceTransactionMeta; +use FireflyIII\Models\TransactionJournal; use FireflyIII\Services\Internal\Destroy\RecurrenceDestroyService; use FireflyIII\Services\Internal\Update\RecurrenceUpdateService; use FireflyIII\User; @@ -129,6 +130,34 @@ class RecurringRepository implements RecurringRepositoryInterface return $return === '' ? null : $return; } + /** + * Returns the journals created for this recurrence, possibly limited by time. + * + * @param Recurrence $recurrence + * @param Carbon|null $start + * @param Carbon|null $end + * + * @return Collection + */ + public function getJournals(Recurrence $recurrence, Carbon $start = null, Carbon $end = null): Collection + { + $query = TransactionJournal + ::leftJoin('journal_meta', 'journal_meta.transaction_journal_id', '=', 'transaction_journals.id') + ->where('transaction_journals.user_id', $recurrence->user_id) + ->whereNull('transaction_journals.deleted_at') + ->where('journal_meta.name', 'recurrence_id') + ->where('journal_meta.data', '"' . $recurrence->id . '"'); + if (null !== $start) { + $query->where('transaction_journals.date', '>=', $start->format('Y-m-d 00:00:00')); + } + if (null !== $end) { + $query->where('transaction_journals.date', '<=', $end->format('Y-m-d 00:00:00')); + } + $result = $query->get(['transaction_journals.*']); + + return $result; + } + /** * Get the notes. * @@ -398,6 +427,8 @@ class RecurringRepository implements RecurringRepositoryInterface } break; } + // filter out all the weekend days: + $return = $this->filterWeekends($repetition, $return); return $return; } @@ -512,12 +543,16 @@ class RecurringRepository implements RecurringRepositoryInterface /** @var Carbon $date */ foreach ($dates as $date) { $isWeekend = $date->isWeekend(); + if (!$isWeekend) { + $return[] = clone $date; + continue; + } - // set back to Friday? + // is weekend and must set back to Friday? if ($isWeekend && $repetition->weekend === RecurrenceRepetition::WEEKEND_TO_FRIDAY) { $clone = clone $date; - $clone->subDays(7 - $date->dayOfWeekIso); - $return[] = $clone; + $clone->addDays(5 - $date->dayOfWeekIso); + $return[] = clone $clone; } // postpone to Monday? @@ -526,10 +561,12 @@ class RecurringRepository implements RecurringRepositoryInterface $clone->addDays(8 - $date->dayOfWeekIso); $return[] = $clone; } - // otherwise, ignore the date! } - // filter unique dates? + // filter unique dates + $collection = new Collection($return); + $filtered = $collection->unique(); + $return = $filtered->toArray(); return $return; } diff --git a/app/Repositories/Recurring/RecurringRepositoryInterface.php b/app/Repositories/Recurring/RecurringRepositoryInterface.php index 6f6963e1b6..98751b3f39 100644 --- a/app/Repositories/Recurring/RecurringRepositoryInterface.php +++ b/app/Repositories/Recurring/RecurringRepositoryInterface.php @@ -78,6 +78,17 @@ interface RecurringRepositoryInterface */ public function getCategory(RecurrenceTransaction $recurrenceTransaction): ?string; + /** + * Returns the journals created for this recurrence, possibly limited by time. + * + * @param Recurrence $recurrence + * @param Carbon|null $start + * @param Carbon|null $end + * + * @return Collection + */ + public function getJournals(Recurrence $recurrence, Carbon $start = null, Carbon $end = null): Collection; + /** * Get the notes. * diff --git a/app/Transformers/RecurrenceTransformer.php b/app/Transformers/RecurrenceTransformer.php index 8a0bba3a1c..0c23487c08 100644 --- a/app/Transformers/RecurrenceTransformer.php +++ b/app/Transformers/RecurrenceTransformer.php @@ -137,6 +137,7 @@ class RecurrenceTransformer extends TransformerAbstract 'repetition_type' => $repetition->repetition_type, 'repetition_moment' => $repetition->repetition_moment, 'repetition_skip' => (int)$repetition->repetition_skip, + 'weekend' => (int)$repetition->weekend, 'description' => $this->repository->repetitionDescription($repetition), 'occurrences' => [], ]; diff --git a/public/js/ff/recurring/edit.js b/public/js/ff/recurring/edit.js index a2aad13572..f499280b57 100644 --- a/public/js/ff/recurring/edit.js +++ b/public/js/ff/recurring/edit.js @@ -67,6 +67,7 @@ function showRepCalendar() { newEventsUri += '&end_date=' + $('#ffInput_repeat_until').val(); newEventsUri += '&reps=' + $('#ffInput_repetitions').val(); newEventsUri += '&first_date=' + $('#ffInput_first_date').val(); + newEventsUri += '&weekend=' + $('#ffInput_weekend').val(); // remove all event sources from calendar: calendar.fullCalendar('removeEventSources'); diff --git a/resources/views/recurring/create.twig b/resources/views/recurring/create.twig index c165ad7dc7..7af6a7082f 100644 --- a/resources/views/recurring/create.twig +++ b/resources/views/recurring/create.twig @@ -20,8 +20,8 @@ {{ ExpandedForm.date('first_date',null, {helpText: trans('firefly.help_first_date')}) }} {{ ExpandedForm.select('repetition_type', [], null, {helpText: trans('firefly.change_date_other_options')}) }} {{ ExpandedForm.number('skip', 0) }} - {{ ExpandedForm.select('repetition_end', repetitionEnds) }} {{ ExpandedForm.select('weekend', weekendResponses, null, {helpText: trans('firefly.help_weekend')}) }} + {{ ExpandedForm.select('repetition_end', repetitionEnds) }} {{ ExpandedForm.date('repeat_until',null) }} {{ ExpandedForm.number('repetitions',null) }}