From d740814f8845c9007479602bcffa0912770b05f3 Mon Sep 17 00:00:00 2001 From: James Cole Date: Sun, 1 Dec 2024 18:32:05 +0100 Subject: [PATCH] API works for multi-account piggies, the rest throws an exception --- .../Models/PiggyBank/ShowController.php | 2 +- .../Integrity/AddTimezonesToDates.php | 4 +- app/Models/PiggyBank.php | 5 ++ .../PiggyBank/ModifiesPiggyBanks.php | 1 + .../PiggyBank/PiggyBankRepository.php | 74 +++++++++---------- app/Transformers/PiggyBankTransformer.php | 58 +++++++++------ app/Transformers/V2/PiggyBankTransformer.php | 1 + app/User.php | 8 -- 8 files changed, 82 insertions(+), 71 deletions(-) diff --git a/app/Api/V1/Controllers/Models/PiggyBank/ShowController.php b/app/Api/V1/Controllers/Models/PiggyBank/ShowController.php index 2005bc4de5..442b47cb38 100644 --- a/app/Api/V1/Controllers/Models/PiggyBank/ShowController.php +++ b/app/Api/V1/Controllers/Models/PiggyBank/ShowController.php @@ -72,7 +72,7 @@ class ShowController extends Controller // types to get, page size: $pageSize = $this->parameters->get('limit'); - // get list of budgets. Count it and split it. + // get list of piggy banks. Count it and split it. $collection = $this->repository->getPiggyBanks(); $count = $collection->count(); $piggyBanks = $collection->slice(($this->parameters->get('page') - 1) * $pageSize, $pageSize); diff --git a/app/Console/Commands/Integrity/AddTimezonesToDates.php b/app/Console/Commands/Integrity/AddTimezonesToDates.php index 7dafa505c4..cd347bd274 100644 --- a/app/Console/Commands/Integrity/AddTimezonesToDates.php +++ b/app/Console/Commands/Integrity/AddTimezonesToDates.php @@ -67,8 +67,8 @@ class AddTimezonesToDates extends Command CurrencyExchangeRate::class => ['date'], // done InvitedUser::class => ['expires'], PiggyBankEvent::class => ['date'], - PiggyBankRepetition::class => ['startdate', 'targetdate'], - PiggyBank::class => ['startdate', 'targetdate'], // done + PiggyBankRepetition::class => ['start_date', 'target_date'], + PiggyBank::class => ['start_date', 'target_date'], // done Recurrence::class => ['first_date', 'repeat_until', 'latest_date'], Tag::class => ['date'], TransactionJournal::class => ['date'], diff --git a/app/Models/PiggyBank.php b/app/Models/PiggyBank.php index 911566e628..d5a936cf31 100644 --- a/app/Models/PiggyBank.php +++ b/app/Models/PiggyBank.php @@ -78,6 +78,11 @@ class PiggyBank extends Model throw new NotFoundHttpException(); } + public function transactionCurrency(): BelongsTo + { + return $this->belongsTo(TransactionCurrency::class); + } + public function account(): BelongsTo { return $this->belongsTo(Account::class); diff --git a/app/Repositories/PiggyBank/ModifiesPiggyBanks.php b/app/Repositories/PiggyBank/ModifiesPiggyBanks.php index 998fed9b23..09169ef898 100644 --- a/app/Repositories/PiggyBank/ModifiesPiggyBanks.php +++ b/app/Repositories/PiggyBank/ModifiesPiggyBanks.php @@ -45,6 +45,7 @@ trait ModifiesPiggyBanks public function addAmountToRepetition(PiggyBankRepetition $repetition, string $amount, TransactionJournal $journal): void { + throw new FireflyException('[a] Piggy bank repetitions are EOL.'); app('log')->debug(sprintf('addAmountToRepetition: %s', $amount)); if (-1 === bccomp($amount, '0')) { app('log')->debug('Remove amount.'); diff --git a/app/Repositories/PiggyBank/PiggyBankRepository.php b/app/Repositories/PiggyBank/PiggyBankRepository.php index 1e9658d290..ce32ca3269 100644 --- a/app/Repositories/PiggyBank/PiggyBankRepository.php +++ b/app/Repositories/PiggyBank/PiggyBankRepository.php @@ -94,7 +94,7 @@ class PiggyBankRepository implements PiggyBankRepositoryInterface public function getAttachments(PiggyBank $piggyBank): Collection { - $set = $piggyBank->attachments()->get(); + $set = $piggyBank->attachments()->get(); /** @var \Storage $disk */ $disk = \Storage::disk('upload'); @@ -115,16 +115,18 @@ class PiggyBankRepository implements PiggyBankRepositoryInterface */ public function getCurrentAmount(PiggyBank $piggyBank): string { - $rep = $this->getRepetition($piggyBank); - if (null === $rep) { - return '0'; + $sum = '0'; + foreach ($piggyBank->accounts as $account) { + $amount = (string) $account->pivot->current_amount; + $amount = '' === $amount ? '0' : $amount; + $sum = bcadd($sum, $amount); } - - return $rep->current_amount; + return $sum; } public function getRepetition(PiggyBank $piggyBank): ?PiggyBankRepetition { + throw new FireflyException('[b] Piggy bank repetitions are EOL.'); return $piggyBank->piggyBankRepetitions()->first(); } @@ -140,17 +142,18 @@ class PiggyBankRepository implements PiggyBankRepositoryInterface */ public function getExactAmount(PiggyBank $piggyBank, PiggyBankRepetition $repetition, TransactionJournal $journal): string { + throw new FireflyException('[c] Piggy bank repetitions are EOL.'); app('log')->debug(sprintf('Now in getExactAmount(%d, %d, %d)', $piggyBank->id, $repetition->id, $journal->id)); - $operator = null; - $currency = null; + $operator = null; + $currency = null; /** @var JournalRepositoryInterface $journalRepost */ - $journalRepost = app(JournalRepositoryInterface::class); + $journalRepost = app(JournalRepositoryInterface::class); $journalRepost->setUser($this->user); /** @var AccountRepositoryInterface $accountRepos */ - $accountRepos = app(AccountRepositoryInterface::class); + $accountRepos = app(AccountRepositoryInterface::class); $accountRepos->setUser($this->user); $defaultCurrency = app('amount')->getDefaultCurrencyByUserGroup($this->user->userGroup); @@ -159,10 +162,10 @@ class PiggyBankRepository implements PiggyBankRepositoryInterface app('log')->debug(sprintf('Piggy bank #%d currency is %s', $piggyBank->id, $piggyBankCurrency->code)); /** @var Transaction $source */ - $source = $journal->transactions()->with(['account'])->where('amount', '<', 0)->first(); + $source = $journal->transactions()->with(['account'])->where('amount', '<', 0)->first(); /** @var Transaction $destination */ - $destination = $journal->transactions()->with(['account'])->where('amount', '>', 0)->first(); + $destination = $journal->transactions()->with(['account'])->where('amount', '>', 0)->first(); // matches source, which means amount will be removed from piggy: if ($source->account_id === $piggyBank->account_id) { @@ -184,12 +187,12 @@ class PiggyBankRepository implements PiggyBankRepositoryInterface } // currency of the account + the piggy bank currency are almost the same. // which amount from the transaction matches? - $amount = null; - if ((int)$source->transaction_currency_id === $currency->id) { + $amount = null; + if ((int) $source->transaction_currency_id === $currency->id) { app('log')->debug('Use normal amount'); $amount = app('steam')->{$operator}($source->amount); // @phpstan-ignore-line } - if ((int)$source->foreign_currency_id === $currency->id) { + if ((int) $source->foreign_currency_id === $currency->id) { app('log')->debug('Use foreign amount'); $amount = app('steam')->{$operator}($source->foreign_amount); // @phpstan-ignore-line } @@ -200,8 +203,8 @@ class PiggyBankRepository implements PiggyBankRepositoryInterface } app('log')->debug(sprintf('The currency is %s and the amount is %s', $currency->code, $amount)); - $room = bcsub($piggyBank->target_amount, $repetition->current_amount); - $compare = bcmul($repetition->current_amount, '-1'); + $room = bcsub($piggyBank->target_amount, $repetition->current_amount); + $compare = bcmul($repetition->current_amount, '-1'); if (0 === bccomp($piggyBank->target_amount, '0')) { // amount is zero? then the "room" is positive amount of we wish to add or remove. @@ -230,10 +233,10 @@ class PiggyBankRepository implements PiggyBankRepositoryInterface return $compare; } - return (string)$amount; + return (string) $amount; } - public function setUser(null|Authenticatable|User $user): void + public function setUser(null | Authenticatable | User $user): void { if ($user instanceof User) { $this->user = $user; @@ -249,7 +252,7 @@ class PiggyBankRepository implements PiggyBankRepositoryInterface /** @var null|Note $note */ $note = $piggyBank->notes()->first(); - return (string)$note?->text; + return (string) $note?->text; } /** @@ -259,12 +262,12 @@ class PiggyBankRepository implements PiggyBankRepositoryInterface { $currency = app('amount')->getDefaultCurrency(); - $set = $this->getPiggyBanks(); + $set = $this->getPiggyBanks(); /** @var PiggyBank $piggy */ foreach ($set as $piggy) { $currentAmount = $this->getRepetition($piggy)->current_amount ?? '0'; - $piggy->name = $piggy->name.' ('.app('amount')->formatAnything($currency, $currentAmount, false).')'; + $piggy->name = $piggy->name . ' (' . app('amount')->formatAnything($currency, $currentAmount, false) . ')'; } return $set; @@ -272,16 +275,17 @@ class PiggyBankRepository implements PiggyBankRepositoryInterface public function getPiggyBanks(): Collection { - return $this->user // @phpstan-ignore-line (phpstan does not recognize objectGroups) - ->piggyBanks() + return PiggyBank + ::leftJoin('account_piggy_bank', 'account_piggy_bank.piggy_bank_id', '=', 'piggy_banks.id') + ->leftJoin('accounts', 'accounts.id', '=', 'account_piggy_bank.account_id') + ->where('accounts.user_id', auth()->user()->id) ->with( [ 'account', 'objectGroups', ] ) - ->orderBy('order', 'ASC')->get() - ; + ->orderBy('piggy_banks.order', 'ASC')->get(); } /** @@ -289,20 +293,17 @@ class PiggyBankRepository implements PiggyBankRepositoryInterface */ public function getSuggestedMonthlyAmount(PiggyBank $piggyBank): string { - $savePerMonth = '0'; - $repetition = $this->getRepetition($piggyBank); - if (null === $repetition) { - return $savePerMonth; - } - if (null !== $piggyBank->target_date && $repetition->current_amount < $piggyBank->target_amount) { + $savePerMonth = '0'; + $currentAmount = $this->getCurrentAmount($piggyBank); + if (null !== $piggyBank->target_date && $currentAmount < $piggyBank->target_amount) { $now = today(config('app.timezone')); $startDate = null !== $piggyBank->start_date && $piggyBank->start_date->gte($now) ? $piggyBank->start_date : $now; - $diffInMonths = (int)$startDate->diffInMonths($piggyBank->target_date); - $remainingAmount = bcsub($piggyBank->target_amount, $repetition->current_amount); + $diffInMonths = (int) $startDate->diffInMonths($piggyBank->target_date); + $remainingAmount = bcsub($piggyBank->target_amount, $currentAmount); // more than 1 month to go and still need money to save: if ($diffInMonths > 0 && 1 === bccomp($remainingAmount, '0')) { - $savePerMonth = bcdiv($remainingAmount, (string)$diffInMonths); + $savePerMonth = bcdiv($remainingAmount, (string) $diffInMonths); } // less than 1 month to go but still need money to save: @@ -342,8 +343,7 @@ class PiggyBankRepository implements PiggyBankRepositoryInterface $search->whereLike('piggy_banks.name', sprintf('%%%s%%', $query)); } $search->orderBy('piggy_banks.order', 'ASC') - ->orderBy('piggy_banks.name', 'ASC') - ; + ->orderBy('piggy_banks.name', 'ASC'); return $search->take($limit)->get(); } diff --git a/app/Transformers/PiggyBankTransformer.php b/app/Transformers/PiggyBankTransformer.php index 4cc10cc466..701c816257 100644 --- a/app/Transformers/PiggyBankTransformer.php +++ b/app/Transformers/PiggyBankTransformer.php @@ -54,25 +54,22 @@ class PiggyBankTransformer extends AbstractTransformer */ public function transform(PiggyBank $piggyBank): array { - $account = $piggyBank->account; + $user = $piggyBank->accounts()->first()->user; // set up repositories - $this->accountRepos->setUser($account->user); - $this->piggyRepos->setUser($account->user); - - // get currency from account, or use default. - $currency = $this->accountRepos->getAccountCurrency($account) ?? app('amount')->getDefaultCurrencyByUserGroup($account->user->userGroup); + $this->accountRepos->setUser($user); + $this->piggyRepos->setUser($user); // note - $notes = $this->piggyRepos->getNoteText($piggyBank); - $notes = '' === $notes ? null : $notes; + $notes = $this->piggyRepos->getNoteText($piggyBank); + $notes = '' === $notes ? null : $notes; $objectGroupId = null; $objectGroupOrder = null; $objectGroupTitle = null; /** @var null|ObjectGroup $objectGroup */ - $objectGroup = $piggyBank->objectGroups->first(); + $objectGroup = $piggyBank->objectGroups->first(); if (null !== $objectGroup) { $objectGroupId = $objectGroup->id; $objectGroupOrder = $objectGroup->order; @@ -80,31 +77,33 @@ class PiggyBankTransformer extends AbstractTransformer } // get currently saved amount: - $currentAmount = app('steam')->bcround($this->piggyRepos->getCurrentAmount($piggyBank), $currency->decimal_places); + $currency = $piggyBank->transactionCurrency; + $currentAmount = app('steam')->bcround($this->piggyRepos->getCurrentAmount($piggyBank), $currency->decimal_places); // Amounts, depending on 0.0 state of target amount - $percentage = null; - $targetAmount = $piggyBank->target_amount; - $leftToSave = null; - $savePerMonth = null; + $percentage = null; + $targetAmount = $piggyBank->target_amount; + $leftToSave = null; + $savePerMonth = null; if (0 !== bccomp($targetAmount, '0')) { // target amount is not 0.00 $leftToSave = bcsub($piggyBank->target_amount, $currentAmount); - $percentage = (int)bcmul(bcdiv($currentAmount, $targetAmount), '100'); + $percentage = (int) bcmul(bcdiv($currentAmount, $targetAmount), '100'); $targetAmount = app('steam')->bcround($targetAmount, $currency->decimal_places); $leftToSave = app('steam')->bcround($leftToSave, $currency->decimal_places); $savePerMonth = app('steam')->bcround($this->piggyRepos->getSuggestedMonthlyAmount($piggyBank), $currency->decimal_places); } - $startDate = $piggyBank->start_date?->format('Y-m-d'); - $targetDate = $piggyBank->target_date?->format('Y-m-d'); + $startDate = $piggyBank->start_date?->format('Y-m-d'); + $targetDate = $piggyBank->target_date?->format('Y-m-d'); return [ - 'id' => (string)$piggyBank->id, + 'id' => (string) $piggyBank->id, 'created_at' => $piggyBank->created_at->toAtomString(), 'updated_at' => $piggyBank->updated_at->toAtomString(), - 'account_id' => (string)$piggyBank->account_id, - 'account_name' => $piggyBank->account->name, + 'accounts' => $this->renderAccounts($piggyBank), + //'account_id' => (string)$piggyBank->account_id, + //'account_name' => $piggyBank->account->name, 'name' => $piggyBank->name, - 'currency_id' => (string)$currency->id, + 'currency_id' => (string) $currency->id, 'currency_code' => $currency->code, 'currency_symbol' => $currency->symbol, 'currency_decimal_places' => $currency->decimal_places, @@ -118,15 +117,28 @@ class PiggyBankTransformer extends AbstractTransformer 'order' => $piggyBank->order, 'active' => true, 'notes' => $notes, - 'object_group_id' => null !== $objectGroupId ? (string)$objectGroupId : null, + 'object_group_id' => null !== $objectGroupId ? (string) $objectGroupId : null, 'object_group_order' => $objectGroupOrder, 'object_group_title' => $objectGroupTitle, 'links' => [ [ 'rel' => 'self', - 'uri' => '/piggy_banks/'.$piggyBank->id, + 'uri' => '/piggy_banks/' . $piggyBank->id, ], ], ]; } + + private function renderAccounts(PiggyBank $piggyBank): array + { + $return = []; + foreach ($piggyBank->accounts as $account) { + $return[] = [ + 'id' => $account->id, + 'name' => $account->name, + // TODO add balance, add left to save. + ]; + } + return $return; + } } diff --git a/app/Transformers/V2/PiggyBankTransformer.php b/app/Transformers/V2/PiggyBankTransformer.php index 30ebe367a7..8dfb88b69c 100644 --- a/app/Transformers/V2/PiggyBankTransformer.php +++ b/app/Transformers/V2/PiggyBankTransformer.php @@ -115,6 +115,7 @@ class PiggyBankTransformer extends AbstractTransformer // grab repetitions (for current amount): $repetitions = PiggyBankRepetition::whereIn('piggy_bank_id', $piggyBanks)->get(); + throw new FireflyException('[d] Piggy bank repetitions are EOL.'); /** @var PiggyBankRepetition $repetition */ foreach ($repetitions as $repetition) { diff --git a/app/User.php b/app/User.php index deb0b896ab..3e44cb1e43 100644 --- a/app/User.php +++ b/app/User.php @@ -332,14 +332,6 @@ class User extends Authenticatable return $this->hasMany(ObjectGroup::class); } - /** - * Link to piggy banks. - */ - public function piggyBanks(): HasManyThrough - { - return $this->hasManyThrough(PiggyBank::class, Account::class); - } - /** * Link to preferences. */