Multi account piggy banks.

This commit is contained in:
James Cole 2024-12-14 17:32:03 +01:00
parent fb6c67fa04
commit 6a62f781e9
No known key found for this signature in database
GPG Key ID: B49A324B7EAD6D80
24 changed files with 572 additions and 404 deletions

View File

@ -72,6 +72,7 @@ class UpgradeDatabase extends Command
'firefly-iii:create-group-memberships',
'firefly-iii:upgrade-group-information',
'firefly-iii:upgrade-currency-preferences',
'firefly-iii:upgrade-multi-piggies',
'firefly-iii:correct-database',
];
$args = [];

View File

@ -93,7 +93,7 @@ class UpgradeMultiPiggyBanks extends Command
{
$this->repository->setUser($piggyBank->account->user);
$this->accountRepository->setUser($piggyBank->account->user);
$repetition = $this->repository->getRepetition($piggyBank);
$repetition = $this->repository->getRepetition($piggyBank, true);
$currency = $this->accountRepository->getAccountCurrency($piggyBank->account) ?? app('amount')->getDefaultCurrencyByUserGroup($piggyBank->account->user->userGroup);
// update piggy bank to have a currency.

View File

@ -42,12 +42,22 @@ class PiggyBankFactory
public User $user {
set(User $value) {
$this->user = $value;
$this->currencyRepository->setUser($value);
$this->accountRepository->setUser($value);
$this->piggyBankRepository->setUser($value);
}
}
private CurrencyRepositoryInterface $currencyRepository;
private AccountRepositoryInterface $accountRepository;
private PiggyBankRepositoryInterface $piggyBankRepository;
public function __construct()
{
$this->currencyRepository = app(CurrencyRepositoryInterface::class);
$this->accountRepository = app(AccountRepositoryInterface::class);
$this->piggyBankRepository = app(PiggyBankRepositoryInterface::class);
}
/**
* Store a piggy bank or come back with an exception.
*
@ -56,12 +66,7 @@ class PiggyBankFactory
* @return PiggyBank
*/
public function store(array $data): PiggyBank {
$this->currencyRepository = app(CurrencyRepositoryInterface::class);
$this->accountRepository = app(AccountRepositoryInterface::class);
$this->piggyBankRepository = app(PiggyBankRepositoryInterface::class);
$this->currencyRepository->setUser($this->user);
$this->accountRepository->setUser($this->user);
$this->piggyBankRepository->setUser($this->user);
$piggyBankData =$data;
// unset some fields
@ -202,14 +207,23 @@ class PiggyBankFactory
}
private function linkToAccountIds(PiggyBank $piggyBank, array $accounts): void {
public function linkToAccountIds(PiggyBank $piggyBank, array $accounts): void {
$toBeLinked = [];
/** @var array $info */
foreach($accounts as $info) {
$account = $this->accountRepository->find((int)($info['account_id'] ?? 0));
if(null === $account) {
continue;
}
$piggyBank->accounts()->syncWithoutDetaching([$account->id => ['current_amount' => $info['current_amount'] ?? '0']]);
if(array_key_exists('current_amount',$info)) {
$toBeLinked[$account->id] = ['current_amount' => $info['current_amount']];
//$piggyBank->accounts()->syncWithoutDetaching([$account->id => ['current_amount' => $info['current_amount'] ?? '0']]);
}
if(!array_key_exists('current_amount', $info)) {
$toBeLinked[$account->id] = [];
//$piggyBank->accounts()->syncWithoutDetaching([$account->id]);
}
}
$piggyBank->accounts()->sync($toBeLinked);
}
}

View File

@ -26,12 +26,14 @@ namespace FireflyIII\Http\Controllers\PiggyBank;
use Carbon\Carbon;
use FireflyIII\Http\Controllers\Controller;
use FireflyIII\Models\Account;
use FireflyIII\Models\PiggyBank;
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
use FireflyIII\Repositories\PiggyBank\PiggyBankRepositoryInterface;
use Illuminate\Contracts\View\Factory;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Log;
use Illuminate\View\View;
/**
@ -51,7 +53,7 @@ class AmountController extends Controller
$this->middleware(
function ($request, $next) {
app('view')->share('title', (string)trans('firefly.piggyBanks'));
app('view')->share('title', (string) trans('firefly.piggyBanks'));
app('view')->share('mainTitleIcon', 'fa-bullseye');
$this->piggyRepos = app(PiggyBankRepositoryInterface::class);
@ -69,16 +71,26 @@ class AmountController extends Controller
*/
public function add(PiggyBank $piggyBank)
{
$leftOnAccount = $this->piggyRepos->leftOnAccount($piggyBank, today(config('app.timezone')));
$savedSoFar = $this->piggyRepos->getCurrentAmount($piggyBank);
$maxAmount = $leftOnAccount;
if (0 !== bccomp($piggyBank->target_amount, '0')) {
$leftToSave = bcsub($piggyBank->target_amount, $savedSoFar);
$maxAmount = min($leftOnAccount, $leftToSave);
$accounts = [];
$total = '0';
$totalSaved = $this->piggyRepos->getCurrentAmount($piggyBank);
$leftToSave = bcsub($piggyBank->target_amount, $totalSaved);
foreach ($piggyBank->accounts as $account) {
$leftOnAccount = $this->piggyRepos->leftOnAccount($piggyBank, $account, today(config('app.timezone'))->endOfDay());
$savedSoFar = $this->piggyRepos->getCurrentAmount($piggyBank, $account);
$maxAmount = 0 === bccomp($piggyBank->target_amount, '0') ? $leftToSave : min($leftOnAccount, $leftToSave);
$accounts[] = [
'account' => $account,
'left_on_account' => $leftOnAccount,
'saved_so_far' => $savedSoFar,
'left_to_save' => $leftToSave,
'max_amount' => $maxAmount,
];
$total = bcadd($total, $leftOnAccount);
}
$currency = $this->accountRepos->getAccountCurrency($piggyBank->account) ?? app('amount')->getDefaultCurrency();
$total = (float) $total; // intentional float.
return view('piggy-banks.add', compact('piggyBank', 'maxAmount', 'currency'));
return view('piggy-banks.add', compact('piggyBank', 'accounts', 'total'));
}
/**
@ -89,18 +101,24 @@ class AmountController extends Controller
public function addMobile(PiggyBank $piggyBank)
{
/** @var Carbon $date */
$date = session('end', today(config('app.timezone')));
$leftOnAccount = $this->piggyRepos->leftOnAccount($piggyBank, $date);
$savedSoFar = $this->piggyRepos->getCurrentAmount($piggyBank);
$maxAmount = $leftOnAccount;
if (0 !== bccomp($piggyBank->target_amount, '0')) {
$leftToSave = bcsub($piggyBank->target_amount, $savedSoFar);
$maxAmount = min($leftOnAccount, $leftToSave);
$date = session('end', today(config('app.timezone')));
$accounts = [];
$total = '0';
foreach ($piggyBank->accounts as $account) {
$leftOnAccount = $this->piggyRepos->leftOnAccount($piggyBank, $account, $date);
$savedSoFar = $this->piggyRepos->getCurrentAmount($piggyBank, $account);
$leftToSave = bcsub($piggyBank->target_amount, $savedSoFar);
$accounts[] = [
'account' => $account,
'left_on_account' => $leftOnAccount,
'saved_so_far' => $savedSoFar,
'left_to_save' => $leftToSave,
'max_amount' => 0 === bccomp($piggyBank->target_amount, '0') ? $leftOnAccount : min($leftOnAccount, $leftToSave),
];
$total = bcadd($total, $leftOnAccount);
}
$currency = $this->accountRepos->getAccountCurrency($piggyBank->account) ?? app('amount')->getDefaultCurrency();
return view('piggy-banks.add-mobile', compact('piggyBank', 'maxAmount', 'currency'));
return view('piggy-banks.add-mobile', compact('piggyBank', 'total', 'accounts'));
}
/**
@ -108,32 +126,47 @@ class AmountController extends Controller
*/
public function postAdd(Request $request, PiggyBank $piggyBank): RedirectResponse
{
$amount = $request->get('amount') ?? '0';
$currency = $this->accountRepos->getAccountCurrency($piggyBank->account) ?? app('amount')->getDefaultCurrency();
// if amount is negative, make positive and continue:
if (-1 === bccomp($amount, '0')) {
$amount = bcmul($amount, '-1');
$data = $request->all();
$amounts = $data['amount'] ?? [];
$total = '0';
Log::debug('Start with loop.');
/** @var Account $account */
foreach ($piggyBank->accounts as $account) {
$amount = (string) ($amounts[$account->id] ?? '0');
if ('' === $amount || 0 === bccomp($amount, '0')) {
continue;
}
if (-1 === bccomp($amount, '0')) {
$amount = bcmul($amount, '-1');
}
// small check to see if the $amount is not more than the total "left to save" value
$currentAmount = $this->piggyRepos->getCurrentAmount($piggyBank);
$leftToSave = 0 === bccomp($piggyBank->target_amount, '0') ? '0' : bcsub($piggyBank->target_amount, $currentAmount);
if (bccomp($amount, $leftToSave) > 0 && 0 !== bccomp($leftToSave, '0')) {
Log::debug(sprintf('Amount "%s" is more than left to save "%s". Using left to save.', $amount, $leftToSave));
$amount = $leftToSave;
}
$canAddAmount = $this->piggyRepos->canAddAmount($piggyBank, $account, $amount);
if ($canAddAmount) {
$this->piggyRepos->addAmount($piggyBank, $account, $amount);
$total = bcadd($total, $amount);
}
$piggyBank->refresh();
}
if ($this->piggyRepos->canAddAmount($piggyBank, $amount)) {
$this->piggyRepos->addAmount($piggyBank, $amount);
session()->flash(
'success',
(string)trans(
'firefly.added_amount_to_piggy',
['amount' => app('amount')->formatAnything($currency, $amount, false), 'name' => $piggyBank->name]
)
);
if (0 !== bccomp($total, '0')) {
session()->flash('success', (string) trans('firefly.added_amount_to_piggy', ['amount' => app('amount')->formatAnything($piggyBank->transactionCurrency, $total, false), 'name' => $piggyBank->name]));
app('preferences')->mark();
return redirect(route('piggy-banks.index'));
}
app('log')->error('Cannot add '.$amount.' because canAddAmount returned false.');
app('log')->error(sprintf('Cannot add %s because canAddAmount returned false.', $total));
session()->flash(
'error',
(string)trans(
(string) trans(
'firefly.cannot_add_amount_piggy',
['amount' => app('amount')->formatAnything($currency, $amount, false), 'name' => e($piggyBank->name)]
['amount' => app('amount')->formatAnything($piggyBank->transactionCurrency, $total, false), 'name' => e($piggyBank->name)]
)
);
@ -145,32 +178,43 @@ class AmountController extends Controller
*/
public function postRemove(Request $request, PiggyBank $piggyBank): RedirectResponse
{
$amount = $request->get('amount') ?? '0';
$currency = $this->accountRepos->getAccountCurrency($piggyBank->account) ?? app('amount')->getDefaultCurrency();
// if amount is negative, make positive and continue:
if (-1 === bccomp($amount, '0')) {
$amount = bcmul($amount, '-1');
$amounts = $request->get('amount') ?? [];
if (!is_array($amounts)) {
$amounts = [];
}
if ($this->piggyRepos->canRemoveAmount($piggyBank, $amount)) {
$this->piggyRepos->removeAmount($piggyBank, $amount);
$total = '0';
/** @var Account $account */
foreach ($piggyBank->accounts as $account) {
$amount = (string) ($amounts[$account->id] ?? '0');
if ('' === $amount || 0 === bccomp($amount, '0')) {
continue;
}
if (-1 === bccomp($amount, '0')) {
$amount = bcmul($amount, '-1');
}
if ($this->piggyRepos->canRemoveAmount($piggyBank, $account, $amount)) {
$this->piggyRepos->removeAmount($piggyBank, $account, $amount);
$total = bcadd($total, $amount);
}
}
if (0 !== bccomp($total, '0')) {
session()->flash(
'success',
(string)trans(
(string) trans(
'firefly.removed_amount_from_piggy',
['amount' => app('amount')->formatAnything($currency, $amount, false), 'name' => $piggyBank->name]
['amount' => app('amount')->formatAnything($piggyBank->transactionCurrency, $total, false), 'name' => $piggyBank->name]
)
);
app('preferences')->mark();
return redirect(route('piggy-banks.index'));
}
$amount = (string)$request->get('amount');
session()->flash(
'error',
(string)trans(
(string) trans(
'firefly.cannot_remove_from_piggy',
['amount' => app('amount')->formatAnything($currency, $amount, false), 'name' => e($piggyBank->name)]
['amount' => app('amount')->formatAnything($piggyBank->transactionCurrency, $total, false), 'name' => e($piggyBank->name)]
)
);
@ -184,10 +228,14 @@ class AmountController extends Controller
*/
public function remove(PiggyBank $piggyBank)
{
$repetition = $this->piggyRepos->getRepetition($piggyBank);
$currency = $this->accountRepos->getAccountCurrency($piggyBank->account) ?? app('amount')->getDefaultCurrency();
return view('piggy-banks.remove', compact('piggyBank', 'repetition', 'currency'));
$accounts = [];
foreach ($piggyBank->accounts as $account) {
$accounts[] = [
'account' => $account,
'saved_so_far' => $this->piggyRepos->getCurrentAmount($piggyBank, $account),
];
}
return view('piggy-banks.remove', compact('piggyBank', 'accounts'));
}
/**
@ -197,9 +245,14 @@ class AmountController extends Controller
*/
public function removeMobile(PiggyBank $piggyBank)
{
$repetition = $this->piggyRepos->getRepetition($piggyBank);
$currency = $this->accountRepos->getAccountCurrency($piggyBank->account) ?? app('amount')->getDefaultCurrency();
$accounts = [];
foreach ($piggyBank->accounts as $account) {
$accounts[] = [
'account' => $account,
'saved_so_far' => $this->piggyRepos->getCurrentAmount($piggyBank, $account),
];
}
return view('piggy-banks.remove-mobile', compact('piggyBank', 'repetition', 'currency'));
return view('piggy-banks.remove-mobile', compact('piggyBank', 'accounts'));
}
}

View File

@ -79,22 +79,21 @@ class EditController extends Controller
// Flash some data to fill the form.
$targetDate = $piggyBank->target_date?->format('Y-m-d');
$startDate = $piggyBank->start_date?->format('Y-m-d');
$currency = $this->accountRepository->getAccountCurrency($piggyBank->account);
if (null === $currency) {
$currency = app('amount')->getDefaultCurrency();
}
$preFilled = [
'name' => $piggyBank->name,
'account_id' => $piggyBank->account_id,
'targetamount' => app('steam')->bcround($piggyBank->target_amount, $currency->decimal_places),
'targetdate' => $targetDate,
'startdate' => $startDate,
'target_amount' => app('steam')->bcround($piggyBank->target_amount, $piggyBank->transactionCurrency->decimal_places),
'target_date' => $targetDate,
'start_date' => $startDate,
'accounts' => [],
'object_group' => null !== $piggyBank->objectGroups->first() ? $piggyBank->objectGroups->first()->title : '',
'notes' => null === $note ? '' : $note->text,
];
foreach($piggyBank->accounts as $account) {
$preFilled['accounts'][] = $account->id;
}
if (0 === bccomp($piggyBank->target_amount, '0')) {
$preFilled['targetamount'] = '';
$preFilled['target_amount'] = '';
}
session()->flash('preFilled', $preFilled);

View File

@ -83,6 +83,7 @@ class ShowController extends Controller
$subTitle = $piggyBank->name;
$attachments = $this->piggyRepos->getAttachments($piggyBank);
return view('piggy-banks.show', compact('piggyBank', 'events', 'subTitle', 'piggy', 'attachments'));
}
}

View File

@ -24,6 +24,8 @@ declare(strict_types=1);
namespace FireflyIII\Http\Requests;
use FireflyIII\Models\PiggyBank;
use FireflyIII\Models\TransactionCurrency;
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
use FireflyIII\Rules\IsValidPositiveAmount;
use FireflyIII\Support\Request\ChecksLogin;
use FireflyIII\Support\Request\ConvertsDataTypes;
@ -44,15 +46,22 @@ class PiggyBankUpdateRequest extends FormRequest
*/
public function getPiggyBankData(): array
{
return [
$accounts = $this->get('accounts');
$data = [
'name' => $this->convertString('name'),
'startdate' => $this->getCarbonDate('startdate'),
'account_id' => $this->convertInteger('account_id'),
'targetamount' => trim($this->convertString('targetamount')),
'targetdate' => $this->getCarbonDate('targetdate'),
'start_date' => $this->getCarbonDate('start_date'),
'target_amount' => trim($this->convertString('target_amount')),
'target_date' => $this->getCarbonDate('target_date'),
'notes' => $this->stringWithNewlines('notes'),
'object_group_title' => $this->convertString('object_group'),
];
if (!is_array($accounts)) {
$accounts = [];
}
foreach ($accounts as $item) {
$data['accounts'][] = ['account_id' => (int) $item];
}
return $data;
}
/**
@ -64,21 +73,62 @@ class PiggyBankUpdateRequest extends FormRequest
$piggy = $this->route()->parameter('piggyBank');
return [
'name' => sprintf('required|min:1|max:255|uniquePiggyBankForUser:%d', $piggy->id),
'account_id' => 'required|belongsToUser:accounts',
'targetamount' => ['nullable', new IsValidPositiveAmount()],
'startdate' => 'date',
'targetdate' => 'date|nullable',
'order' => 'integer|max:32768|min:1',
'object_group' => 'min:0|max:255',
'notes' => 'min:1|max:32768|nullable',
'name' => sprintf('required|min:1|max:255|uniquePiggyBankForUser:%d', $piggy->id),
'accounts' => 'required|array',
'accounts.*' => 'required|belongsToUser:accounts',
'target_amount' => ['nullable', new IsValidPositiveAmount()],
'start_date' => 'date',
'target_date' => 'date|nullable',
'order' => 'integer|max:32768|min:1',
'object_group' => 'min:0|max:255',
'notes' => 'min:1|max:32768|nullable',
];
}
public function withValidator(Validator $validator): void
{
{ // need to have more than one account.
// accounts need to have the same currency or be multi-currency(?).
$validator->after(
function (Validator $validator): void {
// validate start before end only if both are there.
$data = $validator->getData();
$currency = $this->getCurrencyFromData($data);
if (array_key_exists('accounts', $data) && is_array($data['accounts'])) {
$repository = app(AccountRepositoryInterface::class);
$types = config('firefly.piggy_bank_account_types');
foreach ($data['accounts'] as $value) {
$accountId = (int) $value;
$account = $repository->find($accountId);
if (null !== $account) {
// check currency here.
$accountCurrency = $repository->getAccountCurrency($account);
$isMultiCurrency = $repository->getMetaValue($account, 'is_multi_currency');
if ($accountCurrency->id !== $currency->id && 'true' !== $isMultiCurrency) {
$validator->errors()->add('accounts', trans('validation.invalid_account_currency'));
}
$type = $account->accountType->type;
if (!in_array($type, $types, true)) {
$validator->errors()->add('accounts', trans('validation.invalid_account_type'));
}
}
}
}
}
);
if ($validator->fails()) {
Log::channel('audit')->error(sprintf('Validation errors in %s', __CLASS__), $validator->errors()->toArray());
}
}
private function getCurrencyFromData(array $data): TransactionCurrency
{
$currencyId = (int) ($data['transaction_currency_id'] ?? 0);
$currency = TransactionCurrency::find($currencyId);
if (null === $currency) {
return app('amount')->getDefaultCurrency();
}
return $currency;
}
}

View File

@ -32,6 +32,7 @@ use Illuminate\Database\Eloquent\Casts\Attribute;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Database\Eloquent\Relations\MorphMany;
use Illuminate\Database\Eloquent\Relations\MorphToMany;
@ -159,9 +160,9 @@ class Account extends Model
return $this->morphToMany(ObjectGroup::class, 'object_groupable');
}
public function piggyBanks(): HasMany
public function piggyBanks(): BelongsToMany
{
return $this->hasMany(PiggyBank::class);
return $this->belongsToMany(PiggyBank::class);
}
public function scopeAccountTypeIn(EloquentBuilder $query, array $types): void

View File

@ -27,12 +27,14 @@ namespace FireflyIII\Repositories\PiggyBank;
use FireflyIII\Events\Model\PiggyBank\ChangedAmount;
use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Factory\PiggyBankFactory;
use FireflyIII\Models\Account;
use FireflyIII\Models\Note;
use FireflyIII\Models\PiggyBank;
use FireflyIII\Models\PiggyBankRepetition;
use FireflyIII\Models\TransactionJournal;
use FireflyIII\Repositories\ObjectGroup\CreatesObjectGroups;
use FireflyIII\Support\Facades\Amount;
use Illuminate\Support\Facades\Log;
/**
* Trait ModifiesPiggyBanks
@ -55,30 +57,42 @@ trait ModifiesPiggyBanks
}
}
public function removeAmount(PiggyBank $piggyBank, string $amount, ?TransactionJournal $journal = null): bool
public function removeAmount(PiggyBank $piggyBank,Account $account, string $amount, ?TransactionJournal $journal = null): bool
{
$repetition = $this->getRepetition($piggyBank);
if (null === $repetition) {
return false;
}
$repetition->current_amount = bcsub($repetition->current_amount, $amount);
$repetition->save();
$currentAmount = $this->getCurrentAmount($piggyBank, $account);
$pivot = $piggyBank->accounts()->where('accounts.id', $account->id)->first()->pivot;
$pivot->current_amount = bcsub($currentAmount, $amount);
$pivot->save();
app('log')->debug('addAmount [a]: Trigger change for negative amount.');
app('log')->debug('removeAmount [a]: Trigger change for negative amount.');
event(new ChangedAmount($piggyBank, bcmul($amount, '-1'), $journal, null));
return true;
}
public function addAmount(PiggyBank $piggyBank, string $amount, ?TransactionJournal $journal = null): bool
public function removeAmountFromAll(PiggyBank $piggyBank, string $amount): void
{
$repetition = $this->getRepetition($piggyBank);
if (null === $repetition) {
return false;
foreach($piggyBank->accounts as $account) {
$current = $account->pivot->current_amount;
// if this account contains more than the amount, remove the amount and return.
if (1 === bccomp($current, $amount)) {
$this->removeAmount($piggyBank, $account, $amount);
return;
}
// if this account contains less than the amount, remove the current amount, update the amount and continue.
if (bccomp($current, $amount) < 1) {
$this->removeAmount($piggyBank, $account, $current);
$amount = bcsub($amount, $current);
}
}
$currentAmount = $repetition->current_amount ?? '0';
$repetition->current_amount = bcadd($currentAmount, $amount);
$repetition->save();
}
public function addAmount(PiggyBank $piggyBank, Account $account, string $amount, ?TransactionJournal $journal = null): bool
{
$currentAmount = $this->getCurrentAmount($piggyBank, $account);
$pivot = $piggyBank->accounts()->where('accounts.id', $account->id)->first()->pivot;
$pivot->current_amount = bcadd($currentAmount, $amount);
$pivot->save();
app('log')->debug('addAmount [b]: Trigger change for positive amount.');
event(new ChangedAmount($piggyBank, $amount, $journal, null));
@ -86,37 +100,36 @@ trait ModifiesPiggyBanks
return true;
}
public function canAddAmount(PiggyBank $piggyBank, string $amount): bool
public function canAddAmount(PiggyBank $piggyBank, Account $account, string $amount): bool
{
$today = today(config('app.timezone'));
$leftOnAccount = $this->leftOnAccount($piggyBank, $today);
$savedSoFar = $this->getRepetition($piggyBank)->current_amount;
Log::debug('Now in canAddAmount');
$today = today(config('app.timezone'))->endOfDay();
$leftOnAccount = $this->leftOnAccount($piggyBank, $account, $today);
$savedSoFar = $this->getCurrentAmount($piggyBank);
$maxAmount = $leftOnAccount;
$leftToSave = null;
app('log')->debug(sprintf('Left on account: %s on %s', $leftOnAccount, $today->format('Y-m-d H:i:s')));
app('log')->debug(sprintf('Saved so far: %s', $savedSoFar));
if (0 !== bccomp($piggyBank->target_amount, '0')) {
$leftToSave = bcsub($piggyBank->target_amount, $savedSoFar);
$maxAmount = 1 === bccomp($leftOnAccount, $leftToSave) ? $leftToSave : $leftOnAccount;
app('log')->debug(sprintf('Left to save: %s', $leftToSave));
app('log')->debug(sprintf('Maximum amount: %s', $maxAmount));
}
$compare = bccomp($amount, $maxAmount);
$result = $compare <= 0;
app('log')->debug(sprintf('Left on account: %s on %s', $leftOnAccount, $today->format('Y-m-d')));
app('log')->debug(sprintf('Saved so far: %s', $savedSoFar));
app('log')->debug(sprintf('Left to save: %s', $leftToSave));
app('log')->debug(sprintf('Maximum amount: %s', $maxAmount));
app('log')->debug(sprintf('Compare <= 0? %d, so %s', $compare, var_export($result, true)));
app('log')->debug(sprintf('Compare <= 0? %d, so canAddAmount is %s', $compare, var_export($result, true)));
return $result;
}
public function canRemoveAmount(PiggyBank $piggyBank, string $amount): bool
public function canRemoveAmount(PiggyBank $piggyBank, Account $account, string $amount): bool
{
$repetition = $this->getRepetition($piggyBank);
if (null === $repetition) {
return false;
}
$savedSoFar = $repetition->current_amount;
$savedSoFar = $this->getCurrentAmount($piggyBank, $account);
return bccomp($amount, $savedSoFar) <= 0;
}
@ -244,17 +257,24 @@ trait ModifiesPiggyBanks
$this->setOrder($piggyBank, $newOrder);
}
// update the accounts
$factory = new PiggyBankFactory();
$factory->user = $this->user;
$factory->linkToAccountIds($piggyBank, $data['accounts']);
// if the piggy bank is now smaller than the current relevant rep,
// remove money from the rep.
$repetition = $this->getRepetition($piggyBank);
if (null !== $repetition && $repetition->current_amount > $piggyBank->target_amount && 0 !== bccomp($piggyBank->target_amount, '0')) {
$difference = bcsub($piggyBank->target_amount, $repetition->current_amount);
$currentAmount = $this->getCurrentAmount($piggyBank);
if (1 === bccomp($currentAmount, '100') && 0 !== bccomp($piggyBank->target_amount, '0')) {
$difference = bcsub($piggyBank->target_amount, $currentAmount);
// an amount will be removed, create "negative" event:
event(new ChangedAmount($piggyBank, $difference, null, null));
$repetition->current_amount = $piggyBank->target_amount;
$repetition->save();
// question is, from which account(s) to remove the difference?
// solution: just start from the top until there is no more money left to remove.
$this->removeAmountFromAll($piggyBank, app('steam')->positive($difference));
}
// update using name:
@ -295,22 +315,19 @@ trait ModifiesPiggyBanks
if (array_key_exists('name', $data) && '' !== $data['name']) {
$piggyBank->name = $data['name'];
}
if (array_key_exists('account_id', $data) && 0 !== $data['account_id']) {
$piggyBank->account_id = (int)$data['account_id'];
if (array_key_exists('target_amount', $data) && '' !== $data['target_amount']) {
$piggyBank->target_amount = $data['target_amount'];
}
if (array_key_exists('targetamount', $data) && '' !== $data['targetamount']) {
$piggyBank->target_amount = $data['targetamount'];
}
if (array_key_exists('targetamount', $data) && '' === $data['targetamount']) {
if (array_key_exists('target_amount', $data) && '' === $data['target_amount']) {
$piggyBank->target_amount = '0';
}
if (array_key_exists('targetdate', $data) && '' !== $data['targetdate']) {
$piggyBank->target_date = $data['targetdate'];
$piggyBank->target_date_tz = $data['targetdate']?->format('e');
if (array_key_exists('target_date', $data) && '' !== $data['target_date']) {
$piggyBank->target_date = $data['target_date'];
$piggyBank->target_date_tz = $data['target_date']?->format('e');
}
if (array_key_exists('startdate', $data)) {
$piggyBank->start_date = $data['startdate'];
$piggyBank->start_date_tz = $data['targetdate']?->format('e');
if (array_key_exists('start_date', $data)) {
$piggyBank->start_date = $data['start_date'];
$piggyBank->start_date_tz = $data['target_date']?->format('e');
}
$piggyBank->save();

View File

@ -26,6 +26,7 @@ namespace FireflyIII\Repositories\PiggyBank;
use Carbon\Carbon;
use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Factory\PiggyBankFactory;
use FireflyIII\Models\Account;
use FireflyIII\Models\Attachment;
use FireflyIII\Models\Note;
use FireflyIII\Models\PiggyBank;
@ -95,7 +96,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');
@ -114,22 +115,28 @@ class PiggyBankRepository implements PiggyBankRepositoryInterface
/**
* Get current amount saved in piggy bank.
*/
public function getCurrentAmount(PiggyBank $piggyBank): string
public function getCurrentAmount(PiggyBank $piggyBank, ?Account $account = null): string
{
$sum = '0';
foreach ($piggyBank->accounts as $account) {
$amount = (string) $account->pivot->current_amount;
foreach ($piggyBank->accounts as $current) {
if(null !== $account && $account->id !== $current->id) {
continue;
}
$amount = (string) $current->pivot->current_amount;
$amount = '' === $amount ? '0' : $amount;
$sum = bcadd($sum, $amount);
}
Log::debug(sprintf('Current amount in piggy bank #%d ("%s") is %s', $piggyBank->id, $piggyBank->name, $sum));
return $sum;
}
public function getRepetition(PiggyBank $piggyBank): ?PiggyBankRepetition
public function getRepetition(PiggyBank $piggyBank, bool $overrule = false): ?PiggyBankRepetition
{
throw new FireflyException('[b] Piggy bank repetitions are EOL.');
if (false === $overrule) {
throw new FireflyException('[b] Piggy bank repetitions are EOL.');
}
Log::warning('Piggy bank repetitions are EOL.');
return $piggyBank->piggyBankRepetitions()->first();
}
@ -148,15 +155,15 @@ class PiggyBankRepository implements PiggyBankRepositoryInterface
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);
@ -165,10 +172,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) {
@ -190,7 +197,7 @@ class PiggyBankRepository implements PiggyBankRepositoryInterface
}
// currency of the account + the piggy bank currency are almost the same.
// which amount from the transaction matches?
$amount = null;
$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
@ -206,8 +213,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.
@ -239,7 +246,7 @@ class PiggyBankRepository implements PiggyBankRepositoryInterface
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;
@ -264,12 +271,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;
@ -278,16 +285,15 @@ class PiggyBankRepository implements PiggyBankRepositoryInterface
public function getPiggyBanks(): Collection
{
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('piggy_banks.order', 'ASC')->get(['piggy_banks.*'])
;
->leftJoin('accounts', 'accounts.id', '=', 'account_piggy_bank.account_id')
->where('accounts.user_id', auth()->user()->id)
->with(
[
'account',
'objectGroups',
]
)
->orderBy('piggy_banks.order', 'ASC')->distinct()->get(['piggy_banks.*']);
}
/**
@ -320,21 +326,22 @@ class PiggyBankRepository implements PiggyBankRepositoryInterface
/**
* Get for piggy account what is left to put in piggies.
*/
public function leftOnAccount(PiggyBank $piggyBank, Carbon $date): string
public function leftOnAccount(PiggyBank $piggyBank, Account $account, Carbon $date): string
{
$balance = app('steam')->balanceIgnoreVirtual($piggyBank->account, $date);
Log::debug(sprintf('leftOnAccount("%s","%s","%s")', $piggyBank->name, $account->name, $date->format('Y-m-d H:i:s')));
$balance = app('steam')->balanceConvertedIgnoreVirtual($account, $date, $piggyBank->transactionCurrency);
Log::debug(sprintf('Balance is: %s', $balance));
/** @var Collection $piggies */
$piggies = $piggyBank->account->piggyBanks;
$piggies = $account->piggyBanks;
/** @var PiggyBank $current */
foreach ($piggies as $current) {
$repetition = $this->getRepetition($current);
if (null !== $repetition) {
$balance = bcsub($balance, $repetition->current_amount);
}
$amount = $this->getCurrentAmount($current, $account);
$balance = bcsub($balance, $amount);
Log::debug(sprintf('Piggy bank: #%d with amount %s, balance is now %s', $current->id, $amount, $balance));
}
Log::debug(sprintf('Final balance is: %s', $balance));
return $balance;
}
@ -345,8 +352,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();
}

View File

@ -25,6 +25,7 @@ namespace FireflyIII\Repositories\PiggyBank;
use Carbon\Carbon;
use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Models\Account;
use FireflyIII\Models\PiggyBank;
use FireflyIII\Models\PiggyBankRepetition;
use FireflyIII\Models\TransactionJournal;
@ -37,13 +38,13 @@ use Illuminate\Support\Collection;
*/
interface PiggyBankRepositoryInterface
{
public function addAmount(PiggyBank $piggyBank, string $amount, ?TransactionJournal $journal = null): bool;
public function addAmount(PiggyBank $piggyBank, Account $account, string $amount, ?TransactionJournal $journal = null): bool;
public function addAmountToRepetition(PiggyBankRepetition $repetition, string $amount, TransactionJournal $journal): void;
public function canAddAmount(PiggyBank $piggyBank, string $amount): bool;
public function canAddAmount(PiggyBank $piggyBank, Account $account, string $amount): bool;
public function canRemoveAmount(PiggyBank $piggyBank, string $amount): bool;
public function canRemoveAmount(PiggyBank $piggyBank, Account $account, string $amount): bool;
/**
* Destroy piggy bank.
@ -68,7 +69,10 @@ interface PiggyBankRepositoryInterface
/**
* Get current amount saved in piggy bank.
*/
public function getCurrentAmount(PiggyBank $piggyBank): string;
public function getCurrentAmount(PiggyBank $piggyBank, ?Account $account = null): string;
/**
* Get current amount saved in piggy bank.
*/
/**
* Get all events.
@ -97,7 +101,7 @@ interface PiggyBankRepositoryInterface
*/
public function getPiggyBanksWithAmount(): Collection;
public function getRepetition(PiggyBank $piggyBank): ?PiggyBankRepetition;
public function getRepetition(PiggyBank $piggyBank, bool $overrule = false): ?PiggyBankRepetition;
/**
* Returns the suggested amount the user should save per month, or "".
@ -107,9 +111,10 @@ interface PiggyBankRepositoryInterface
/**
* Get for piggy account what is left to put in piggies.
*/
public function leftOnAccount(PiggyBank $piggyBank, Carbon $date): string;
public function leftOnAccount(PiggyBank $piggyBank,Account $account, Carbon $date): string;
public function removeAmount(PiggyBank $piggyBank, string $amount, ?TransactionJournal $journal = null): bool;
public function removeAmount(PiggyBank $piggyBank, Account $account, string $amount, ?TransactionJournal $journal = null): bool;
public function removeAmountFromAll(PiggyBank $piggyBank, string $amount): void;
public function removeObjectGroup(PiggyBank $piggyBank): PiggyBank;

View File

@ -44,34 +44,52 @@ class Steam
*/
public function balanceIgnoreVirtual(Account $account, Carbon $date): string
{
// Log::warning(sprintf('Deprecated method %s, do not use.', __METHOD__));
throw new FireflyException('Deprecated method balanceIgnoreVirtual.');
/** @var AccountRepositoryInterface $repository */
$repository = app(AccountRepositoryInterface::class);
$repository = app(AccountRepositoryInterface::class);
$repository->setUser($account->user);
$currencyId = (int) $repository->getMetaValue($account, 'currency_id');
$transactions = $account->transactions()
->leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id')
->where('transaction_journals.date', '<=', $date->format('Y-m-d 23:59:59'))
->where('transactions.transaction_currency_id', $currencyId)
->get(['transactions.amount'])->toArray()
;
$nativeBalance = $this->sumTransactions($transactions, 'amount');
$currencyId = (int) $repository->getMetaValue($account, 'currency_id');
$transactions = $account->transactions()
->leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id')
->where('transaction_journals.date', '<=', $date->format('Y-m-d 23:59:59'))
->where('transactions.transaction_currency_id', $currencyId)
->get(['transactions.amount'])->toArray();
$nativeBalance = $this->sumTransactions($transactions, 'amount');
// get all balances in foreign currency:
$transactions = $account->transactions()
->leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id')
->where('transaction_journals.date', '<=', $date->format('Y-m-d 23:59:59'))
->where('transactions.foreign_currency_id', $currencyId)
->where('transactions.transaction_currency_id', '!=', $currencyId)
->get(['transactions.foreign_amount'])->toArray()
;
$transactions = $account->transactions()
->leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id')
->where('transaction_journals.date', '<=', $date->format('Y-m-d 23:59:59'))
->where('transactions.foreign_currency_id', $currencyId)
->where('transactions.transaction_currency_id', '!=', $currencyId)
->get(['transactions.foreign_amount'])->toArray();
$foreignBalance = $this->sumTransactions($transactions, 'foreign_amount');
return bcadd($nativeBalance, $foreignBalance);
}
public function balanceConvertedIgnoreVirtual(Account $account, Carbon $date, TransactionCurrency $currency): string
{
$balance = $this->balanceConverted($account, $date, $currency);
$virtual = null === $account->virtual_balance ? '0' : $account->virtual_balance;
// currency of account
$repository = app(AccountRepositoryInterface::class);
$repository->setUser($account->user);
$accountCurrency = $repository->getAccountCurrency($account) ?? app('amount')->getDefaultCurrencyByUserGroup($account->user->userGroup);
if ($accountCurrency->id !== $currency->id && 0 !== bccomp($virtual, '0')) {
// convert amount to given currency.
Log::debug(sprintf('Created new ExchangeRateConverter in %s', __METHOD__));
$converter = new ExchangeRateConverter();
$virtual = $converter->convert($accountCurrency, $currency, $date, $virtual);
}
return bcsub($balance, $virtual);
}
public function sumTransactions(array $transactions, string $key): string
{
$sum = '0';
@ -96,7 +114,7 @@ class Steam
public function balanceInRange(Account $account, Carbon $start, Carbon $end, ?TransactionCurrency $currency = null): array
{
// Log::warning(sprintf('Deprecated method %s, do not use.', __METHOD__));
$cache = new CacheProperties();
$cache = new CacheProperties();
$cache->addProperty($account->id);
$cache->addProperty('balance-in-range');
$cache->addProperty(null !== $currency ? $currency->id : 0);
@ -108,42 +126,41 @@ class Steam
$start->subDay();
$end->addDay();
$balances = [];
$formatted = $start->format('Y-m-d');
$startBalance = $this->balance($account, $start, $currency);
$balances = [];
$formatted = $start->format('Y-m-d');
$startBalance = $this->balance($account, $start, $currency);
$balances[$formatted] = $startBalance;
if (null === $currency) {
$repository = app(AccountRepositoryInterface::class);
$repository->setUser($account->user);
$currency = $repository->getAccountCurrency($account) ?? app('amount')->getDefaultCurrencyByUserGroup($account->user->userGroup);
$currency = $repository->getAccountCurrency($account) ?? app('amount')->getDefaultCurrencyByUserGroup($account->user->userGroup);
}
$currencyId = $currency->id;
$currencyId = $currency->id;
$start->addDay();
// query!
$set = $account->transactions()
->leftJoin('transaction_journals', 'transactions.transaction_journal_id', '=', 'transaction_journals.id')
->where('transaction_journals.date', '>=', $start->format('Y-m-d 00:00:00'))
->where('transaction_journals.date', '<=', $end->format('Y-m-d 23:59:59'))
->groupBy('transaction_journals.date')
->groupBy('transactions.transaction_currency_id')
->groupBy('transactions.foreign_currency_id')
->orderBy('transaction_journals.date', 'ASC')
->whereNull('transaction_journals.deleted_at')
->get(
[ // @phpstan-ignore-line
'transaction_journals.date',
'transactions.transaction_currency_id',
\DB::raw('SUM(transactions.amount) AS modified'),
'transactions.foreign_currency_id',
\DB::raw('SUM(transactions.foreign_amount) AS modified_foreign'),
]
)
;
$set = $account->transactions()
->leftJoin('transaction_journals', 'transactions.transaction_journal_id', '=', 'transaction_journals.id')
->where('transaction_journals.date', '>=', $start->format('Y-m-d 00:00:00'))
->where('transaction_journals.date', '<=', $end->format('Y-m-d 23:59:59'))
->groupBy('transaction_journals.date')
->groupBy('transactions.transaction_currency_id')
->groupBy('transactions.foreign_currency_id')
->orderBy('transaction_journals.date', 'ASC')
->whereNull('transaction_journals.deleted_at')
->get(
[ // @phpstan-ignore-line
'transaction_journals.date',
'transactions.transaction_currency_id',
\DB::raw('SUM(transactions.amount) AS modified'),
'transactions.foreign_currency_id',
\DB::raw('SUM(transactions.foreign_amount) AS modified_foreign'),
]
);
$currentBalance = $startBalance;
$currentBalance = $startBalance;
/** @var Transaction $entry */
foreach ($set as $entry) {
@ -173,7 +190,7 @@ class Steam
public function balanceByTransactions(Account $account, Carbon $date, ?TransactionCurrency $currency): array
{
$cache = new CacheProperties();
$cache = new CacheProperties();
$cache->addProperty($account->id);
$cache->addProperty('balance-by-transactions');
$cache->addProperty($date);
@ -182,13 +199,12 @@ class Steam
return $cache->get();
}
$query = $account->transactions()
->leftJoin('transaction_journals', 'transactions.transaction_journal_id', '=', 'transaction_journals.id')
->orderBy('transaction_journals.date', 'desc')
->orderBy('transaction_journals.order', 'asc')
->orderBy('transaction_journals.description', 'desc')
->orderBy('transactions.amount', 'desc')
;
$query = $account->transactions()
->leftJoin('transaction_journals', 'transactions.transaction_journal_id', '=', 'transaction_journals.id')
->orderBy('transaction_journals.date', 'desc')
->orderBy('transaction_journals.order', 'asc')
->orderBy('transaction_journals.description', 'desc')
->orderBy('transactions.amount', 'desc');
if (null !== $currency) {
$query->where('transactions.transaction_currency_id', $currency->id);
$query->limit(1);
@ -203,7 +219,7 @@ class Steam
$return = [];
$result = $query->get(['transactions.transaction_currency_id', 'transactions.balance_after']);
foreach ($result as $entry) {
$key = (int) $entry->transaction_currency_id;
$key = (int) $entry->transaction_currency_id;
if (array_key_exists($key, $return)) {
continue;
}
@ -222,7 +238,7 @@ class Steam
{
// Log::warning(sprintf('Deprecated method %s, do not use.', __METHOD__));
// abuse chart properties:
$cache = new CacheProperties();
$cache = new CacheProperties();
$cache->addProperty($account->id);
$cache->addProperty('balance');
$cache->addProperty($date);
@ -232,26 +248,24 @@ class Steam
}
/** @var AccountRepositoryInterface $repository */
$repository = app(AccountRepositoryInterface::class);
$repository = app(AccountRepositoryInterface::class);
if (null === $currency) {
$currency = $repository->getAccountCurrency($account) ?? app('amount')->getDefaultCurrencyByUserGroup($account->user->userGroup);
}
// first part: get all balances in own currency:
$transactions = $account->transactions()
->leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id')
->where('transaction_journals.date', '<=', $date->format('Y-m-d 23:59:59'))
->where('transactions.transaction_currency_id', $currency->id)
->get(['transactions.amount'])->toArray()
;
$nativeBalance = $this->sumTransactions($transactions, 'amount');
$transactions = $account->transactions()
->leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id')
->where('transaction_journals.date', '<=', $date->format('Y-m-d 23:59:59'))
->where('transactions.transaction_currency_id', $currency->id)
->get(['transactions.amount'])->toArray();
$nativeBalance = $this->sumTransactions($transactions, 'amount');
// get all balances in foreign currency:
$transactions = $account->transactions()
->leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id')
->where('transaction_journals.date', '<=', $date->format('Y-m-d 23:59:59'))
->where('transactions.foreign_currency_id', $currency->id)
->where('transactions.transaction_currency_id', '!=', $currency->id)
->get(['transactions.foreign_amount'])->toArray()
;
->leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id')
->where('transaction_journals.date', '<=', $date->format('Y-m-d 23:59:59'))
->where('transactions.foreign_currency_id', $currency->id)
->where('transactions.transaction_currency_id', '!=', $currency->id)
->get(['transactions.foreign_amount'])->toArray();
$foreignBalance = $this->sumTransactions($transactions, 'foreign_amount');
$balance = bcadd($nativeBalance, $foreignBalance);
$virtual = null === $account->virtual_balance ? '0' : $account->virtual_balance;
@ -270,7 +284,7 @@ class Steam
public function balanceInRangeConverted(Account $account, Carbon $start, Carbon $end, TransactionCurrency $native): array
{
// Log::warning(sprintf('Deprecated method %s, do not use.', __METHOD__));
$cache = new CacheProperties();
$cache = new CacheProperties();
$cache->addProperty($account->id);
$cache->addProperty('balance-in-range-converted');
$cache->addProperty($native->id);
@ -290,35 +304,34 @@ class Steam
Log::debug(sprintf('Start balance on %s is %s', $formatted, $startBalance));
Log::debug(sprintf('Created new ExchangeRateConverter in %s', __METHOD__));
$converter = new ExchangeRateConverter();
$converter = new ExchangeRateConverter();
// not sure why this is happening:
$start->addDay();
// grab all transactions between start and end:
$set = $account->transactions()
->leftJoin('transaction_journals', 'transactions.transaction_journal_id', '=', 'transaction_journals.id')
->where('transaction_journals.date', '>=', $start->format('Y-m-d 00:00:00'))
->where('transaction_journals.date', '<=', $end->format('Y-m-d 23:59:59'))
->orderBy('transaction_journals.date', 'ASC')
->whereNull('transaction_journals.deleted_at')
->get(
[
'transaction_journals.date',
'transactions.transaction_currency_id',
'transactions.amount',
'transactions.foreign_currency_id',
'transactions.foreign_amount',
]
)->toArray()
;
$set = $account->transactions()
->leftJoin('transaction_journals', 'transactions.transaction_journal_id', '=', 'transaction_journals.id')
->where('transaction_journals.date', '>=', $start->format('Y-m-d 00:00:00'))
->where('transaction_journals.date', '<=', $end->format('Y-m-d 23:59:59'))
->orderBy('transaction_journals.date', 'ASC')
->whereNull('transaction_journals.deleted_at')
->get(
[
'transaction_journals.date',
'transactions.transaction_currency_id',
'transactions.amount',
'transactions.foreign_currency_id',
'transactions.foreign_amount',
]
)->toArray();
// loop the set and convert if necessary:
$currentBalance = $startBalance;
$currentBalance = $startBalance;
/** @var Transaction $transaction */
foreach ($set as $transaction) {
$day = false;
$day = false;
try {
$day = Carbon::parse($transaction['date'], config('app.timezone'));
@ -328,7 +341,7 @@ class Steam
if (false === $day) {
$day = today(config('app.timezone'));
}
$format = $day->format('Y-m-d');
$format = $day->format('Y-m-d');
// if the transaction is in the expected currency, change nothing.
if ((int) $transaction['transaction_currency_id'] === $native->id) {
// change the current balance, set it to today, continue the loop.
@ -351,21 +364,21 @@ class Steam
$currency = $currencies[$currencyId] ?? TransactionCurrency::find($currencyId);
$currencies[$currencyId] = $currency;
$rate = $converter->getCurrencyRate($currency, $native, $day);
$convertedAmount = bcmul($transaction['amount'], $rate);
$currentBalance = bcadd($currentBalance, $convertedAmount);
$balances[$format] = $currentBalance;
$rate = $converter->getCurrencyRate($currency, $native, $day);
$convertedAmount = bcmul($transaction['amount'], $rate);
$currentBalance = bcadd($currentBalance, $convertedAmount);
$balances[$format] = $currentBalance;
Log::debug(sprintf(
'%s: transaction in %s(!). Conversion rate is %s. %s %s = %s %s',
$format,
$currency->code,
$rate,
$currency->code,
$transaction['amount'],
$native->code,
$convertedAmount
));
'%s: transaction in %s(!). Conversion rate is %s. %s %s = %s %s',
$format,
$currency->code,
$rate,
$currency->code,
$transaction['amount'],
$native->code,
$convertedAmount
));
}
$cache->store($balances);
@ -397,7 +410,7 @@ class Steam
{
// Log::warning(sprintf('Deprecated method %s, do not use.', __METHOD__));
Log::debug(sprintf('Now in balanceConverted (%s) for account #%d, converting to %s', $date->format('Y-m-d'), $account->id, $native->code));
$cache = new CacheProperties();
$cache = new CacheProperties();
$cache->addProperty($account->id);
$cache->addProperty('balance');
$cache->addProperty($date);
@ -418,72 +431,66 @@ class Steam
return $this->balance($account, $date);
}
$new = [];
$existing = [];
$new[] = $account->transactions() // 1
->leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id')
->where('transaction_journals.date', '<=', $date->format('Y-m-d 23:59:59'))
->where('transactions.transaction_currency_id', $currency->id)
->whereNull('transactions.foreign_currency_id')
->get(['transaction_journals.date', 'transactions.amount'])->toArray()
;
$new = [];
$existing = [];
$new[] = $account->transactions() // 1
->leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id')
->where('transaction_journals.date', '<=', $date->format('Y-m-d 23:59:59'))
->where('transactions.transaction_currency_id', $currency->id)
->whereNull('transactions.foreign_currency_id')
->get(['transaction_journals.date', 'transactions.amount'])->toArray();
Log::debug(sprintf('%d transaction(s) in set #1', count($new[0])));
$existing[] = $account->transactions() // 2
->leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id')
->where('transaction_journals.date', '<=', $date->format('Y-m-d 23:59:59'))
->where('transactions.transaction_currency_id', $native->id)
->whereNull('transactions.foreign_currency_id')
->get(['transactions.amount'])->toArray()
;
->leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id')
->where('transaction_journals.date', '<=', $date->format('Y-m-d 23:59:59'))
->where('transactions.transaction_currency_id', $native->id)
->whereNull('transactions.foreign_currency_id')
->get(['transactions.amount'])->toArray();
Log::debug(sprintf('%d transaction(s) in set #2', count($existing[0])));
$new[] = $account->transactions() // 3
->leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id')
->where('transaction_journals.date', '<=', $date->format('Y-m-d 23:59:59'))
->where('transactions.transaction_currency_id', '!=', $currency->id)
->where('transactions.transaction_currency_id', '!=', $native->id)
->whereNull('transactions.foreign_currency_id')
->get(['transaction_journals.date', 'transactions.amount'])->toArray()
;
$new[] = $account->transactions() // 3
->leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id')
->where('transaction_journals.date', '<=', $date->format('Y-m-d 23:59:59'))
->where('transactions.transaction_currency_id', '!=', $currency->id)
->where('transactions.transaction_currency_id', '!=', $native->id)
->whereNull('transactions.foreign_currency_id')
->get(['transaction_journals.date', 'transactions.amount'])->toArray();
Log::debug(sprintf('%d transactions in set #3', count($new[1])));
$existing[] = $account->transactions() // 4
->leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id')
->where('transaction_journals.date', '<=', $date->format('Y-m-d 23:59:59'))
->where('transactions.foreign_currency_id', $native->id)
->whereNotNull('transactions.foreign_amount')
->get(['transactions.foreign_amount'])->toArray()
;
->leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id')
->where('transaction_journals.date', '<=', $date->format('Y-m-d 23:59:59'))
->where('transactions.foreign_currency_id', $native->id)
->whereNotNull('transactions.foreign_amount')
->get(['transactions.foreign_amount'])->toArray();
Log::debug(sprintf('%d transactions in set #4', count($existing[1])));
$new[] = $account->transactions()// 5
->leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id')
->where('transaction_journals.date', '<=', $date->format('Y-m-d 23:59:59'))
->where('transactions.transaction_currency_id', $currency->id)
->where('transactions.foreign_currency_id', '!=', $native->id)
->whereNotNull('transactions.foreign_amount')
->get(['transaction_journals.date', 'transactions.amount'])->toArray()
;
$new[] = $account->transactions()// 5
->leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id')
->where('transaction_journals.date', '<=', $date->format('Y-m-d 23:59:59'))
->where('transactions.transaction_currency_id', $currency->id)
->where('transactions.foreign_currency_id', '!=', $native->id)
->whereNotNull('transactions.foreign_amount')
->get(['transaction_journals.date', 'transactions.amount'])->toArray();
Log::debug(sprintf('%d transactions in set #5', count($new[2])));
$new[] = $account->transactions()// 6
->leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id')
->where('transaction_journals.date', '<=', $date->format('Y-m-d 23:59:59'))
->where('transactions.transaction_currency_id', '!=', $currency->id)
->where('transactions.foreign_currency_id', '!=', $native->id)
->whereNotNull('transactions.foreign_amount')
->get(['transaction_journals.date', 'transactions.amount'])->toArray()
;
$new[] = $account->transactions()// 6
->leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id')
->where('transaction_journals.date', '<=', $date->format('Y-m-d 23:59:59'))
->where('transactions.transaction_currency_id', '!=', $currency->id)
->where('transactions.foreign_currency_id', '!=', $native->id)
->whereNotNull('transactions.foreign_amount')
->get(['transaction_journals.date', 'transactions.amount'])->toArray();
Log::debug(sprintf('%d transactions in set #6', count($new[3])));
// process both sets of transactions. Of course, no need to convert set "existing".
$balance = $this->sumTransactions($existing[0], 'amount');
$balance = bcadd($balance, $this->sumTransactions($existing[1], 'foreign_amount'));
$balance = $this->sumTransactions($existing[0], 'amount');
$balance = bcadd($balance, $this->sumTransactions($existing[1], 'foreign_amount'));
Log::debug(sprintf('Balance from set #2 and #4 is %f', $balance));
// need to convert the others. All sets use the "amount" value as their base (that's easy)
// but we need to convert each transaction separately because the date difference may
// incur huge currency changes.
Log::debug(sprintf('Created new ExchangeRateConverter in %s', __METHOD__));
$start = clone $date;
$end = clone $date;
$converter = new ExchangeRateConverter();
$start = clone $date;
$end = clone $date;
$converter = new ExchangeRateConverter();
foreach ($new as $set) {
foreach ($set as $transaction) {
$currentDate = false;
@ -506,7 +513,7 @@ class Steam
foreach ($new as $set) {
foreach ($set as $transaction) {
$currentDate = false;
$currentDate = false;
try {
$currentDate = Carbon::parse($transaction['date'], config('app.timezone'));
@ -523,9 +530,9 @@ class Steam
}
// add virtual balance (also needs conversion)
$virtual = null === $account->virtual_balance ? '0' : $account->virtual_balance;
$virtual = $converter->convert($currency, $native, $account->created_at, $virtual);
$balance = bcadd($balance, $virtual);
$virtual = null === $account->virtual_balance ? '0' : $account->virtual_balance;
$virtual = $converter->convert($currency, $native, $account->created_at, $virtual);
$balance = bcadd($balance, $virtual);
$converter->summarize();
$cache->store($balance);
@ -542,9 +549,9 @@ class Steam
public function balancesByAccounts(Collection $accounts, Carbon $date): array
{
// Log::warning(sprintf('Deprecated method %s, do not use.', __METHOD__));
$ids = $accounts->pluck('id')->toArray();
$ids = $accounts->pluck('id')->toArray();
// cache this property.
$cache = new CacheProperties();
$cache = new CacheProperties();
$cache->addProperty($ids);
$cache->addProperty('balances');
$cache->addProperty($date);
@ -573,9 +580,9 @@ class Steam
public function balancesByAccountsConverted(Collection $accounts, Carbon $date): array
{
// Log::warning(sprintf('Deprecated method %s, do not use.', __METHOD__));
$ids = $accounts->pluck('id')->toArray();
$ids = $accounts->pluck('id')->toArray();
// cache this property.
$cache = new CacheProperties();
$cache = new CacheProperties();
$cache->addProperty($ids);
$cache->addProperty('balances-converted');
$cache->addProperty($date);
@ -591,9 +598,9 @@ class Steam
$default = app('amount')->getDefaultCurrencyByUserGroup($account->user->userGroup);
$result[$account->id]
= [
'balance' => $this->balance($account, $date),
'native_balance' => $this->balanceConverted($account, $date, $default),
];
'balance' => $this->balance($account, $date),
'native_balance' => $this->balanceConverted($account, $date, $default),
];
}
$cache->store($result);
@ -607,9 +614,9 @@ class Steam
public function balancesPerCurrencyByAccounts(Collection $accounts, Carbon $date): array
{
// Log::warning(sprintf('Deprecated method %s, do not use.', __METHOD__));
$ids = $accounts->pluck('id')->toArray();
$ids = $accounts->pluck('id')->toArray();
// cache this property.
$cache = new CacheProperties();
$cache = new CacheProperties();
$cache->addProperty($ids);
$cache->addProperty('balances-per-currency');
$cache->addProperty($date);
@ -634,7 +641,7 @@ class Steam
{
// Log::warning(sprintf('Deprecated method %s, do not use.', __METHOD__));
// abuse chart properties:
$cache = new CacheProperties();
$cache = new CacheProperties();
$cache->addProperty($account->id);
$cache->addProperty('balance-per-currency');
$cache->addProperty($date);
@ -642,10 +649,9 @@ class Steam
return $cache->get();
}
$query = $account->transactions()
->leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id')
->where('transaction_journals.date', '<=', $date->format('Y-m-d 23:59:59'))
->groupBy('transactions.transaction_currency_id')
;
->leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id')
->where('transaction_journals.date', '<=', $date->format('Y-m-d 23:59:59'))
->groupBy('transactions.transaction_currency_id');
$balances = $query->get(['transactions.transaction_currency_id', \DB::raw('SUM(transactions.amount) as sum_for_currency')]); // @phpstan-ignore-line
$return = [];
@ -677,10 +683,10 @@ class Steam
// Log::debug(sprintf('Trying bcround("%s",%d)', $number, $precision));
if (str_contains($number, '.')) {
if ('-' !== $number[0]) {
return bcadd($number, '0.'.str_repeat('0', $precision).'5', $precision);
return bcadd($number, '0.' . str_repeat('0', $precision) . '5', $precision);
}
return bcsub($number, '0.'.str_repeat('0', $precision).'5', $precision);
return bcsub($number, '0.' . str_repeat('0', $precision) . '5', $precision);
}
return $number;
@ -763,15 +769,15 @@ class Steam
{
$list = [];
$set = auth()->user()->transactions()
->whereIn('transactions.account_id', $accounts)
->groupBy(['transactions.account_id', 'transaction_journals.user_id'])
->get(['transactions.account_id', \DB::raw('MAX(transaction_journals.date) AS max_date')]) // @phpstan-ignore-line
$set = auth()->user()->transactions()
->whereIn('transactions.account_id', $accounts)
->groupBy(['transactions.account_id', 'transaction_journals.user_id'])
->get(['transactions.account_id', \DB::raw('MAX(transaction_journals.date) AS max_date')]) // @phpstan-ignore-line
;
/** @var Transaction $entry */
foreach ($set as $entry) {
$date = new Carbon($entry->max_date, config('app.timezone'));
$date = new Carbon($entry->max_date, config('app.timezone'));
$date->setTimezone(config('app.timezone'));
$list[$entry->account_id] = $date;
}
@ -846,9 +852,9 @@ class Steam
public function getSafeUrl(string $unknownUrl, string $safeUrl): string
{
// Log::debug(sprintf('getSafeUrl(%s, %s)', $unknownUrl, $safeUrl));
$returnUrl = $safeUrl;
$unknownHost = parse_url($unknownUrl, PHP_URL_HOST);
$safeHost = parse_url($safeUrl, PHP_URL_HOST);
$returnUrl = $safeUrl;
$unknownHost = parse_url($unknownUrl, PHP_URL_HOST);
$safeHost = parse_url($safeUrl, PHP_URL_HOST);
if (null !== $unknownHost && $unknownHost === $safeHost) {
$returnUrl = $unknownUrl;
@ -885,7 +891,7 @@ class Steam
*/
public function floatalize(string $value): string
{
$value = strtoupper($value);
$value = strtoupper($value);
if (!str_contains($value, 'E')) {
return $value;
}

View File

@ -26,6 +26,7 @@ namespace FireflyIII\TransactionRules\Actions;
use FireflyIII\Events\Model\Rule\RuleActionFailedOnArray;
use FireflyIII\Events\TriggeredAuditLog;
use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Models\PiggyBank;
use FireflyIII\Models\RuleAction;
use FireflyIII\Models\Transaction;
@ -81,6 +82,7 @@ class UpdatePiggybank implements ActionInterface
if ($source->account_id === $piggyBank->account_id) {
app('log')->debug('Piggy bank account is linked to source, so remove amount from piggy bank.');
throw new FireflyException('Reference the correct account here.');
$this->removeAmount($piggyBank, $journal, $journalObj, $destination->amount);
event(
@ -161,6 +163,7 @@ class UpdatePiggybank implements ActionInterface
}
// make sure we can remove amount:
throw new FireflyException('Reference the correct account here.');
if (false === $repository->canRemoveAmount($piggyBank, $amount)) {
app('log')->warning(sprintf('Cannot remove %s from piggy bank.', $amount));
event(new RuleActionFailedOnArray($this->action, $array, trans('rules.cannot_remove_from_piggy', ['amount' => $amount, 'name' => $piggyBank->name])));
@ -169,6 +172,7 @@ class UpdatePiggybank implements ActionInterface
}
app('log')->debug(sprintf('Will now remove %s from piggy bank.', $amount));
throw new FireflyException('Reference the correct account here.');
$repository->removeAmount($piggyBank, $amount, $journal);
}
@ -199,6 +203,7 @@ class UpdatePiggybank implements ActionInterface
}
// make sure we can add amount:
throw new FireflyException('Reference the correct account here.');
if (false === $repository->canAddAmount($piggyBank, $amount)) {
app('log')->warning(sprintf('Cannot add %s to piggy bank.', $amount));
event(new RuleActionFailedOnArray($this->action, $array, trans('rules.cannot_add_to_piggy', ['amount' => $amount, 'name' => $piggyBank->name])));

View File

@ -78,7 +78,7 @@ class PiggyBankTransformer extends AbstractTransformer
// get currently saved amount:
$currency = $piggyBank->transactionCurrency;
$currentAmount = app('steam')->bcround($this->piggyRepos->getCurrentAmount($piggyBank), $currency->decimal_places);
$currentAmount = $this->piggyRepos->getCurrentAmount($piggyBank);
// Amounts, depending on 0.0 state of target amount
$percentage = null;

View File

@ -106,6 +106,10 @@ class User extends Authenticatable
return $this->hasMany(Account::class);
}
public function piggyBanks() {
throw new FireflyException('Method no longer supported.');
}
/**
* Link to attachments
*/

View File

@ -28,8 +28,8 @@ return [
'slack' => ['enabled' => true, 'ui_configurable' => 1],
'ntfy' => ['enabled' => true, 'ui_configurable' => 1],
'pushover' => ['enabled' => true, 'ui_configurable' => 1],
'gotify' => ['enabled' => false, 'ui_configurable' => 0],
'pushbullet' => ['enabled' => false, 'ui_configurable' => 0],
// 'gotify' => ['enabled' => false, 'ui_configurable' => 0],
// 'pushbullet' => ['enabled' => false, 'ui_configurable' => 0],
],
'notifications' => [
'user' => [

View File

@ -25,9 +25,9 @@
<td style="text-align: right;">
{% if event.amount < 0 %}
<span class="text-danger money-negative">{{ trans('firefly.removed_amount', {amount: formatAmountByAccount(event.piggyBank.account, event.amount, false)})|raw }}</span>
<span class="text-danger money-negative">{{ trans('firefly.removed_amount', {amount: formatAmountBySymbol(event.amount,event.piggyBank.transactionCurrency.symbol, false)})|raw }}</span>
{% else %}
<span class="text-success money-positive">{{ trans('firefly.added_amount', {amount: formatAmountByAccount(event.piggyBank.account, event.amount, false)})|raw }}</span>
<span class="text-success money-positive">{{ trans('firefly.added_amount', {amount: formatAmountBySymbol(event.amount, event.piggyBank.transactionCurrency.symbol, false)})|raw }}</span>
{% endif %}
</td>
</tr>

View File

@ -14,16 +14,16 @@
<h3 class="box-title">{{ trans('firefly.add_money_to_piggy', {name: piggyBank.name}) }}</h3>
</div>
<div class="box-body">
{% if maxAmount > 0 %}
<p>
{{ 'max_amount_add'|_ }}: {{ formatAmountByCurrency(currency,maxAmount) }}.
</p>
{% if total > 0 %}
<div class="input-group">
<div class="input-group-addon">{{ currency.symbol|raw }}</div>
<input step="any" class="form-control" id="amount" autocomplete="off" name="amount" max="{{ maxAmount|round(2) }}"
type="number"/>
</div>
{% for account in accounts %}
<strong>{{ account.account.name }} ({{ 'max_amount_add'|_ }}: {{ formatAmountByCurrency(piggyBank.transactionCurrency, account.max_amount) }})</strong>
<div class="input-group">
<div class="input-group-addon">{{ piggyBank.transactionCurrency.symbol|raw }}</div>
<input step="any" min="0" class="form-control" id="amount_{{ account.account.id }}" autocomplete="off" name="amount[{{ account.account.id }}]" max="{{ account.max_amount|round(piggyBank.transactionCurrency.decimal_places) }}" type="number"/>
</div>
{% endfor %}
<p>
&nbsp;
</p>

View File

@ -5,19 +5,17 @@
</button>
<h4 class="modal-title">{{ trans('firefly.add_money_to_piggy_title', {name: piggyBank.name}) }}</h4>
</div>
{% if maxAmount > 0 %}
{% if total > 0 %}
<form style="display: inline;" id="add" action="{{ route('piggy-banks.add', piggyBank.id) }}" method="POST">
<div class="modal-body">
<input type="hidden" name="_token" value="{{ csrf_token() }}"/>
<p>
{{ 'max_amount_add'|_ }}: {{ formatAmountByCurrency(currency,maxAmount) }}.
</p>
{% for account in accounts %}
<strong>{{ account.account.name }} ({{ 'max_amount_add'|_ }}: {{ formatAmountByCurrency(piggyBank.transactionCurrency, account.max_amount) }})</strong>
<div class="input-group">
<div class="input-group-addon">{{ currency.symbol|raw }}</div>
<input step="any" class="form-control" id="amount" autocomplete="off" name="amount" max="{{ maxAmount|round(currency.decimal_places) }}" type="number"/>
<div class="input-group-addon">{{ piggyBank.transactionCurrency.symbol|raw }}</div>
<input step="any" min="0" class="form-control" id="amount_{{ account.account.id }}" autocomplete="off" name="amount[{{ account.account.id }}]" max="{{ account.max_amount|round(piggyBank.transactionCurrency.decimal_places) }}" type="number"/>
</div>
{% endfor %}
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">{{ 'close'|_ }}</button>

View File

@ -17,7 +17,7 @@
<div class="box-body">
{{ ExpandedForm.text('name') }}
{{ ExpandedForm.amountNoCurrency('target_amount') }}
{{ CurrencyForm.currencyList('transaction_ currency_id', null, {helpText:'piggy_default_currency'|_}) }}
{{ CurrencyForm.currencyList('transaction_currency_id', null, {helpText:'piggy_default_currency'|_}) }}
{{ AccountForm.assetLiabilityMultiAccountList('accounts', preFilled.accounts, {label: 'saveOnAccounts'|_, helpText: 'piggy_account_currency_match'|_ }) }}
</div>

View File

@ -22,8 +22,9 @@
<div class="box-body">
{{ ExpandedForm.text('name') }}
{{ AccountForm.assetAccountList('account_id', null, {label: 'saveOnAccount'|_ }) }}
{{ ExpandedForm.amountNoCurrency('targetamount') }}
{{ ExpandedForm.amountNoCurrency('target_amount') }}
{{ CurrencyForm.currencyList('transaction_currency_id', null, {helpText:'piggy_default_currency'|_}) }}
{{ AccountForm.assetLiabilityMultiAccountList('accounts', preFilled.accounts, {label: 'saveOnAccounts'|_, helpText: 'piggy_account_currency_match'|_ }) }}
</div>
</div>

View File

@ -14,15 +14,17 @@
</div>
<div class="box-body">
<p>
{{ 'max_amount_remove'|_ }}: {{ formatAmountByCurrency(currency, repetition.currentamount) }}.
</p>
{% for account in accounts %}
<p>
{{ account.account.name }}: {{ 'max_amount_remove'|_ }}: {{ formatAmountByCurrency(piggyBank.transactionCurrency, account.saved_so_far) }}.
</p>
<div class="input-group">
<div class="input-group-addon">{{ piggyBank.transactionCurrency.symbol|raw }}</div>
<input step="any" class="form-control" id="amount_{{ account.account.id }}" autocomplete="off" name="amount[{{ account.account.id }}]" max="{{ account.saved_so_far }}"
type="number">
</div>
{% endfor %}
<div class="input-group">
<div class="input-group-addon">{{ currency.symbol|raw }}</div>
<input step="any" class="form-control" id="amount" autocomplete="off" name="amount" max="{{ repetition.currentamount }}"
type="number"/>
</div>
<p>
&nbsp;
</p>

View File

@ -10,15 +10,16 @@
</div>
<div class="modal-body">
{% for account in accounts %}
<p>
{{ 'max_amount_remove'|_ }}: {{ formatAmountByCurrency(currency, repetition.currentamount) }}.
{{ account.account.name }}: {{ 'max_amount_remove'|_ }}: {{ formatAmountByCurrency(piggyBank.transactionCurrency, account.saved_so_far) }}.
</p>
<div class="input-group">
<div class="input-group-addon">{{ currency.symbol|raw }}</div>
<input step="any" class="form-control" id="amount" autocomplete="off" name="amount" max="{{ repetition.currentamount }}"
<div class="input-group-addon">{{ piggyBank.transactionCurrency.symbol|raw }}</div>
<input step="any" class="form-control" id="amount_{{ account.account.id }}" autocomplete="off" name="amount[{{ account.account.id }}]" max="{{ account.saved_so_far }}"
type="number">
</div>
{% endfor %}
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">{{ 'close'|_ }}</button>

View File

@ -33,8 +33,12 @@
<div class="box-body no-padding">
<table class="table table-responsive table-hover" id="piggyDetails">
<tr>
<td style="width:40%;">{{ 'account'|_ }}</td>
<td><a href="{{ route('accounts.show', piggyBank.account_id) }}">{{ piggyBank.account.name }}</a></td>
<td style="width:40%;">{{ 'saveOnAccounts'|_ }}</td>
<td>
{% for account in piggy.accounts %}
<a href="{{ route('accounts.show', account.id) }}">{{ account.name }}</a><br>
{% endfor %}
</td>
</tr>
{% if piggy.object_group_title %}
<tr>