mirror of
				https://github.com/firefly-iii/firefly-iii.git
				synced 2025-11-03 20:55:05 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			662 lines
		
	
	
		
			24 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
			
		
		
	
	
			662 lines
		
	
	
		
			24 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
<?php
 | 
						|
/**
 | 
						|
 * RecurringRepository.php
 | 
						|
 * Copyright (c) 2019 james@firefly-iii.org
 | 
						|
 *
 | 
						|
 * This file is part of Firefly III (https://github.com/firefly-iii).
 | 
						|
 *
 | 
						|
 * This program is free software: you can redistribute it and/or modify
 | 
						|
 * it under the terms of the GNU Affero General Public License as
 | 
						|
 * published by the Free Software Foundation, either version 3 of the
 | 
						|
 * License, or (at your option) any later version.
 | 
						|
 *
 | 
						|
 * This program is distributed in the hope that it will be useful,
 | 
						|
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
						|
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
						|
 * GNU Affero General Public License for more details.
 | 
						|
 *
 | 
						|
 * You should have received a copy of the GNU Affero General Public License
 | 
						|
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
 | 
						|
 */
 | 
						|
 | 
						|
declare(strict_types=1);
 | 
						|
 | 
						|
namespace FireflyIII\Repositories\Recurring;
 | 
						|
 | 
						|
use Carbon\Carbon;
 | 
						|
use FireflyIII\Exceptions\FireflyException;
 | 
						|
use FireflyIII\Factory\RecurrenceFactory;
 | 
						|
use FireflyIII\Helpers\Collector\GroupCollectorInterface;
 | 
						|
use FireflyIII\Models\Note;
 | 
						|
use FireflyIII\Models\Preference;
 | 
						|
use FireflyIII\Models\Recurrence;
 | 
						|
use FireflyIII\Models\RecurrenceMeta;
 | 
						|
use FireflyIII\Models\RecurrenceRepetition;
 | 
						|
use FireflyIII\Models\RecurrenceTransaction;
 | 
						|
use FireflyIII\Models\RecurrenceTransactionMeta;
 | 
						|
use FireflyIII\Models\TransactionJournal;
 | 
						|
use FireflyIII\Models\TransactionJournalMeta;
 | 
						|
use FireflyIII\Services\Internal\Destroy\RecurrenceDestroyService;
 | 
						|
use FireflyIII\Services\Internal\Update\RecurrenceUpdateService;
 | 
						|
use FireflyIII\Support\Repositories\Recurring\CalculateRangeOccurrences;
 | 
						|
use FireflyIII\Support\Repositories\Recurring\CalculateXOccurrences;
 | 
						|
use FireflyIII\Support\Repositories\Recurring\CalculateXOccurrencesSince;
 | 
						|
use FireflyIII\Support\Repositories\Recurring\FiltersWeekends;
 | 
						|
use FireflyIII\User;
 | 
						|
use Illuminate\Contracts\Auth\Authenticatable;
 | 
						|
use Illuminate\Database\Eloquent\Builder;
 | 
						|
use Illuminate\Pagination\LengthAwarePaginator;
 | 
						|
use Illuminate\Support\Collection;
 | 
						|
use JsonException;
 | 
						|
 | 
						|
/**
 | 
						|
 * Class RecurringRepository
 | 
						|
 */
 | 
						|
class RecurringRepository implements RecurringRepositoryInterface
 | 
						|
{
 | 
						|
    use CalculateRangeOccurrences;
 | 
						|
    use CalculateXOccurrences;
 | 
						|
    use CalculateXOccurrencesSince;
 | 
						|
    use FiltersWeekends;
 | 
						|
 | 
						|
    private User $user;
 | 
						|
 | 
						|
    /**
 | 
						|
     * @inheritDoc
 | 
						|
     */
 | 
						|
    public function createdPreviously(Recurrence $recurrence, Carbon $date): bool
 | 
						|
    {
 | 
						|
        // if not, loop set and try to read the recurrence_date. If it matches start or end, return it as well.
 | 
						|
        $set
 | 
						|
            = TransactionJournalMeta::where(static function (Builder $q1) use ($recurrence) {
 | 
						|
            $q1->where('name', 'recurrence_id');
 | 
						|
            $q1->where('data', json_encode((string)$recurrence->id));
 | 
						|
        })->get(['journal_meta.transaction_journal_id']);
 | 
						|
 | 
						|
        // there are X journals made for this recurrence. Any of them meant for today?
 | 
						|
        foreach ($set as $journalMeta) {
 | 
						|
            $count = TransactionJournalMeta::where(static function (Builder $q2) use ($date) {
 | 
						|
                $string = (string)$date;
 | 
						|
                app('log')->debug(sprintf('Search for date: %s', json_encode($string)));
 | 
						|
                $q2->where('name', 'recurrence_date');
 | 
						|
                $q2->where('data', json_encode($string));
 | 
						|
            })
 | 
						|
                                           ->where('transaction_journal_id', $journalMeta->transaction_journal_id)
 | 
						|
                                           ->count();
 | 
						|
            if ($count > 0) {
 | 
						|
                app('log')->debug(sprintf('Looks like journal #%d was already created', $journalMeta->transaction_journal_id));
 | 
						|
                return true;
 | 
						|
            }
 | 
						|
        }
 | 
						|
        return false;
 | 
						|
    }
 | 
						|
 | 
						|
    /**
 | 
						|
     * Returns all of the user's recurring transactions.
 | 
						|
     *
 | 
						|
     * @return Collection
 | 
						|
     */
 | 
						|
    public function get(): Collection
 | 
						|
    {
 | 
						|
        return $this->user->recurrences()
 | 
						|
                          ->with(['TransactionCurrency', 'TransactionType', 'RecurrenceRepetitions', 'RecurrenceTransactions'])
 | 
						|
                          ->orderBy('active', 'DESC')
 | 
						|
                          ->orderBy('transaction_type_id', 'ASC')
 | 
						|
                          ->orderBy('title', 'ASC')
 | 
						|
                          ->get();
 | 
						|
    }
 | 
						|
 | 
						|
    /**
 | 
						|
     * Destroy a recurring transaction.
 | 
						|
     *
 | 
						|
     * @param Recurrence $recurrence
 | 
						|
     */
 | 
						|
    public function destroy(Recurrence $recurrence): void
 | 
						|
    {
 | 
						|
        /** @var RecurrenceDestroyService $service */
 | 
						|
        $service = app(RecurrenceDestroyService::class);
 | 
						|
        $service->destroy($recurrence);
 | 
						|
    }
 | 
						|
 | 
						|
    /**
 | 
						|
     * @inheritDoc
 | 
						|
     */
 | 
						|
    public function destroyAll(): void
 | 
						|
    {
 | 
						|
        $this->user->recurrences()->delete();
 | 
						|
    }
 | 
						|
 | 
						|
    /**
 | 
						|
     * Get ALL recurring transactions.
 | 
						|
     *
 | 
						|
     * @return Collection
 | 
						|
     */
 | 
						|
    public function getAll(): Collection
 | 
						|
    {
 | 
						|
        // grab ALL recurring transactions:
 | 
						|
        return Recurrence::with(['TransactionCurrency', 'TransactionType', 'RecurrenceRepetitions', 'RecurrenceTransactions'])
 | 
						|
                         ->orderBy('active', 'DESC')
 | 
						|
                         ->orderBy('title', 'ASC')
 | 
						|
                         ->get();
 | 
						|
    }
 | 
						|
 | 
						|
    /**
 | 
						|
     * @inheritDoc
 | 
						|
     */
 | 
						|
    public function getBillId(RecurrenceTransaction $recTransaction): ?int
 | 
						|
    {
 | 
						|
        $return = null;
 | 
						|
        /** @var RecurrenceTransactionMeta $meta */
 | 
						|
        foreach ($recTransaction->recurrenceTransactionMeta as $meta) {
 | 
						|
            if ('bill_id' === $meta->name) {
 | 
						|
                $return = (int)$meta->value;
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
        return $return;
 | 
						|
    }
 | 
						|
 | 
						|
    /**
 | 
						|
     * Get the budget ID from a recurring transaction transaction.
 | 
						|
     *
 | 
						|
     * @param RecurrenceTransaction $recTransaction
 | 
						|
     *
 | 
						|
     * @return null|int
 | 
						|
     */
 | 
						|
    public function getBudget(RecurrenceTransaction $recTransaction): ?int
 | 
						|
    {
 | 
						|
        $return = 0;
 | 
						|
        /** @var RecurrenceTransactionMeta $meta */
 | 
						|
        foreach ($recTransaction->recurrenceTransactionMeta as $meta) {
 | 
						|
            if ('budget_id' === $meta->name) {
 | 
						|
                $return = (int)$meta->value;
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
        return 0 === $return ? null : $return;
 | 
						|
    }
 | 
						|
 | 
						|
    /**
 | 
						|
     * Get the category from a recurring transaction transaction.
 | 
						|
     *
 | 
						|
     * @param RecurrenceTransaction $recTransaction
 | 
						|
     *
 | 
						|
     * @return null|int
 | 
						|
     */
 | 
						|
    public function getCategoryId(RecurrenceTransaction $recTransaction): ?int
 | 
						|
    {
 | 
						|
        $return = '';
 | 
						|
        /** @var RecurrenceTransactionMeta $meta */
 | 
						|
        foreach ($recTransaction->recurrenceTransactionMeta as $meta) {
 | 
						|
            if ('category_id' === $meta->name) {
 | 
						|
                $return = (int)$meta->value;
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
        return '' === $return ? null : $return;
 | 
						|
    }
 | 
						|
 | 
						|
    /**
 | 
						|
     * Get the category from a recurring transaction transaction.
 | 
						|
     *
 | 
						|
     * @param RecurrenceTransaction $recTransaction
 | 
						|
     *
 | 
						|
     * @return null|string
 | 
						|
     */
 | 
						|
    public function getCategoryName(RecurrenceTransaction $recTransaction): ?string
 | 
						|
    {
 | 
						|
        $return = '';
 | 
						|
        /** @var RecurrenceTransactionMeta $meta */
 | 
						|
        foreach ($recTransaction->recurrenceTransactionMeta as $meta) {
 | 
						|
            if ('category_name' === $meta->name) {
 | 
						|
                $return = (string)$meta->value;
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
        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 int
 | 
						|
     */
 | 
						|
    public function getJournalCount(Recurrence $recurrence, Carbon $start = null, Carbon $end = null): int
 | 
						|
    {
 | 
						|
        $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'));
 | 
						|
        }
 | 
						|
        return $query->count('transaction_journals.id');
 | 
						|
    }
 | 
						|
 | 
						|
    /**
 | 
						|
     * Get journal ID's for journals created by this recurring transaction.
 | 
						|
     *
 | 
						|
     * @param Recurrence $recurrence
 | 
						|
     *
 | 
						|
     * @return array
 | 
						|
     */
 | 
						|
    public function getJournalIds(Recurrence $recurrence): array
 | 
						|
    {
 | 
						|
        return TransactionJournalMeta::leftJoin('transaction_journals', 'transaction_journals.id', '=', 'journal_meta.transaction_journal_id')
 | 
						|
                                     ->where('transaction_journals.user_id', $this->user->id)
 | 
						|
                                     ->where('journal_meta.name', '=', 'recurrence_id')
 | 
						|
                                     ->where('journal_meta.data', '=', json_encode((string)$recurrence->id))
 | 
						|
                                     ->get(['journal_meta.transaction_journal_id'])->pluck('transaction_journal_id')->toArray();
 | 
						|
    }
 | 
						|
 | 
						|
    /**
 | 
						|
     * Get the notes.
 | 
						|
     *
 | 
						|
     * @param Recurrence $recurrence
 | 
						|
     *
 | 
						|
     * @return string
 | 
						|
     */
 | 
						|
    public function getNoteText(Recurrence $recurrence): string
 | 
						|
    {
 | 
						|
        /** @var Note|null $note */
 | 
						|
        $note = $recurrence->notes()->first();
 | 
						|
        return (string)$note?->text;
 | 
						|
    }
 | 
						|
 | 
						|
    /**
 | 
						|
     * @param RecurrenceTransaction $transaction
 | 
						|
     *
 | 
						|
     * @return int|null
 | 
						|
     */
 | 
						|
    public function getPiggyBank(RecurrenceTransaction $transaction): ?int
 | 
						|
    {
 | 
						|
        $meta = $transaction->recurrenceTransactionMeta;
 | 
						|
        /** @var RecurrenceTransactionMeta $metaEntry */
 | 
						|
        foreach ($meta as $metaEntry) {
 | 
						|
            if ('piggy_bank_id' === $metaEntry->name) {
 | 
						|
                return (int)$metaEntry->value;
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
        return null;
 | 
						|
    }
 | 
						|
 | 
						|
    /**
 | 
						|
     * Get the tags from the recurring transaction.
 | 
						|
     *
 | 
						|
     * @param RecurrenceTransaction $transaction
 | 
						|
     *
 | 
						|
     * @return array
 | 
						|
     * @throws JsonException
 | 
						|
     */
 | 
						|
    public function getTags(RecurrenceTransaction $transaction): array
 | 
						|
    {
 | 
						|
        $tags = [];
 | 
						|
        /** @var RecurrenceMeta $meta */
 | 
						|
        foreach ($transaction->recurrenceTransactionMeta as $meta) {
 | 
						|
            if ('tags' === $meta->name && '' !== $meta->value) {
 | 
						|
                $tags = json_decode($meta->value, true, 512, JSON_THROW_ON_ERROR);
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
        return $tags;
 | 
						|
    }
 | 
						|
 | 
						|
    /**
 | 
						|
     * @param Recurrence $recurrence
 | 
						|
     * @param int        $page
 | 
						|
     * @param int        $pageSize
 | 
						|
     *
 | 
						|
     * @return LengthAwarePaginator
 | 
						|
     */
 | 
						|
    public function getTransactionPaginator(Recurrence $recurrence, int $page, int $pageSize): LengthAwarePaginator
 | 
						|
    {
 | 
						|
        $journalMeta = TransactionJournalMeta::leftJoin('transaction_journals', 'transaction_journals.id', '=', 'journal_meta.transaction_journal_id')
 | 
						|
                                             ->whereNull('transaction_journals.deleted_at')
 | 
						|
                                             ->where('transaction_journals.user_id', $this->user->id)
 | 
						|
                                             ->where('name', 'recurrence_id')
 | 
						|
                                             ->where('data', json_encode((string)$recurrence->id))
 | 
						|
                                             ->get()->pluck('transaction_journal_id')->toArray();
 | 
						|
        $search      = [];
 | 
						|
        foreach ($journalMeta as $journalId) {
 | 
						|
            $search[] = (int)$journalId;
 | 
						|
        }
 | 
						|
        /** @var GroupCollectorInterface $collector */
 | 
						|
        $collector = app(GroupCollectorInterface::class);
 | 
						|
 | 
						|
        $collector->setUser($recurrence->user);
 | 
						|
        $collector->withCategoryInformation()->withBudgetInformation()->setLimit($pageSize)->setPage($page)
 | 
						|
                  ->withAccountInformation();
 | 
						|
        $collector->setJournalIds($search);
 | 
						|
 | 
						|
        return $collector->getPaginatedGroups();
 | 
						|
    }
 | 
						|
 | 
						|
    /**
 | 
						|
     * @param User|Authenticatable|null $user
 | 
						|
     */
 | 
						|
    public function setUser(User | Authenticatable | null $user): void
 | 
						|
    {
 | 
						|
        if ($user instanceof User) {
 | 
						|
            $this->user = $user;
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    /**
 | 
						|
     * @param Recurrence $recurrence
 | 
						|
     *
 | 
						|
     * @return Collection
 | 
						|
     */
 | 
						|
    public function getTransactions(Recurrence $recurrence): Collection
 | 
						|
    {
 | 
						|
        $journalMeta = TransactionJournalMeta::leftJoin('transaction_journals', 'transaction_journals.id', '=', 'journal_meta.transaction_journal_id')
 | 
						|
                                             ->whereNull('transaction_journals.deleted_at')
 | 
						|
                                             ->where('transaction_journals.user_id', $this->user->id)
 | 
						|
                                             ->where('name', 'recurrence_id')
 | 
						|
                                             ->where('data', json_encode((string)$recurrence->id))
 | 
						|
                                             ->get()->pluck('transaction_journal_id')->toArray();
 | 
						|
        $search      = [];
 | 
						|
 | 
						|
        foreach ($journalMeta as $journalId) {
 | 
						|
            $search[] = (int)$journalId;
 | 
						|
        }
 | 
						|
        if (0 === count($search)) {
 | 
						|
            return new Collection();
 | 
						|
        }
 | 
						|
 | 
						|
        /** @var GroupCollectorInterface $collector */
 | 
						|
        $collector = app(GroupCollectorInterface::class);
 | 
						|
 | 
						|
        $collector->setUser($recurrence->user);
 | 
						|
        $collector->withCategoryInformation()->withBudgetInformation()->withAccountInformation();
 | 
						|
        // filter on specific journals.
 | 
						|
        $collector->setJournalIds($search);
 | 
						|
 | 
						|
        return $collector->getGroups();
 | 
						|
    }
 | 
						|
 | 
						|
    /**
 | 
						|
     * Calculate the next X iterations starting on the date given in $date.
 | 
						|
     *
 | 
						|
     * @param RecurrenceRepetition $repetition
 | 
						|
     * @param Carbon               $date
 | 
						|
     * @param int                  $count
 | 
						|
     *
 | 
						|
     * @return array
 | 
						|
     *
 | 
						|
     */
 | 
						|
    public function getXOccurrences(RecurrenceRepetition $repetition, Carbon $date, int $count): array
 | 
						|
    {
 | 
						|
        $skipMod     = $repetition->repetition_skip + 1;
 | 
						|
        $occurrences = [];
 | 
						|
        if ('daily' === $repetition->repetition_type) {
 | 
						|
            $occurrences = $this->getXDailyOccurrences($date, $count, $skipMod);
 | 
						|
        }
 | 
						|
        if ('weekly' === $repetition->repetition_type) {
 | 
						|
            $occurrences = $this->getXWeeklyOccurrences($date, $count, $skipMod, $repetition->repetition_moment);
 | 
						|
        }
 | 
						|
        if ('monthly' === $repetition->repetition_type) {
 | 
						|
            $occurrences = $this->getXMonthlyOccurrences($date, $count, $skipMod, $repetition->repetition_moment);
 | 
						|
        }
 | 
						|
        if ('ndom' === $repetition->repetition_type) {
 | 
						|
            $occurrences = $this->getXNDomOccurrences($date, $count, $skipMod, $repetition->repetition_moment);
 | 
						|
        }
 | 
						|
        if ('yearly' === $repetition->repetition_type) {
 | 
						|
            $occurrences = $this->getXYearlyOccurrences($date, $count, $skipMod, $repetition->repetition_moment);
 | 
						|
        }
 | 
						|
 | 
						|
        // filter out all the weekend days:
 | 
						|
        return $this->filterWeekends($repetition, $occurrences);
 | 
						|
    }
 | 
						|
 | 
						|
    /**
 | 
						|
     * Calculate the next X iterations starting on the date given in $date.
 | 
						|
     * Returns an array of Carbon objects.
 | 
						|
     *
 | 
						|
     * Only returns them of they are after $afterDate
 | 
						|
     *
 | 
						|
     * @param RecurrenceRepetition $repetition
 | 
						|
     * @param Carbon               $date
 | 
						|
     * @param Carbon               $afterDate
 | 
						|
     * @param int                  $count
 | 
						|
     *
 | 
						|
     * @return array
 | 
						|
     */
 | 
						|
    public function getXOccurrencesSince(RecurrenceRepetition $repetition, Carbon $date, Carbon $afterDate, int $count): array
 | 
						|
    {
 | 
						|
        app('log')->debug('Now in getXOccurrencesSince()');
 | 
						|
        $skipMod     = $repetition->repetition_skip + 1;
 | 
						|
        $occurrences = [];
 | 
						|
        if ('daily' === $repetition->repetition_type) {
 | 
						|
            $occurrences = $this->getXDailyOccurrencesSince($date, $afterDate, $count, $skipMod);
 | 
						|
        }
 | 
						|
        if ('weekly' === $repetition->repetition_type) {
 | 
						|
            $occurrences = $this->getXWeeklyOccurrencesSince($date, $afterDate, $count, $skipMod, $repetition->repetition_moment);
 | 
						|
        }
 | 
						|
        if ('monthly' === $repetition->repetition_type) {
 | 
						|
            $occurrences = $this->getXMonthlyOccurrencesSince($date, $afterDate, $count, $skipMod, $repetition->repetition_moment);
 | 
						|
        }
 | 
						|
        if ('ndom' === $repetition->repetition_type) {
 | 
						|
            $occurrences = $this->getXNDomOccurrencesSince($date, $afterDate, $count, $skipMod, $repetition->repetition_moment);
 | 
						|
        }
 | 
						|
        if ('yearly' === $repetition->repetition_type) {
 | 
						|
            $occurrences = $this->getXYearlyOccurrencesSince($date, $afterDate, $count, $skipMod, $repetition->repetition_moment);
 | 
						|
        }
 | 
						|
 | 
						|
        // filter out all the weekend days:
 | 
						|
        $occurrences = $this->filterWeekends($repetition, $occurrences);
 | 
						|
 | 
						|
        // filter out everything if "repeat_until" is set.
 | 
						|
        $repeatUntil = $repetition->recurrence->repeat_until;
 | 
						|
 | 
						|
        return $this->filterMaxDate($repeatUntil, $occurrences);
 | 
						|
    }
 | 
						|
 | 
						|
    /**
 | 
						|
     * @param Carbon|null $max
 | 
						|
     * @param array       $occurrences
 | 
						|
     *
 | 
						|
     * @return array
 | 
						|
     */
 | 
						|
    private function filterMaxDate(?Carbon $max, array $occurrences): array
 | 
						|
    {
 | 
						|
        if (null === $max) {
 | 
						|
            return $occurrences;
 | 
						|
        }
 | 
						|
        $filtered = [];
 | 
						|
        foreach ($occurrences as $date) {
 | 
						|
            if ($date->lte($max)) {
 | 
						|
                $filtered[] = $date;
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
        return $filtered;
 | 
						|
    }
 | 
						|
 | 
						|
    /**
 | 
						|
     * Parse the repetition in a string that is user readable.
 | 
						|
     *
 | 
						|
     * @param RecurrenceRepetition $repetition
 | 
						|
     *
 | 
						|
     * @return string
 | 
						|
     * @throws FireflyException
 | 
						|
     */
 | 
						|
    public function repetitionDescription(RecurrenceRepetition $repetition): string
 | 
						|
    {
 | 
						|
        app('log')->debug('Now in repetitionDescription()');
 | 
						|
        /** @var Preference $pref */
 | 
						|
        $pref     = app('preferences')->getForUser($this->user, 'language', config('firefly.default_language', 'en_US'));
 | 
						|
        $language = $pref->data;
 | 
						|
        if (is_array($language)) {
 | 
						|
            $language = 'en_US';
 | 
						|
        }
 | 
						|
        $language = (string)$language;
 | 
						|
        if ('daily' === $repetition->repetition_type) {
 | 
						|
            return (string)trans('firefly.recurring_daily', [], $language);
 | 
						|
        }
 | 
						|
        if ('weekly' === $repetition->repetition_type) {
 | 
						|
            $dayOfWeek = trans(sprintf('config.dow_%s', $repetition->repetition_moment), [], $language);
 | 
						|
            if ($repetition->repetition_skip > 0) {
 | 
						|
                return (string)trans('firefly.recurring_weekly_skip', ['weekday' => $dayOfWeek, 'skip' => $repetition->repetition_skip + 1], $language);
 | 
						|
            }
 | 
						|
 | 
						|
            return (string)trans('firefly.recurring_weekly', ['weekday' => $dayOfWeek], $language);
 | 
						|
        }
 | 
						|
        if ('monthly' === $repetition->repetition_type) {
 | 
						|
            if ($repetition->repetition_skip > 0) {
 | 
						|
                return (string)trans(
 | 
						|
                    'firefly.recurring_monthly_skip',
 | 
						|
                    ['dayOfMonth' => $repetition->repetition_moment, 'skip' => $repetition->repetition_skip + 1],
 | 
						|
                    $language
 | 
						|
                );
 | 
						|
            }
 | 
						|
 | 
						|
            return (string)trans(
 | 
						|
                'firefly.recurring_monthly',
 | 
						|
                ['dayOfMonth' => $repetition->repetition_moment, 'skip' => $repetition->repetition_skip - 1],
 | 
						|
                $language
 | 
						|
            );
 | 
						|
        }
 | 
						|
        if ('ndom' === $repetition->repetition_type) {
 | 
						|
            $parts = explode(',', $repetition->repetition_moment);
 | 
						|
            // first part is number of week, second is weekday.
 | 
						|
            $dayOfWeek = trans(sprintf('config.dow_%s', $parts[1]), [], $language);
 | 
						|
 | 
						|
            return (string)trans('firefly.recurring_ndom', ['weekday' => $dayOfWeek, 'dayOfMonth' => $parts[0]], $language);
 | 
						|
        }
 | 
						|
        if ('yearly' === $repetition->repetition_type) {
 | 
						|
            //
 | 
						|
            $today       = today(config('app.timezone'))->endOfYear();
 | 
						|
            $repDate     = Carbon::createFromFormat('Y-m-d', $repetition->repetition_moment);
 | 
						|
            if(false === $repDate) {
 | 
						|
                $repDate = clone $today;
 | 
						|
            }
 | 
						|
            $diffInYears = $today->diffInYears($repDate);
 | 
						|
            $repDate->addYears($diffInYears); // technically not necessary.
 | 
						|
            $string = $repDate->isoFormat((string)trans('config.month_and_day_no_year_js'));
 | 
						|
 | 
						|
            return (string)trans('firefly.recurring_yearly', ['date' => $string], $language);
 | 
						|
        }
 | 
						|
 | 
						|
        return '';
 | 
						|
    }
 | 
						|
 | 
						|
    /**
 | 
						|
     * @inheritDoc
 | 
						|
     */
 | 
						|
    public function searchRecurrence(string $query, int $limit): Collection
 | 
						|
    {
 | 
						|
        $search = $this->user->recurrences();
 | 
						|
        if ('' !== $query) {
 | 
						|
            $search->where('recurrences.title', 'LIKE', sprintf('%%%s%%', $query));
 | 
						|
        }
 | 
						|
        $search
 | 
						|
            ->orderBy('recurrences.title', 'ASC');
 | 
						|
 | 
						|
        return $search->take($limit)->get(['id', 'title', 'description']);
 | 
						|
    }
 | 
						|
 | 
						|
    /**
 | 
						|
     * @param array $data
 | 
						|
     *
 | 
						|
     * @return Recurrence
 | 
						|
     * @throws FireflyException
 | 
						|
     * @throws JsonException
 | 
						|
     */
 | 
						|
    public function store(array $data): Recurrence
 | 
						|
    {
 | 
						|
        /** @var RecurrenceFactory $factory */
 | 
						|
        $factory = app(RecurrenceFactory::class);
 | 
						|
        $factory->setUser($this->user);
 | 
						|
        return $factory->create($data);
 | 
						|
    }
 | 
						|
 | 
						|
    /**
 | 
						|
     * @inheritDoc
 | 
						|
     */
 | 
						|
    public function totalTransactions(Recurrence $recurrence, RecurrenceRepetition $repetition): int
 | 
						|
    {
 | 
						|
        // if repeat = null just return 0.
 | 
						|
        if (null === $recurrence->repeat_until && 0 === (int)$recurrence->repetitions) {
 | 
						|
            return 0;
 | 
						|
        }
 | 
						|
        // expect X transactions then stop. Return that number
 | 
						|
        if (null === $recurrence->repeat_until && 0 !== (int)$recurrence->repetitions) {
 | 
						|
            return (int)$recurrence->repetitions;
 | 
						|
        }
 | 
						|
 | 
						|
        // need to calculate, this depends on the repetition:
 | 
						|
        if (null !== $recurrence->repeat_until && 0 === (int)$recurrence->repetitions) {
 | 
						|
            $occurrences = $this->getOccurrencesInRange($repetition, $recurrence->first_date ?? today(), $recurrence->repeat_until);
 | 
						|
 | 
						|
            return count($occurrences);
 | 
						|
        }
 | 
						|
 | 
						|
        return 0;
 | 
						|
    }
 | 
						|
 | 
						|
    /**
 | 
						|
     * Generate events in the date range.
 | 
						|
     *
 | 
						|
     * @param RecurrenceRepetition $repetition
 | 
						|
     * @param Carbon               $start
 | 
						|
     * @param Carbon               $end
 | 
						|
     *
 | 
						|
     * @return array
 | 
						|
     *
 | 
						|
     */
 | 
						|
    public function getOccurrencesInRange(RecurrenceRepetition $repetition, Carbon $start, Carbon $end): array
 | 
						|
    {
 | 
						|
        $occurrences = [];
 | 
						|
        $mutator     = clone $start;
 | 
						|
        $mutator->startOfDay();
 | 
						|
        $skipMod = $repetition->repetition_skip + 1;
 | 
						|
        app('log')->debug(sprintf('Calculating occurrences for rep type "%s"', $repetition->repetition_type));
 | 
						|
        app('log')->debug(sprintf('Mutator is now: %s', $mutator->format('Y-m-d')));
 | 
						|
 | 
						|
        if ('daily' === $repetition->repetition_type) {
 | 
						|
            $occurrences = $this->getDailyInRange($mutator, $end, $skipMod);
 | 
						|
        }
 | 
						|
        if ('weekly' === $repetition->repetition_type) {
 | 
						|
            $occurrences = $this->getWeeklyInRange($mutator, $end, $skipMod, $repetition->repetition_moment);
 | 
						|
        }
 | 
						|
        if ('monthly' === $repetition->repetition_type) {
 | 
						|
            $occurrences = $this->getMonthlyInRange($mutator, $end, $skipMod, $repetition->repetition_moment);
 | 
						|
        }
 | 
						|
        if ('ndom' === $repetition->repetition_type) {
 | 
						|
            $occurrences = $this->getNdomInRange($mutator, $end, $skipMod, $repetition->repetition_moment);
 | 
						|
        }
 | 
						|
        if ('yearly' === $repetition->repetition_type) {
 | 
						|
            $occurrences = $this->getYearlyInRange($mutator, $end, $skipMod, $repetition->repetition_moment);
 | 
						|
        }
 | 
						|
 | 
						|
        // filter out all the weekend days:
 | 
						|
        return $this->filterWeekends($repetition, $occurrences);
 | 
						|
    }
 | 
						|
 | 
						|
    /**
 | 
						|
     * Update a recurring transaction.
 | 
						|
     *
 | 
						|
     * @param Recurrence $recurrence
 | 
						|
     * @param array      $data
 | 
						|
     *
 | 
						|
     * @return Recurrence
 | 
						|
     * @throws FireflyException
 | 
						|
     */
 | 
						|
    public function update(Recurrence $recurrence, array $data): Recurrence
 | 
						|
    {
 | 
						|
        /** @var RecurrenceUpdateService $service */
 | 
						|
        $service = app(RecurrenceUpdateService::class);
 | 
						|
 | 
						|
        return $service->update($recurrence, $data);
 | 
						|
    }
 | 
						|
}
 |