diff --git a/app/Api/V1/Controllers/Models/Recurrence/ShowController.php b/app/Api/V1/Controllers/Models/Recurrence/ShowController.php index 7440af4404..f3db9a5532 100644 --- a/app/Api/V1/Controllers/Models/Recurrence/ShowController.php +++ b/app/Api/V1/Controllers/Models/Recurrence/ShowController.php @@ -112,6 +112,13 @@ class ShowController extends Controller { $manager = $this->getManager(); + // enrich + /** @var User $admin */ + $admin = auth()->user(); + $enrichment = new RecurringEnrichment(); + $enrichment->setUser($admin); + $recurrence = $enrichment->enrichSingle($recurrence); + /** @var RecurrenceTransformer $transformer */ $transformer = app(RecurrenceTransformer::class); $transformer->setParameters($this->parameters); diff --git a/app/Support/JsonApi/Enrichments/RecurringEnrichment.php b/app/Support/JsonApi/Enrichments/RecurringEnrichment.php index 36809c9ac7..a155b22975 100644 --- a/app/Support/JsonApi/Enrichments/RecurringEnrichment.php +++ b/app/Support/JsonApi/Enrichments/RecurringEnrichment.php @@ -5,28 +5,59 @@ namespace FireflyIII\Support\JsonApi\Enrichments; use Carbon\Carbon; use FireflyIII\Enums\RecurrenceRepetitionWeekend; use FireflyIII\Enums\TransactionTypeEnum; +use FireflyIII\Exceptions\FireflyException; +use FireflyIII\Factory\CategoryFactory; +use FireflyIII\Models\Account; +use FireflyIII\Models\Bill; +use FireflyIII\Models\Budget; +use FireflyIII\Models\Category; +use FireflyIII\Models\Note; +use FireflyIII\Models\PiggyBank; use FireflyIII\Models\Preference; use FireflyIII\Models\Recurrence; use FireflyIII\Models\RecurrenceRepetition; +use FireflyIII\Models\RecurrenceTransaction; +use FireflyIII\Models\RecurrenceTransactionMeta; +use FireflyIII\Models\TransactionCurrency; use FireflyIII\Models\TransactionType; use FireflyIII\Models\UserGroup; use FireflyIII\Repositories\Recurring\RecurringRepositoryInterface; +use FireflyIII\Support\Facades\Amount; use FireflyIII\Support\Facades\Preferences; +use FireflyIII\Support\Http\Api\ExchangeRateConverter; use FireflyIII\User; use Illuminate\Database\Eloquent\Model; +use Illuminate\Support\Arr; use Illuminate\Support\Collection; use Illuminate\Support\Facades\Log; class RecurringEnrichment implements EnrichmentInterface { - private Collection $collection; - private array $ids = []; - private array $transactionTypeIds = []; - private array $transactionTypes = []; - private array $repetitions = []; - private User $user; - private UserGroup $userGroup; - private string $language = 'en_US'; + private Collection $collection; + private array $ids = []; + private array $transactionTypeIds = []; + private array $transactionTypes = []; + private array $notes = []; + private array $repetitions = []; + private array $transactions = []; + private User $user; + private UserGroup $userGroup; + private string $language = 'en_US'; + private array $currencyIds = []; + private array $foreignCurrencyIds = []; + private array $sourceAccountIds = []; + private array $destinationAccountIds = []; + private array $accounts = []; + private array $currencies = []; + private array $recurrenceIds = []; + private TransactionCurrency $primaryCurrency; + private bool $convertToPrimary = false; + + public function __construct() + { + $this->primaryCurrency = Amount::getPrimaryCurrency(); + $this->convertToPrimary = Amount::convertToPrimary(); + } public function enrich(Collection $collection): Collection { @@ -34,6 +65,10 @@ class RecurringEnrichment implements EnrichmentInterface $this->collectIds(); $this->collectRepetitions(); $this->collectTransactions(); + $this->collectCurrencies(); + $this->collectNotes(); + $this->collectAccounts(); + $this->collectTransactionMetaData(); $this->appendCollectedData(); @@ -82,6 +117,7 @@ class RecurringEnrichment implements EnrichmentInterface private function collectRepetitions(): void { + Log::debug('Start of enrichment: collectRepetitions()'); $repository = app(RecurringRepositoryInterface::class); $repository->setUserGroup($this->userGroup); $set = RecurrenceRepetition::whereIn('recurrence_id', $this->ids)->get(); @@ -115,10 +151,53 @@ class RecurringEnrichment implements EnrichmentInterface 'occurrences' => $occurrences, ]; } + Log::debug('End of enrichment: collectRepetitions()'); } private function collectTransactions(): void { + $set = RecurrenceTransaction::whereIn('recurrence_id', $this->ids)->get(); + /** @var RecurrenceTransaction $transaction */ + foreach ($set as $transaction) { + $id = (int)$transaction->recurrence_id; + $transactionId = (int)$transaction->id; + $this->recurrenceIds[$transactionId] = $id; + $this->transactions[$id] ??= []; + $amount = $transaction->amount; + $foreignAmount = $transaction->foreign_amount; + + $this->transactions[$id][$transactionId] = [ + 'id' => (string)$transactionId, + 'recurrence_id' => $id, + 'transaction_currency_id' => (int)$transaction->transaction_currency_id, + 'foreign_currency_id' => null === $transaction->foreign_currency_id ? null : (int)$transaction->foreign_currency_id, + 'source_id' => (int)$transaction->source_id, + 'object_has_currency_setting' => true, + 'destination_id' => (int)$transaction->destination_id, + 'amount' => $amount, + 'foreign_amount' => $foreignAmount, + 'pc_amount' => null, + 'pc_foreign_amount' => null, + 'description' => $transaction->description, + 'tags' => [], + 'category_id' => null, + 'category_name' => null, + 'budget_id' => null, + 'budget_name' => null, + 'piggy_bank_id' => null, + 'piggy_bank_name' => null, + 'subscription_id' => null, + 'subscription_name' => null, + + ]; + // collect all kinds of meta data to be collected later. + $this->currencyIds[$transactionId] = (int)$transaction->transaction_currency_id; + $this->sourceAccountIds[$transactionId] = (int)$transaction->source_id; + $this->destinationAccountIds[$transactionId] = (int)$transaction->destination_id; + if (null !== $transaction->foreign_currency_id) { + $this->foreignCurrencyIds[$transactionId] = (int)$transaction->foreign_currency_id; + } + } } private function appendCollectedData(): void @@ -126,7 +205,9 @@ class RecurringEnrichment implements EnrichmentInterface $this->collection = $this->collection->map(function (Recurrence $item) { $id = (int)$item->id; $meta = [ - 'repetitions' => array_values($this->repetitions[$id] ?? []), + 'notes' => $this->notes[$id] ?? null, + 'repetitions' => array_values($this->repetitions[$id] ?? []), + 'transactions' => $this->processTransactions(array_values($this->transactions[$id] ?? [])), ]; $item->meta = $meta; @@ -196,4 +277,297 @@ class RecurringEnrichment implements EnrichmentInterface $language = (string)$language; $this->language = $language; } + + private function collectCurrencies(): void + { + $all = array_merge(array_unique($this->currencyIds), array_unique($this->foreignCurrencyIds)); + $currencies = TransactionCurrency::whereIn('id', array_unique($all))->get(); + foreach ($currencies as $currency) { + $id = (int)$currency->id; + $this->currencies[$id] = $currency; + } + } + + private function processTransactions(array $transactions): array + { + $return = []; + $converter = new ExchangeRateConverter(); + foreach ($transactions as $transaction) { + $currencyId = $transaction['transaction_currency_id']; + $pcAmount = null; + $pcForeignAmount = null; + // set the same amount in the primary currency, if both are the same anyway. + if (true === $this->convertToPrimary && $currencyId === (int)$this->primaryCurrency->id) { + $pcAmount = $transaction['amount']; + } + // convert the amount to the primary currency, if it is not the same. + if (true === $this->convertToPrimary && $currencyId !== (int)$this->primaryCurrency->id) { + $pcAmount = $converter->convert($this->currencies[$currencyId], $this->primaryCurrency, today(), $transaction['amount']); + } + if (null !== $transaction['foreign_amount']) { + $foreignCurrencyId = $transaction['foreign_currency_id']; + if ($foreignCurrencyId !== $this->primaryCurrency->id) { + $pcForeignAmount = $converter->convert($this->currencies[$foreignCurrencyId], $this->primaryCurrency, today(), $transaction['foreign_amount']); + } + } + + $transaction['pc_amount'] = $pcAmount; + $transaction['pc_foreign_amount'] = $pcForeignAmount; + + $sourceId = $transaction['source_id']; + $transaction['source_name'] = $this->accounts[$sourceId]->name; + $transaction['source_iban'] = $this->accounts[$sourceId]->iban; + $transaction['source_type'] = $this->accounts[$sourceId]->accountType->type; + $transaction['source_id'] = (string)$transaction['source_id']; + + $destId = $transaction['destination_id']; + $transaction['destination_name'] = $this->accounts[$destId]->name; + $transaction['destination_iban'] = $this->accounts[$destId]->iban; + $transaction['destination_type'] = $this->accounts[$destId]->accountType->type; + $transaction['destination_id'] = (string)$transaction['destination_id']; + + $transaction['currency_id'] = (string)$currencyId; + $transaction['currency_name'] = $this->currencies[$currencyId]->name; + $transaction['currency_code'] = $this->currencies[$currencyId]->code; + $transaction['currency_symbol'] = $this->currencies[$currencyId]->symbol; + $transaction['currency_decimal_places'] = $this->currencies[$currencyId]->decimal_places; + + $transaction['primary_currency_id'] = (string)$this->primaryCurrency->id; + $transaction['primary_currency_name'] = $this->primaryCurrency->name; + $transaction['primary_currency_code'] = $this->primaryCurrency->code; + $transaction['primary_currency_symbol'] = $this->primaryCurrency->symbol; + $transaction['primary_currency_decimal_places'] = $this->primaryCurrency->decimal_places; + + // $transaction['foreign_currency_id'] = null; + $transaction['foreign_currency_name'] = null; + $transaction['foreign_currency_code'] = null; + $transaction['foreign_currency_symbol'] = null; + $transaction['foreign_currency_decimal_places'] = null; + if (null !== $transaction['foreign_currency_id']) { + $currencyId = $transaction['foreign_currency_id']; + $transaction['foreign_currency_id'] = (string)$currencyId; + $transaction['foreign_currency_name'] = $this->currencies[$currencyId]->name; + $transaction['foreign_currency_code'] = $this->currencies[$currencyId]->code; + $transaction['foreign_currency_symbol'] = $this->currencies[$currencyId]->symbol; + $transaction['foreign_currency_decimal_places'] = $this->currencies[$currencyId]->decimal_places; + } + unset($transaction['transaction_currency_id']); + $return[] = $transaction; + } + return $return; + } + + private function collectAccounts(): void + { + $all = array_merge(array_unique($this->sourceAccountIds), array_unique($this->destinationAccountIds)); + $accounts = Account::with(['accountType'])->whereIn('id', array_unique($all))->get(); + /** @var Account $account */ + foreach ($accounts as $account) { + $id = (int)$account->id; + $this->accounts[$id] = $account; + } + } + + private function collectTransactionMetaData(): void + { + $ids = array_keys($this->transactions); + $meta = RecurrenceTransactionMeta::whereIn('rt_id', $ids)->get(); + // other meta-data to be collected: + $billIds = []; + $piggyBankIds = []; + $categoryIds = []; + $categoryNames = []; + $budgetIds = []; + foreach ($meta as $entry) { + $id = (int)$entry->id; + $transactionId = (int)$entry->rt_id; + $recurrenceId = $this->recurrenceIds[$transactionId]; + $name = (string)$entry->name; + + switch ($name) { + default: + throw new FireflyException(sprintf('Recurrence transformer cant handle field "%s"', $name)); + + case 'bill_id': + if ((int)$entry->value > 0) { + $this->transactions[$recurrenceId][$transactionId]['subscription_id'] = $entry->value; + if (!array_key_exists($id, $billIds)) { + $billIds[$id] = [ + 'recurrence_id' => $recurrenceId, + 'transaction_id' => $transactionId, + 'bill_id' => (int)$entry->value, + ]; + } + } + break; + + case 'tags': + $this->transactions[$recurrenceId][$transactionId]['tags'] = json_decode((string)$entry->value); + + break; + + case 'piggy_bank_id': + if ((int)$entry->value > 0) { + $this->transactions[$recurrenceId][$transactionId]['piggy_bank_id'] = (string)$entry->value; + if (!array_key_exists($id, $piggyBankIds)) { + $piggyBankIds[$id] = [ + 'recurrence_id' => $recurrenceId, + 'transaction_id' => $transactionId, + 'piggy_bank_id' => (int)$entry->value, + ]; + } + } + break; + + case 'category_id': + if ((int)$entry->value > 0) { + $this->transactions[$recurrenceId][$transactionId]['category_id'] = (string)$entry->value; + if (!array_key_exists($id, $categoryIds)) { + $categoryIds[$id] = [ + 'recurrence_id' => $recurrenceId, + 'transaction_id' => $transactionId, + 'category_id' => (int)$entry->value, + ]; + } + } + break; + + case 'category_name': + if ('' !== (string)$entry->value) { + $this->transactions[$recurrenceId][$transactionId]['category_name'] = (string)$entry->value; + if (!array_key_exists($id, $categoryIds)) { + $categoryNames[$id] = [ + 'recurrence_id' => $recurrenceId, + 'transaction_id' => $transactionId, + 'category_name' => $entry->value, + ]; + } + } + break; + + case 'budget_id': + if ((int)$entry->value > 0) { + $this->transactions[$recurrenceId][$transactionId]['budget_id'] = (string)$entry->value; + if (!array_key_exists($id, $budgetIds)) { + $budgetIds[$id] = [ + 'recurrence_id' => $recurrenceId, + 'transaction_id' => $transactionId, + 'budget_id' => (int)$entry->value, + ]; + } + } + break; + } + } + $this->collectBillInfo($billIds); + $this->collectPiggyBankInfo($piggyBankIds); + $this->collectCategoryIdInfo($categoryIds); + $this->collectCategoryNameInfo($categoryNames); + $this->collectBudgetInfo($budgetIds); + } + + private function collectBillInfo(array $billIds): void + { + if (0 === count($billIds)) { + return; + } + $ids = Arr::pluck($billIds, 'bill_id'); + $bills = Bill::whereIn('id', $ids)->get(); + $mapped = []; + foreach ($bills as $bill) { + $mapped[(int)$bill->id] = $bill; + } + foreach ($billIds as $info) { + $recurrenceId = $info['recurrence_id']; + $transactionId = $info['transaction_id']; + $this->transactions[$recurrenceId][$transactionId]['subscription_name'] = $mapped[$info['bill_id']]->name ?? ''; + } + } + + private function collectPiggyBankInfo(array $piggyBankIds): void + { + if (0 === count($piggyBankIds)) { + return; + } + $ids = Arr::pluck($piggyBankIds, 'piggy_bank_id'); + $piggyBanks = PiggyBank::whereIn('id', $ids)->get(); + $mapped = []; + foreach ($piggyBanks as $piggyBank) { + $mapped[(int)$piggyBank->id] = $piggyBank; + } + foreach ($piggyBankIds as $info) { + $recurrenceId = $info['recurrence_id']; + $transactionId = $info['transaction_id']; + $this->transactions[$recurrenceId][$transactionId]['piggy_bank_name'] = $mapped[$info['piggy_bank_id']]->name ?? ''; + } + } + + private function collectCategoryIdInfo(array $categoryIds): void + { + if (0 === count($categoryIds)) { + return; + } + $ids = Arr::pluck($categoryIds, 'category_id'); + $categories = Category::whereIn('id', $ids)->get(); + $mapped = []; + foreach ($categories as $category) { + $mapped[(int)$category->id] = $category; + } + foreach ($categoryIds as $info) { + $recurrenceId = $info['recurrence_id']; + $transactionId = $info['transaction_id']; + $this->transactions[$recurrenceId][$transactionId]['category_name'] = $mapped[$info['category_id']]->name ?? ''; + } + } + + /** + * TODO This method does look-up in a loop. + */ + private function collectCategoryNameInfo(array $categoryNames): void + { + if (0 === count($categoryNames)) { + return; + } + $factory = app(CategoryFactory::class); + $factory->setUser($this->user); + foreach ($categoryNames as $info) { + $recurrenceId = $info['recurrence_id']; + $transactionId = $info['transaction_id']; + $category = $factory->findOrCreate(null, $info['category_name']); + if (null !== $category) { + $this->transactions[$recurrenceId][$transactionId]['category_id'] = (string)$category->id; + $this->transactions[$recurrenceId][$transactionId]['category_name'] = $category->name; + } + } + } + + private function collectBudgetInfo(array $budgetIds): void + { + if (0 === count($budgetIds)) { + return; + } + $ids = Arr::pluck($budgetIds, 'budget_id'); + $categories = Budget::whereIn('id', $ids)->get(); + $mapped = []; + foreach ($categories as $category) { + $mapped[(int)$category->id] = $category; + } + foreach ($budgetIds as $info) { + $recurrenceId = $info['recurrence_id']; + $transactionId = $info['transaction_id']; + $this->transactions[$recurrenceId][$transactionId]['budget_name'] = $mapped[$info['budget_id']]->name ?? ''; + } + } + + private function collectNotes(): void + { + $notes = Note::query()->whereIn('noteable_id', $this->ids) + ->whereNotNull('notes.text') + ->where('notes.text', '!=', '') + ->where('noteable_type', Recurrence::class)->get(['notes.noteable_id', 'notes.text'])->toArray(); + foreach ($notes as $note) { + $this->notes[(int)$note['noteable_id']] = (string)$note['text']; + } + Log::debug(sprintf('Enrich with %d note(s)', count($this->notes))); + } } diff --git a/app/Support/Repositories/Recurring/CalculateXOccurrencesSince.php b/app/Support/Repositories/Recurring/CalculateXOccurrencesSince.php index 7a1d34e39e..a2266b51f5 100644 --- a/app/Support/Repositories/Recurring/CalculateXOccurrencesSince.php +++ b/app/Support/Repositories/Recurring/CalculateXOccurrencesSince.php @@ -25,6 +25,7 @@ declare(strict_types=1); namespace FireflyIII\Support\Repositories\Recurring; use Carbon\Carbon; +use Illuminate\Support\Facades\Log; /** * Class CalculateXOccurrencesSince @@ -37,7 +38,7 @@ trait CalculateXOccurrencesSince */ protected function getXDailyOccurrencesSince(Carbon $date, Carbon $afterDate, int $count, int $skipMod): array { - app('log')->debug(sprintf('Now in %s', __METHOD__)); + Log::debug(sprintf('Now in %s', __METHOD__)); $return = []; $mutator = clone $date; $total = 0; @@ -62,7 +63,7 @@ trait CalculateXOccurrencesSince */ protected function getXMonthlyOccurrencesSince(Carbon $date, Carbon $afterDate, int $count, int $skipMod, string $moment): array { - app('log')->debug(sprintf('Now in %s(%s, %s, %d)', __METHOD__, $date->format('Y-m-d'), $afterDate->format('Y-m-d'), $count)); + Log::debug(sprintf('Now in %s(%s, %s, %d)', __METHOD__, $date->format('Y-m-d'), $afterDate->format('Y-m-d'), $count)); $return = []; $mutator = clone $date; $total = 0; @@ -70,24 +71,25 @@ trait CalculateXOccurrencesSince $dayOfMonth = (int) $moment; $dayOfMonth = 0 === $dayOfMonth ? 1 : $dayOfMonth; if ($mutator->day > $dayOfMonth) { - app('log')->debug(sprintf('%d is after %d, add a month. Mutator is now', $mutator->day, $dayOfMonth)); + Log::debug(sprintf('%d is after %d, add a month. Mutator is now...', $mutator->day, $dayOfMonth)); // day has passed already, add a month. $mutator->addMonth(); - app('log')->debug(sprintf('%s', $mutator->format('Y-m-d'))); + Log::debug(sprintf('%s', $mutator->toAtomString())); } while ($total < $count) { $domCorrected = min($dayOfMonth, $mutator->daysInMonth); $mutator->day = $domCorrected; - app('log')->debug(sprintf('Mutator is now %s', $mutator->format('Y-m-d'))); + $mutator->setTime(0,0,0); if (0 === $attempts % $skipMod && $mutator->gte($afterDate)) { - app('log')->debug('Is added to the list.'); + Log::debug(sprintf('Mutator is now %s and is added to the list.', $mutator->toAtomString())); $return[] = clone $mutator; ++$total; } ++$attempts; $mutator = $mutator->endOfMonth()->addDay(); } + Log::debug('Collected enough occurrences.'); return $return; } @@ -100,7 +102,7 @@ trait CalculateXOccurrencesSince */ protected function getXNDomOccurrencesSince(Carbon $date, Carbon $afterDate, int $count, int $skipMod, string $moment): array { - app('log')->debug(sprintf('Now in %s', __METHOD__)); + Log::debug(sprintf('Now in %s', __METHOD__)); $return = []; $total = 0; $attempts = 0; @@ -134,7 +136,7 @@ trait CalculateXOccurrencesSince */ protected function getXWeeklyOccurrencesSince(Carbon $date, Carbon $afterDate, int $count, int $skipMod, string $moment): array { - app('log')->debug(sprintf('Now in %s', __METHOD__)); + Log::debug(sprintf('Now in %s', __METHOD__)); $return = []; $total = 0; $attempts = 0; @@ -173,7 +175,7 @@ trait CalculateXOccurrencesSince */ protected function getXYearlyOccurrencesSince(Carbon $date, Carbon $afterDate, int $count, int $skipMod, string $moment): array { - app('log')->debug(sprintf('Now in %s(%s, %d, %d, %s)', __METHOD__, $date->format('Y-m-d'), $date->format('Y-m-d'), $count, $skipMod)); + Log::debug(sprintf('Now in %s(%s, %d, %d, %s)', __METHOD__, $date->format('Y-m-d'), $date->format('Y-m-d'), $count, $skipMod)); $return = []; $mutator = clone $date; $total = 0; @@ -181,19 +183,19 @@ trait CalculateXOccurrencesSince $date = new Carbon($moment); $date->year = $mutator->year; if ($mutator > $date) { - app('log')->debug( + Log::debug( sprintf('mutator (%s) > date (%s), so add a year to date (%s)', $mutator->format('Y-m-d'), $date->format('Y-m-d'), $date->format('Y-m-d')) ); $date->addYear(); - app('log')->debug(sprintf('Date is now %s', $date->format('Y-m-d'))); + Log::debug(sprintf('Date is now %s', $date->format('Y-m-d'))); } $obj = clone $date; while ($total < $count) { - app('log')->debug(sprintf('total (%d) < count (%d) so go.', $total, $count)); - app('log')->debug(sprintf('attempts (%d) %% skipmod (%d) === %d', $attempts, $skipMod, $attempts % $skipMod)); - app('log')->debug(sprintf('Obj (%s) gte afterdate (%s)? %s', $obj->format('Y-m-d'), $afterDate->format('Y-m-d'), var_export($obj->gte($afterDate), true))); + Log::debug(sprintf('total (%d) < count (%d) so go.', $total, $count)); + Log::debug(sprintf('attempts (%d) %% skipmod (%d) === %d', $attempts, $skipMod, $attempts % $skipMod)); + Log::debug(sprintf('Obj (%s) gte afterdate (%s)? %s', $obj->format('Y-m-d'), $afterDate->format('Y-m-d'), var_export($obj->gte($afterDate), true))); if (0 === $attempts % $skipMod && $obj->gte($afterDate)) { - app('log')->debug('All conditions true, add obj.'); + Log::debug('All conditions true, add obj.'); $return[] = clone $obj; ++$total; } diff --git a/app/Transformers/RecurrenceTransformer.php b/app/Transformers/RecurrenceTransformer.php index 8428882e70..ffd3a86a4b 100644 --- a/app/Transformers/RecurrenceTransformer.php +++ b/app/Transformers/RecurrenceTransformer.php @@ -45,22 +45,12 @@ use function Safe\json_decode; */ class RecurrenceTransformer extends AbstractTransformer { - private readonly BillRepositoryInterface $billRepos; - private readonly BudgetRepositoryInterface $budgetRepos; - private readonly CategoryFactory $factory; - private readonly PiggyBankRepositoryInterface $piggyRepos; - private readonly RecurringRepositoryInterface $repository; /** * RecurrenceTransformer constructor. */ public function __construct() { - $this->repository = app(RecurringRepositoryInterface::class); - $this->piggyRepos = app(PiggyBankRepositoryInterface::class); - $this->factory = app(CategoryFactory::class); - $this->budgetRepos = app(BudgetRepositoryInterface::class); - $this->billRepos = app(BillRepositoryInterface::class); } /** @@ -71,15 +61,8 @@ class RecurrenceTransformer extends AbstractTransformer public function transform(Recurrence $recurrence): array { Log::debug('Now in Recurrence::transform()'); - $this->repository->setUser($recurrence->user); - $this->piggyRepos->setUser($recurrence->user); - $this->factory->setUser($recurrence->user); - - $this->budgetRepos->setUser($recurrence->user); - Log::debug('Set user.'); $shortType = (string)config(sprintf('firefly.transactionTypesToShort.%s', $recurrence->transactionType->type)); - $notes = $this->repository->getNoteText($recurrence); $reps = 0 === (int)$recurrence->repetitions ? null : (int)$recurrence->repetitions; Log::debug('Get basic data.'); @@ -97,10 +80,9 @@ class RecurrenceTransformer extends AbstractTransformer 'apply_rules' => $recurrence->apply_rules, 'active' => $recurrence->active, 'nr_of_repetitions' => $reps, - 'notes' => '' === $notes ? null : $notes, - 'new_repetitions' => $recurrence->meta['repetitions'], - 'repetitions' => $this->getRepetitions($recurrence), - 'transactions' => $this->getTransactions($recurrence), + 'notes' => $recurrence->meta['notes'], + 'repetitions' => $recurrence->meta['repetitions'], + 'new_transactions' => $recurrence->meta['transactions'], 'links' => [ [ 'rel' => 'self', @@ -109,208 +91,4 @@ class RecurrenceTransformer extends AbstractTransformer ], ]; } - - /** - * @throws FireflyException - */ - private function getRepetitions(Recurrence $recurrence): array - { - Log::debug('Now in getRepetitions().'); - $fromDate = $recurrence->latest_date ?? $recurrence->first_date; - $return = []; - - /** @var RecurrenceRepetition $repetition */ - foreach ($recurrence->recurrenceRepetitions as $repetition) { - $repetitionArray = [ - 'id' => (string)$repetition->id, - 'created_at' => $repetition->created_at->toAtomString(), - 'updated_at' => $repetition->updated_at->toAtomString(), - 'type' => $repetition->repetition_type, - 'moment' => $repetition->repetition_moment, - 'skip' => $repetition->repetition_skip, - 'weekend' => $repetition->weekend, - 'description' => $this->repository->repetitionDescription($repetition), - 'occurrences' => [], - ]; - - // get the (future) occurrences for this specific type of repetition: - $amount = 'daily' === $repetition->repetition_type ? 9 : 5; - $occurrences = $this->repository->getXOccurrencesSince($repetition, $fromDate, now(), $amount); - - /** @var Carbon $carbon */ - foreach ($occurrences as $carbon) { - $repetitionArray['occurrences'][] = $carbon->toAtomString(); - } - - $return[] = $repetitionArray; - } - - return $return; - } - - /** - * @throws FireflyException - */ - private function getTransactions(Recurrence $recurrence): array - { - Log::debug(sprintf('Now in %s', __METHOD__)); - $return = []; - - // get all transactions: - /** @var RecurrenceTransaction $transaction */ - foreach ($recurrence->recurrenceTransactions()->get() as $transaction) { - /** @var null|Account $sourceAccount */ - $sourceAccount = $transaction->sourceAccount; - - /** @var null|Account $destinationAccount */ - $destinationAccount = $transaction->destinationAccount; - $foreignCurrencyCode = null; - $foreignCurrencySymbol = null; - $foreignCurrencyDp = null; - $foreignCurrencyId = null; - if (null !== $transaction->foreign_currency_id) { - $foreignCurrencyId = (int)$transaction->foreign_currency_id; - $foreignCurrencyCode = $transaction->foreignCurrency->code; - $foreignCurrencySymbol = $transaction->foreignCurrency->symbol; - $foreignCurrencyDp = $transaction->foreignCurrency->decimal_places; - } - - // source info: - $sourceName = ''; - $sourceId = null; - $sourceType = null; - $sourceIban = null; - if (null !== $sourceAccount) { - $sourceName = $sourceAccount->name; - $sourceId = $sourceAccount->id; - $sourceType = $sourceAccount->accountType->type; - $sourceIban = $sourceAccount->iban; - } - $destinationName = ''; - $destinationId = null; - $destinationType = null; - $destinationIban = null; - if (null !== $destinationAccount) { - $destinationName = $destinationAccount->name; - $destinationId = $destinationAccount->id; - $destinationType = $destinationAccount->accountType->type; - $destinationIban = $destinationAccount->iban; - } - $amount = Steam::bcround($transaction->amount, $transaction->transactionCurrency->decimal_places); - $foreignAmount = null; - if (null !== $transaction->foreign_currency_id && null !== $transaction->foreign_amount) { - $foreignAmount = Steam::bcround($transaction->foreign_amount, $foreignCurrencyDp); - } - $transactionArray = [ - 'id' => (string)$transaction->id, - 'currency_id' => (string)$transaction->transaction_currency_id, - 'currency_code' => $transaction->transactionCurrency->code, - 'currency_symbol' => $transaction->transactionCurrency->symbol, - 'currency_decimal_places' => $transaction->transactionCurrency->decimal_places, - 'foreign_currency_id' => null === $foreignCurrencyId ? null : (string)$foreignCurrencyId, - 'foreign_currency_code' => $foreignCurrencyCode, - 'foreign_currency_symbol' => $foreignCurrencySymbol, - 'foreign_currency_decimal_places' => $foreignCurrencyDp, - 'source_id' => (string)$sourceId, - 'source_name' => $sourceName, - 'source_iban' => $sourceIban, - 'source_type' => $sourceType, - 'destination_id' => (string)$destinationId, - 'destination_name' => $destinationName, - 'destination_iban' => $destinationIban, - 'destination_type' => $destinationType, - 'amount' => $amount, - 'foreign_amount' => $foreignAmount, - 'description' => $transaction->description, - ]; - $transactionArray = $this->getTransactionMeta($transaction, $transactionArray); - if (null !== $transaction->foreign_currency_id) { - $transactionArray['foreign_currency_code'] = $transaction->foreignCurrency->code; - $transactionArray['foreign_currency_symbol'] = $transaction->foreignCurrency->symbol; - $transactionArray['foreign_currency_decimal_places'] = $transaction->foreignCurrency->decimal_places; - } - - // store transaction in recurrence array. - $return[] = $transactionArray; - } - - return $return; - } - - /** - * @throws FireflyException - */ - private function getTransactionMeta(RecurrenceTransaction $transaction, array $array): array - { - Log::debug(sprintf('Now in %s', __METHOD__)); - $array['tags'] = []; - $array['category_id'] = null; - $array['category_name'] = null; - $array['budget_id'] = null; - $array['budget_name'] = null; - $array['piggy_bank_id'] = null; - $array['piggy_bank_name'] = null; - $array['bill_id'] = null; - $array['bill_name'] = null; - - /** @var RecurrenceTransactionMeta $transactionMeta */ - foreach ($transaction->recurrenceTransactionMeta as $transactionMeta) { - switch ($transactionMeta->name) { - default: - throw new FireflyException(sprintf('Recurrence transformer cant handle field "%s"', $transactionMeta->name)); - - case 'bill_id': - $bill = $this->billRepos->find((int)$transactionMeta->value); - if (null !== $bill) { - $array['bill_id'] = (string)$bill->id; - $array['bill_name'] = $bill->name; - } - - break; - - case 'tags': - $array['tags'] = json_decode((string)$transactionMeta->value); - - break; - - case 'piggy_bank_id': - $piggy = $this->piggyRepos->find((int)$transactionMeta->value); - if (null !== $piggy) { - $array['piggy_bank_id'] = (string)$piggy->id; - $array['piggy_bank_name'] = $piggy->name; - } - - break; - - case 'category_id': - $category = $this->factory->findOrCreate((int)$transactionMeta->value, null); - if (null !== $category) { - $array['category_id'] = (string)$category->id; - $array['category_name'] = $category->name; - } - - break; - - case 'category_name': - $category = $this->factory->findOrCreate(null, $transactionMeta->value); - if (null !== $category) { - $array['category_id'] = (string)$category->id; - $array['category_name'] = $category->name; - } - - break; - - case 'budget_id': - $budget = $this->budgetRepos->find((int)$transactionMeta->value); - if (null !== $budget) { - $array['budget_id'] = (string)$budget->id; - $array['budget_name'] = $budget->name; - } - - break; - } - } - - return $array; - } }