API works for multi-account piggies, the rest throws an exception

This commit is contained in:
James Cole
2024-12-01 18:32:05 +01:00
parent cdf1ebf3f7
commit d740814f88
8 changed files with 82 additions and 71 deletions

View File

@@ -72,7 +72,7 @@ class ShowController extends Controller
// types to get, page size: // types to get, page size:
$pageSize = $this->parameters->get('limit'); $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(); $collection = $this->repository->getPiggyBanks();
$count = $collection->count(); $count = $collection->count();
$piggyBanks = $collection->slice(($this->parameters->get('page') - 1) * $pageSize, $pageSize); $piggyBanks = $collection->slice(($this->parameters->get('page') - 1) * $pageSize, $pageSize);

View File

@@ -67,8 +67,8 @@ class AddTimezonesToDates extends Command
CurrencyExchangeRate::class => ['date'], // done CurrencyExchangeRate::class => ['date'], // done
InvitedUser::class => ['expires'], InvitedUser::class => ['expires'],
PiggyBankEvent::class => ['date'], PiggyBankEvent::class => ['date'],
PiggyBankRepetition::class => ['startdate', 'targetdate'], PiggyBankRepetition::class => ['start_date', 'target_date'],
PiggyBank::class => ['startdate', 'targetdate'], // done PiggyBank::class => ['start_date', 'target_date'], // done
Recurrence::class => ['first_date', 'repeat_until', 'latest_date'], Recurrence::class => ['first_date', 'repeat_until', 'latest_date'],
Tag::class => ['date'], Tag::class => ['date'],
TransactionJournal::class => ['date'], TransactionJournal::class => ['date'],

View File

@@ -78,6 +78,11 @@ class PiggyBank extends Model
throw new NotFoundHttpException(); throw new NotFoundHttpException();
} }
public function transactionCurrency(): BelongsTo
{
return $this->belongsTo(TransactionCurrency::class);
}
public function account(): BelongsTo public function account(): BelongsTo
{ {
return $this->belongsTo(Account::class); return $this->belongsTo(Account::class);

View File

@@ -45,6 +45,7 @@ trait ModifiesPiggyBanks
public function addAmountToRepetition(PiggyBankRepetition $repetition, string $amount, TransactionJournal $journal): void 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)); app('log')->debug(sprintf('addAmountToRepetition: %s', $amount));
if (-1 === bccomp($amount, '0')) { if (-1 === bccomp($amount, '0')) {
app('log')->debug('Remove amount.'); app('log')->debug('Remove amount.');

View File

@@ -94,7 +94,7 @@ class PiggyBankRepository implements PiggyBankRepositoryInterface
public function getAttachments(PiggyBank $piggyBank): Collection public function getAttachments(PiggyBank $piggyBank): Collection
{ {
$set = $piggyBank->attachments()->get(); $set = $piggyBank->attachments()->get();
/** @var \Storage $disk */ /** @var \Storage $disk */
$disk = \Storage::disk('upload'); $disk = \Storage::disk('upload');
@@ -115,16 +115,18 @@ class PiggyBankRepository implements PiggyBankRepositoryInterface
*/ */
public function getCurrentAmount(PiggyBank $piggyBank): string public function getCurrentAmount(PiggyBank $piggyBank): string
{ {
$rep = $this->getRepetition($piggyBank); $sum = '0';
if (null === $rep) { foreach ($piggyBank->accounts as $account) {
return '0'; $amount = (string) $account->pivot->current_amount;
$amount = '' === $amount ? '0' : $amount;
$sum = bcadd($sum, $amount);
} }
return $sum;
return $rep->current_amount;
} }
public function getRepetition(PiggyBank $piggyBank): ?PiggyBankRepetition public function getRepetition(PiggyBank $piggyBank): ?PiggyBankRepetition
{ {
throw new FireflyException('[b] Piggy bank repetitions are EOL.');
return $piggyBank->piggyBankRepetitions()->first(); return $piggyBank->piggyBankRepetitions()->first();
} }
@@ -140,17 +142,18 @@ class PiggyBankRepository implements PiggyBankRepositoryInterface
*/ */
public function getExactAmount(PiggyBank $piggyBank, PiggyBankRepetition $repetition, TransactionJournal $journal): string 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)); app('log')->debug(sprintf('Now in getExactAmount(%d, %d, %d)', $piggyBank->id, $repetition->id, $journal->id));
$operator = null; $operator = null;
$currency = null; $currency = null;
/** @var JournalRepositoryInterface $journalRepost */ /** @var JournalRepositoryInterface $journalRepost */
$journalRepost = app(JournalRepositoryInterface::class); $journalRepost = app(JournalRepositoryInterface::class);
$journalRepost->setUser($this->user); $journalRepost->setUser($this->user);
/** @var AccountRepositoryInterface $accountRepos */ /** @var AccountRepositoryInterface $accountRepos */
$accountRepos = app(AccountRepositoryInterface::class); $accountRepos = app(AccountRepositoryInterface::class);
$accountRepos->setUser($this->user); $accountRepos->setUser($this->user);
$defaultCurrency = app('amount')->getDefaultCurrencyByUserGroup($this->user->userGroup); $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)); app('log')->debug(sprintf('Piggy bank #%d currency is %s', $piggyBank->id, $piggyBankCurrency->code));
/** @var Transaction $source */ /** @var Transaction $source */
$source = $journal->transactions()->with(['account'])->where('amount', '<', 0)->first(); $source = $journal->transactions()->with(['account'])->where('amount', '<', 0)->first();
/** @var Transaction $destination */ /** @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: // matches source, which means amount will be removed from piggy:
if ($source->account_id === $piggyBank->account_id) { 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. // currency of the account + the piggy bank currency are almost the same.
// which amount from the transaction matches? // which amount from the transaction matches?
$amount = null; $amount = null;
if ((int)$source->transaction_currency_id === $currency->id) { if ((int) $source->transaction_currency_id === $currency->id) {
app('log')->debug('Use normal amount'); app('log')->debug('Use normal amount');
$amount = app('steam')->{$operator}($source->amount); // @phpstan-ignore-line $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'); app('log')->debug('Use foreign amount');
$amount = app('steam')->{$operator}($source->foreign_amount); // @phpstan-ignore-line $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)); app('log')->debug(sprintf('The currency is %s and the amount is %s', $currency->code, $amount));
$room = bcsub($piggyBank->target_amount, $repetition->current_amount); $room = bcsub($piggyBank->target_amount, $repetition->current_amount);
$compare = bcmul($repetition->current_amount, '-1'); $compare = bcmul($repetition->current_amount, '-1');
if (0 === bccomp($piggyBank->target_amount, '0')) { if (0 === bccomp($piggyBank->target_amount, '0')) {
// amount is zero? then the "room" is positive amount of we wish to add or remove. // 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 $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) { if ($user instanceof User) {
$this->user = $user; $this->user = $user;
@@ -249,7 +252,7 @@ class PiggyBankRepository implements PiggyBankRepositoryInterface
/** @var null|Note $note */ /** @var null|Note $note */
$note = $piggyBank->notes()->first(); $note = $piggyBank->notes()->first();
return (string)$note?->text; return (string) $note?->text;
} }
/** /**
@@ -259,12 +262,12 @@ class PiggyBankRepository implements PiggyBankRepositoryInterface
{ {
$currency = app('amount')->getDefaultCurrency(); $currency = app('amount')->getDefaultCurrency();
$set = $this->getPiggyBanks(); $set = $this->getPiggyBanks();
/** @var PiggyBank $piggy */ /** @var PiggyBank $piggy */
foreach ($set as $piggy) { foreach ($set as $piggy) {
$currentAmount = $this->getRepetition($piggy)->current_amount ?? '0'; $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; return $set;
@@ -272,16 +275,17 @@ class PiggyBankRepository implements PiggyBankRepositoryInterface
public function getPiggyBanks(): Collection public function getPiggyBanks(): Collection
{ {
return $this->user // @phpstan-ignore-line (phpstan does not recognize objectGroups) return PiggyBank
->piggyBanks() ::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( ->with(
[ [
'account', 'account',
'objectGroups', '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 public function getSuggestedMonthlyAmount(PiggyBank $piggyBank): string
{ {
$savePerMonth = '0'; $savePerMonth = '0';
$repetition = $this->getRepetition($piggyBank); $currentAmount = $this->getCurrentAmount($piggyBank);
if (null === $repetition) { if (null !== $piggyBank->target_date && $currentAmount < $piggyBank->target_amount) {
return $savePerMonth;
}
if (null !== $piggyBank->target_date && $repetition->current_amount < $piggyBank->target_amount) {
$now = today(config('app.timezone')); $now = today(config('app.timezone'));
$startDate = null !== $piggyBank->start_date && $piggyBank->start_date->gte($now) ? $piggyBank->start_date : $now; $startDate = null !== $piggyBank->start_date && $piggyBank->start_date->gte($now) ? $piggyBank->start_date : $now;
$diffInMonths = (int)$startDate->diffInMonths($piggyBank->target_date); $diffInMonths = (int) $startDate->diffInMonths($piggyBank->target_date);
$remainingAmount = bcsub($piggyBank->target_amount, $repetition->current_amount); $remainingAmount = bcsub($piggyBank->target_amount, $currentAmount);
// more than 1 month to go and still need money to save: // more than 1 month to go and still need money to save:
if ($diffInMonths > 0 && 1 === bccomp($remainingAmount, '0')) { 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: // 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->whereLike('piggy_banks.name', sprintf('%%%s%%', $query));
} }
$search->orderBy('piggy_banks.order', 'ASC') $search->orderBy('piggy_banks.order', 'ASC')
->orderBy('piggy_banks.name', 'ASC') ->orderBy('piggy_banks.name', 'ASC');
;
return $search->take($limit)->get(); return $search->take($limit)->get();
} }

View File

@@ -54,25 +54,22 @@ class PiggyBankTransformer extends AbstractTransformer
*/ */
public function transform(PiggyBank $piggyBank): array public function transform(PiggyBank $piggyBank): array
{ {
$account = $piggyBank->account; $user = $piggyBank->accounts()->first()->user;
// set up repositories // set up repositories
$this->accountRepos->setUser($account->user); $this->accountRepos->setUser($user);
$this->piggyRepos->setUser($account->user); $this->piggyRepos->setUser($user);
// get currency from account, or use default.
$currency = $this->accountRepos->getAccountCurrency($account) ?? app('amount')->getDefaultCurrencyByUserGroup($account->user->userGroup);
// note // note
$notes = $this->piggyRepos->getNoteText($piggyBank); $notes = $this->piggyRepos->getNoteText($piggyBank);
$notes = '' === $notes ? null : $notes; $notes = '' === $notes ? null : $notes;
$objectGroupId = null; $objectGroupId = null;
$objectGroupOrder = null; $objectGroupOrder = null;
$objectGroupTitle = null; $objectGroupTitle = null;
/** @var null|ObjectGroup $objectGroup */ /** @var null|ObjectGroup $objectGroup */
$objectGroup = $piggyBank->objectGroups->first(); $objectGroup = $piggyBank->objectGroups->first();
if (null !== $objectGroup) { if (null !== $objectGroup) {
$objectGroupId = $objectGroup->id; $objectGroupId = $objectGroup->id;
$objectGroupOrder = $objectGroup->order; $objectGroupOrder = $objectGroup->order;
@@ -80,31 +77,33 @@ class PiggyBankTransformer extends AbstractTransformer
} }
// get currently saved amount: // 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 // Amounts, depending on 0.0 state of target amount
$percentage = null; $percentage = null;
$targetAmount = $piggyBank->target_amount; $targetAmount = $piggyBank->target_amount;
$leftToSave = null; $leftToSave = null;
$savePerMonth = null; $savePerMonth = null;
if (0 !== bccomp($targetAmount, '0')) { // target amount is not 0.00 if (0 !== bccomp($targetAmount, '0')) { // target amount is not 0.00
$leftToSave = bcsub($piggyBank->target_amount, $currentAmount); $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); $targetAmount = app('steam')->bcround($targetAmount, $currency->decimal_places);
$leftToSave = app('steam')->bcround($leftToSave, $currency->decimal_places); $leftToSave = app('steam')->bcround($leftToSave, $currency->decimal_places);
$savePerMonth = app('steam')->bcround($this->piggyRepos->getSuggestedMonthlyAmount($piggyBank), $currency->decimal_places); $savePerMonth = app('steam')->bcround($this->piggyRepos->getSuggestedMonthlyAmount($piggyBank), $currency->decimal_places);
} }
$startDate = $piggyBank->start_date?->format('Y-m-d'); $startDate = $piggyBank->start_date?->format('Y-m-d');
$targetDate = $piggyBank->target_date?->format('Y-m-d'); $targetDate = $piggyBank->target_date?->format('Y-m-d');
return [ return [
'id' => (string)$piggyBank->id, 'id' => (string) $piggyBank->id,
'created_at' => $piggyBank->created_at->toAtomString(), 'created_at' => $piggyBank->created_at->toAtomString(),
'updated_at' => $piggyBank->updated_at->toAtomString(), 'updated_at' => $piggyBank->updated_at->toAtomString(),
'account_id' => (string)$piggyBank->account_id, 'accounts' => $this->renderAccounts($piggyBank),
'account_name' => $piggyBank->account->name, //'account_id' => (string)$piggyBank->account_id,
//'account_name' => $piggyBank->account->name,
'name' => $piggyBank->name, 'name' => $piggyBank->name,
'currency_id' => (string)$currency->id, 'currency_id' => (string) $currency->id,
'currency_code' => $currency->code, 'currency_code' => $currency->code,
'currency_symbol' => $currency->symbol, 'currency_symbol' => $currency->symbol,
'currency_decimal_places' => $currency->decimal_places, 'currency_decimal_places' => $currency->decimal_places,
@@ -118,15 +117,28 @@ class PiggyBankTransformer extends AbstractTransformer
'order' => $piggyBank->order, 'order' => $piggyBank->order,
'active' => true, 'active' => true,
'notes' => $notes, 'notes' => $notes,
'object_group_id' => null !== $objectGroupId ? (string)$objectGroupId : null, 'object_group_id' => null !== $objectGroupId ? (string) $objectGroupId : null,
'object_group_order' => $objectGroupOrder, 'object_group_order' => $objectGroupOrder,
'object_group_title' => $objectGroupTitle, 'object_group_title' => $objectGroupTitle,
'links' => [ 'links' => [
[ [
'rel' => 'self', '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;
}
} }

View File

@@ -115,6 +115,7 @@ class PiggyBankTransformer extends AbstractTransformer
// grab repetitions (for current amount): // grab repetitions (for current amount):
$repetitions = PiggyBankRepetition::whereIn('piggy_bank_id', $piggyBanks)->get(); $repetitions = PiggyBankRepetition::whereIn('piggy_bank_id', $piggyBanks)->get();
throw new FireflyException('[d] Piggy bank repetitions are EOL.');
/** @var PiggyBankRepetition $repetition */ /** @var PiggyBankRepetition $repetition */
foreach ($repetitions as $repetition) { foreach ($repetitions as $repetition) {

View File

@@ -332,14 +332,6 @@ class User extends Authenticatable
return $this->hasMany(ObjectGroup::class); return $this->hasMany(ObjectGroup::class);
} }
/**
* Link to piggy banks.
*/
public function piggyBanks(): HasManyThrough
{
return $this->hasManyThrough(PiggyBank::class, Account::class);
}
/** /**
* Link to preferences. * Link to preferences.
*/ */