Compare commits

...

14 Commits

Author SHA1 Message Date
github-actions[bot]
9c195dcc66 Merge pull request #10416 from firefly-iii/release-1749057506
🤖 Automatically merge the PR into the develop branch.
2025-06-04 19:18:33 +02:00
JC5
0b45506e52 🤖 Auto commit for release 'develop' on 2025-06-04 2025-06-04 19:18:26 +02:00
James Cole
51e58f8d88 Fix #10413 2025-06-04 15:40:29 +02:00
James Cole
d0c658e79a Update message to include IP 2025-06-03 20:00:13 +02:00
github-actions[bot]
35e0791a9f Merge pull request #10411 from firefly-iii/release-1748971941
🤖 Automatically merge the PR into the develop branch.
2025-06-03 19:32:29 +02:00
JC5
637ecc66d2 🤖 Auto commit for release 'develop' on 2025-06-03 2025-06-03 19:32:21 +02:00
James Cole
3a38175b2a Fix renamed variable. 2025-06-03 19:26:07 +02:00
James Cole
d78fd86d7a Catch already linked transactions. 2025-06-02 20:46:09 +02:00
James Cole
395332d6dd Fix #10403 2025-06-02 20:32:58 +02:00
James Cole
c5cbceb81a Fix #10399 2025-06-02 05:39:43 +02:00
github-actions[bot]
ec94f1bcf1 Merge pull request #10401 from firefly-iii/release-1748835078
🤖 Automatically merge the PR into the develop branch.
2025-06-02 05:31:25 +02:00
JC5
ee3d18a8ea 🤖 Auto commit for release 'develop' on 2025-06-02 2025-06-02 05:31:18 +02:00
James Cole
a9cd8b6512 Better fit for run recalculations. 2025-06-01 14:00:35 +02:00
James Cole
5bd87036b0 Config works now. Does not feel solved yet, but still. 2025-06-01 13:32:18 +02:00
37 changed files with 318 additions and 188 deletions

View File

@@ -34,7 +34,6 @@ use FireflyIII\Support\JsonApi\Enrichments\TransactionGroupEnrichment;
use FireflyIII\Transformers\TransactionGroupTransformer; use FireflyIII\Transformers\TransactionGroupTransformer;
use FireflyIII\User; use FireflyIII\User;
use Illuminate\Http\JsonResponse; use Illuminate\Http\JsonResponse;
use Illuminate\Support\Facades\Log;
use League\Fractal\Resource\Item; use League\Fractal\Resource\Item;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
@@ -73,26 +72,24 @@ class UpdateController extends Controller
public function update(UpdateRequest $request, TransactionGroup $transactionGroup): JsonResponse public function update(UpdateRequest $request, TransactionGroup $transactionGroup): JsonResponse
{ {
app('log')->debug('Now in update routine for transaction group'); app('log')->debug('Now in update routine for transaction group');
$data = $request->getAll(); $data = $request->getAll();
$oldAmount = $this->groupRepository->getTotalAmount($transactionGroup); $oldHash = $this->groupRepository->getCompareHash($transactionGroup);
$transactionGroup = $this->groupRepository->update($transactionGroup, $data); $transactionGroup = $this->groupRepository->update($transactionGroup, $data);
$newAmount = $this->groupRepository->getTotalAmount($transactionGroup); $newHash = $this->groupRepository->getCompareHash($transactionGroup);
$manager = $this->getManager(); $manager = $this->getManager();
Log::debug(sprintf('Old amount: %s, new amount: %s', $oldAmount, $newAmount));
app('preferences')->mark(); app('preferences')->mark();
$applyRules = $data['apply_rules'] ?? true; $applyRules = $data['apply_rules'] ?? true;
$fireWebhooks = $data['fire_webhooks'] ?? true; $fireWebhooks = $data['fire_webhooks'] ?? true;
$amountChanged = 0 !== bccomp($oldAmount, $newAmount); $runRecalculations = $oldHash !== $newHash;
event(new UpdatedTransactionGroup($transactionGroup, $applyRules, $fireWebhooks, $amountChanged)); event(new UpdatedTransactionGroup($transactionGroup, $applyRules, $fireWebhooks, $runRecalculations));
/** @var User $admin */ /** @var User $admin */
$admin = auth()->user(); $admin = auth()->user();
// use new group collector: // use new group collector:
/** @var GroupCollectorInterface $collector */ /** @var GroupCollectorInterface $collector */
$collector = app(GroupCollectorInterface::class); $collector = app(GroupCollectorInterface::class);
$collector $collector
->setUser($admin) ->setUser($admin)
// filter on transaction group. // filter on transaction group.
@@ -101,20 +98,20 @@ class UpdateController extends Controller
->withAPIInformation() ->withAPIInformation()
; ;
$selectedGroup = $collector->getGroups()->first(); $selectedGroup = $collector->getGroups()->first();
if (null === $selectedGroup) { if (null === $selectedGroup) {
throw new NotFoundHttpException(); throw new NotFoundHttpException();
} }
// enrich // enrich
$enrichment = new TransactionGroupEnrichment(); $enrichment = new TransactionGroupEnrichment();
$enrichment->setUser($admin); $enrichment->setUser($admin);
$selectedGroup = $enrichment->enrichSingle($selectedGroup); $selectedGroup = $enrichment->enrichSingle($selectedGroup);
/** @var TransactionGroupTransformer $transformer */ /** @var TransactionGroupTransformer $transformer */
$transformer = app(TransactionGroupTransformer::class); $transformer = app(TransactionGroupTransformer::class);
$transformer->setParameters($this->parameters); $transformer->setParameters($this->parameters);
$resource = new Item($selectedGroup, $transformer, 'transactions'); $resource = new Item($selectedGroup, $transformer, 'transactions');
return response()->json($manager->createData($resource)->toArray())->header('Content-Type', self::CONTENT_TYPE); return response()->json($manager->createData($resource)->toArray())->header('Content-Type', self::CONTENT_TYPE);
} }

View File

@@ -65,29 +65,29 @@ class UpdateController extends Controller
public function update(UpdateRequest $request, TransactionGroup $transactionGroup): JsonResponse public function update(UpdateRequest $request, TransactionGroup $transactionGroup): JsonResponse
{ {
app('log')->debug('Now in update routine for transaction group [v2]!'); app('log')->debug('Now in update routine for transaction group [v2]!');
$data = $request->getAll(); $data = $request->getAll();
$transactionGroup = $this->groupRepository->update($transactionGroup, $data); $transactionGroup = $this->groupRepository->update($transactionGroup, $data);
$applyRules = $data['apply_rules'] ?? true; $applyRules = $data['apply_rules'] ?? true;
$fireWebhooks = $data['fire_webhooks'] ?? true; $fireWebhooks = $data['fire_webhooks'] ?? true;
$amountChanged = true; $runRecalculations = true;
event(new UpdatedTransactionGroup($transactionGroup, $applyRules, $fireWebhooks, $amountChanged)); event(new UpdatedTransactionGroup($transactionGroup, $applyRules, $fireWebhooks, $runRecalculations));
app('preferences')->mark(); app('preferences')->mark();
/** @var User $admin */ /** @var User $admin */
$admin = auth()->user(); $admin = auth()->user();
// use new group collector: // use new group collector:
/** @var GroupCollectorInterface $collector */ /** @var GroupCollectorInterface $collector */
$collector = app(GroupCollectorInterface::class); $collector = app(GroupCollectorInterface::class);
$collector->setUser($admin)->setTransactionGroup($transactionGroup); $collector->setUser($admin)->setTransactionGroup($transactionGroup);
$selectedGroup = $collector->getGroups()->first(); $selectedGroup = $collector->getGroups()->first();
if (null === $selectedGroup) { if (null === $selectedGroup) {
throw new FireflyException('200032: Cannot find transaction. Possibly, a rule deleted this transaction after its creation.'); throw new FireflyException('200032: Cannot find transaction. Possibly, a rule deleted this transaction after its creation.');
} }
$transformer = new TransactionGroupTransformer(); $transformer = new TransactionGroupTransformer();
$transformer->setParameters($this->parameters); $transformer->setParameters($this->parameters);
return response()->api($this->jsonApiObject('transactions', $selectedGroup, $transformer))->header('Content-Type', self::CONTENT_TYPE); return response()->api($this->jsonApiObject('transactions', $selectedGroup, $transformer))->header('Content-Type', self::CONTENT_TYPE);

View File

@@ -58,6 +58,7 @@ class CorrectsGroupAccounts extends Command
$handler = new UpdatedGroupEventHandler(); $handler = new UpdatedGroupEventHandler();
foreach ($groups as $groupId) { foreach ($groups as $groupId) {
$group = TransactionGroup::find($groupId); $group = TransactionGroup::find($groupId);
// TODO in theory the "unifyAccounts" method could lead to the need for run recalculations.
$event = new UpdatedTransactionGroup($group, true, true, false); $event = new UpdatedTransactionGroup($group, true, true, false);
$handler->unifyAccounts($event); $handler->unifyAccounts($event);
} }

View File

@@ -0,0 +1,16 @@
<?php
declare(strict_types=1);
namespace FireflyIII\Events\Model\PiggyBank;
use FireflyIII\Events\Event;
use FireflyIII\Models\PiggyBank;
use Illuminate\Queue\SerializesModels;
class ChangedName extends Event
{
use SerializesModels;
public function __construct(public PiggyBank $piggyBank, public string $oldName, public string $newName) {}
}

View File

@@ -37,5 +37,5 @@ class UpdatedTransactionGroup extends Event
/** /**
* Create a new event instance. * Create a new event instance.
*/ */
public function __construct(public TransactionGroup $transactionGroup, public bool $applyRules, public bool $fireWebhooks, public bool $amountChanged) {} public function __construct(public TransactionGroup $transactionGroup, public bool $applyRules, public bool $fireWebhooks, public bool $runRecalculations) {}
} }

View File

@@ -24,6 +24,10 @@ declare(strict_types=1);
namespace FireflyIII\Handlers\Events\Model; namespace FireflyIII\Handlers\Events\Model;
use FireflyIII\Events\Model\PiggyBank\ChangedName;
use FireflyIII\Models\Account;
use FireflyIII\Models\Rule;
use FireflyIII\Models\RuleAction;
use FireflyIII\Models\TransactionGroup; use FireflyIII\Models\TransactionGroup;
use FireflyIII\Events\Model\PiggyBank\ChangedAmount; use FireflyIII\Events\Model\PiggyBank\ChangedAmount;
use FireflyIII\Models\PiggyBankEvent; use FireflyIII\Models\PiggyBankEvent;
@@ -33,6 +37,24 @@ use FireflyIII\Models\PiggyBankEvent;
*/ */
class PiggyBankEventHandler class PiggyBankEventHandler
{ {
public function changedPiggyBankName(ChangedName $event): void
{
// loop all accounts, collect all user's rules.
/** @var Account $account */
foreach ($event->piggyBank->accounts as $account) {
/** @var Rule $rule */
foreach ($account->user->rules as $rule) {
/** @var RuleAction $ruleAction */
foreach ($rule->ruleActions()->where('action_type', 'update_piggy')->get() as $ruleAction) {
if ($event->oldName === $ruleAction->action_value) {
$ruleAction->action_value = $event->newName;
$ruleAction->save();
}
}
}
}
}
public function changePiggyAmount(ChangedAmount $event): void public function changePiggyAmount(ChangedAmount $event): void
{ {
// find journal if group is present. // find journal if group is present.

View File

@@ -49,7 +49,7 @@ class UpdatedGroupEventHandler
$this->processRules($event); $this->processRules($event);
$this->recalculateCredit($event); $this->recalculateCredit($event);
$this->triggerWebhooks($event); $this->triggerWebhooks($event);
if ($event->amountChanged) { if ($event->runRecalculations) {
$this->updateRunningBalance($event); $this->updateRunningBalance($event);
} }

View File

@@ -75,13 +75,22 @@ class AmountController extends Controller
$totalSaved = $this->piggyRepos->getCurrentAmount($piggyBank); $totalSaved = $this->piggyRepos->getCurrentAmount($piggyBank);
foreach ($piggyBank->accounts as $account) { foreach ($piggyBank->accounts as $account) {
$leftOnAccount = $this->piggyRepos->leftOnAccount($piggyBank, $account, $date); $leftOnAccount = $this->piggyRepos->leftOnAccount($piggyBank, $account, $date);
$savedSoFar = $this->piggyRepos->getCurrentAmount($piggyBank, $account); $leftToSave = bcsub($piggyBank->target_amount, $totalSaved);
$leftToSave = bcsub($piggyBank->target_amount, $savedSoFar);
$maxAmount = 0 === bccomp($piggyBank->target_amount, '0') ? $leftOnAccount : min($leftOnAccount, $leftToSave); $maxAmount = 0 === bccomp($piggyBank->target_amount, '0') ? $leftOnAccount : min($leftOnAccount, $leftToSave);
Log::debug(sprintf(
'Account "%s", left on account "%s", saved so far "%s", left to save "%s", max amount "%s".',
$account->name,
$leftOnAccount,
$totalSaved,
$leftToSave,
$maxAmount,
));
$accounts[] = [ $accounts[] = [
'account' => $account, 'account' => $account,
'left_on_account' => $leftOnAccount, 'left_on_account' => $leftOnAccount,
'saved_so_far' => $savedSoFar, 'total_saved' => $totalSaved,
'left_to_save' => $leftToSave, 'left_to_save' => $leftToSave,
'max_amount' => $maxAmount, 'max_amount' => $maxAmount,
]; ];
@@ -100,18 +109,18 @@ class AmountController extends Controller
public function addMobile(PiggyBank $piggyBank) public function addMobile(PiggyBank $piggyBank)
{ {
/** @var Carbon $date */ /** @var Carbon $date */
$date = session('end', today(config('app.timezone'))); $date = session('end', today(config('app.timezone')));
$accounts = []; $accounts = [];
$total = '0'; $total = '0';
$totalSaved = $this->piggyRepos->getCurrentAmount($piggyBank);
foreach ($piggyBank->accounts as $account) { foreach ($piggyBank->accounts as $account) {
$leftOnAccount = $this->piggyRepos->leftOnAccount($piggyBank, $account, $date); $leftOnAccount = $this->piggyRepos->leftOnAccount($piggyBank, $account, $date);
$savedSoFar = $this->piggyRepos->getCurrentAmount($piggyBank, $account); $leftToSave = bcsub($piggyBank->target_amount, $totalSaved);
$leftToSave = bcsub($piggyBank->target_amount, $savedSoFar);
$maxAmount = 0 === bccomp($piggyBank->target_amount, '0') ? $leftOnAccount : min($leftOnAccount, $leftToSave); $maxAmount = 0 === bccomp($piggyBank->target_amount, '0') ? $leftOnAccount : min($leftOnAccount, $leftToSave);
$accounts[] = [ $accounts[] = [
'account' => $account, 'account' => $account,
'left_on_account' => $leftOnAccount, 'left_on_account' => $leftOnAccount,
'saved_so_far' => $savedSoFar, 'total_saved' => $totalSaved,
'left_to_save' => $leftToSave, 'left_to_save' => $leftToSave,
'max_amount' => $maxAmount, 'max_amount' => $maxAmount,
]; ];

View File

@@ -63,6 +63,14 @@ class EditController extends Controller
); );
} }
public function resetHistory(PiggyBank $piggyBank): RedirectResponse
{
$this->piggyRepos->resetHistory($piggyBank);
session()->flash('success', (string) trans('firefly.piggy_history_reset'));
return redirect(route('piggy-banks.show', [$piggyBank->id]));
}
/** /**
* Edit a piggy bank. * Edit a piggy bank.
* *

View File

@@ -292,7 +292,7 @@ class ConvertController extends Controller
$group->refresh(); $group->refresh();
session()->flash('success', (string) trans('firefly.converted_to_'.$destinationType->type)); session()->flash('success', (string) trans('firefly.converted_to_'.$destinationType->type));
event(new UpdatedTransactionGroup($group, true, true, false)); event(new UpdatedTransactionGroup($group, true, true, true));
return redirect(route('transactions.show', [$group->id])); return redirect(route('transactions.show', [$group->id]));
} }

View File

@@ -191,15 +191,15 @@ class MassController extends Controller
*/ */
private function updateJournal(int $journalId, MassEditJournalRequest $request): void private function updateJournal(int $journalId, MassEditJournalRequest $request): void
{ {
$journal = $this->repository->find($journalId); $journal = $this->repository->find($journalId);
if (!$journal instanceof TransactionJournal) { if (!$journal instanceof TransactionJournal) {
throw new FireflyException(sprintf('Trying to edit non-existent or deleted journal #%d', $journalId)); throw new FireflyException(sprintf('Trying to edit non-existent or deleted journal #%d', $journalId));
} }
$service = app(JournalUpdateService::class); $service = app(JournalUpdateService::class);
// for each field, call the update service. // for each field, call the update service.
$service->setTransactionJournal($journal); $service->setTransactionJournal($journal);
$data = [ $data = [
'date' => $this->getDateFromRequest($request, $journal->id, 'date'), 'date' => $this->getDateFromRequest($request, $journal->id, 'date'),
'description' => $this->getStringFromRequest($request, $journal->id, 'description'), 'description' => $this->getStringFromRequest($request, $journal->id, 'description'),
'source_id' => $this->getIntFromRequest($request, $journal->id, 'source_id'), 'source_id' => $this->getIntFromRequest($request, $journal->id, 'source_id'),
@@ -217,8 +217,8 @@ class MassController extends Controller
$service->setData($data); $service->setData($data);
$service->update(); $service->update();
// trigger rules // trigger rules
$amountChanged = $service->isAmountChanged(); $runRecalculations = $service->isCompareHashChanged();
event(new UpdatedTransactionGroup($journal->transactionGroup, true, true, $amountChanged)); event(new UpdatedTransactionGroup($journal->transactionGroup, true, true, $runRecalculations));
} }
private function getDateFromRequest(MassEditJournalRequest $request, int $journalId, string $key): ?Carbon private function getDateFromRequest(MassEditJournalRequest $request, int $journalId, string $key): ?Carbon

View File

@@ -24,7 +24,6 @@ declare(strict_types=1);
namespace FireflyIII\Http\Requests; namespace FireflyIII\Http\Requests;
use Illuminate\Contracts\Validation\Validator; use Illuminate\Contracts\Validation\Validator;
use Carbon\Carbon;
use FireflyIII\Support\Request\ChecksLogin; use FireflyIII\Support\Request\ChecksLogin;
use Illuminate\Foundation\Http\FormRequest; use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Support\Facades\Log; use Illuminate\Support\Facades\Log;
@@ -41,15 +40,9 @@ class SelectTransactionsRequest extends FormRequest
*/ */
public function rules(): array public function rules(): array
{ {
// fixed
/** @var Carbon $sessionFirst */
$sessionFirst = clone session('first');
$first = $sessionFirst->subDay()->format('Y-m-d');
$today = today(config('app.timezone'))->addDay()->format('Y-m-d');
return [ return [
'start' => 'required|date|after:'.$first, 'start' => 'required|date|after:1900-01-01|before:2099-12-31|before:end|required_with:end',
'end' => 'required|date|before:'.$today, 'end' => 'required|date|after:1900-01-01|before:2099-12-31|after:start|required_with:start',
'accounts' => 'required', 'accounts' => 'required',
'accounts.*' => 'required|exists:accounts,id|belongsToUser:accounts', 'accounts.*' => 'required|exists:accounts,id|belongsToUser:accounts',
]; ];

View File

@@ -67,9 +67,10 @@ class UserFailedLoginAttempt extends Notification
{ {
$settings = ReturnsSettings::getSettings('ntfy', 'user', $notifiable); $settings = ReturnsSettings::getSettings('ntfy', 'user', $notifiable);
$message = new Message(); $message = new Message();
$ip = Request::ip();
$message->topic($settings['ntfy_topic']); $message->topic($settings['ntfy_topic']);
$message->title((string) trans('email.failed_login_subject')); $message->title((string) trans('email.failed_login_subject'));
$message->body((string) trans('email.failed_login_message', ['email' => $this->user->email])); $message->body((string) trans('email.failed_login_message', ['ip' => $ip, 'email' => $this->user->email]));
return $message; return $message;
} }
@@ -79,7 +80,9 @@ class UserFailedLoginAttempt extends Notification
*/ */
public function toPushover(User $notifiable): PushoverMessage public function toPushover(User $notifiable): PushoverMessage
{ {
return PushoverMessage::create((string) trans('email.failed_login_message', ['email' => $this->user->email])) $ip = Request::ip();
return PushoverMessage::create((string) trans('email.failed_login_message', ['ip' => $ip, 'email' => $this->user->email]))
->title((string) trans('email.failed_login_subject')) ->title((string) trans('email.failed_login_subject'))
; ;
} }
@@ -89,7 +92,8 @@ class UserFailedLoginAttempt extends Notification
*/ */
public function toSlack(User $notifiable): SlackMessage public function toSlack(User $notifiable): SlackMessage
{ {
$message = (string) trans('email.failed_login_message', ['email' => $this->user->email]); $ip = Request::ip();
$message = (string) trans('email.failed_login_message', ['ip' => $ip, 'email' => $this->user->email]);
return new SlackMessage()->content($message); return new SlackMessage()->content($message);
} }

View File

@@ -31,6 +31,7 @@ use FireflyIII\Events\Model\BudgetLimit\Created;
use FireflyIII\Events\Model\BudgetLimit\Deleted; use FireflyIII\Events\Model\BudgetLimit\Deleted;
use FireflyIII\Events\Model\BudgetLimit\Updated; use FireflyIII\Events\Model\BudgetLimit\Updated;
use FireflyIII\Events\Model\PiggyBank\ChangedAmount; use FireflyIII\Events\Model\PiggyBank\ChangedAmount;
use FireflyIII\Events\Model\PiggyBank\ChangedName;
use FireflyIII\Events\Model\Rule\RuleActionFailedOnArray; use FireflyIII\Events\Model\Rule\RuleActionFailedOnArray;
use FireflyIII\Events\Model\Rule\RuleActionFailedOnObject; use FireflyIII\Events\Model\Rule\RuleActionFailedOnObject;
use FireflyIII\Events\NewVersionAvailable; use FireflyIII\Events\NewVersionAvailable;
@@ -210,6 +211,9 @@ class EventServiceProvider extends ServiceProvider
ChangedAmount::class => [ ChangedAmount::class => [
'FireflyIII\Handlers\Events\Model\PiggyBankEventHandler@changePiggyAmount', 'FireflyIII\Handlers\Events\Model\PiggyBankEventHandler@changePiggyAmount',
], ],
ChangedName::class => [
'FireflyIII\Handlers\Events\Model\PiggyBankEventHandler@changedPiggyBankName',
],
// budget related events: CRUD budget limit // budget related events: CRUD budget limit
Created::class => [ Created::class => [

View File

@@ -25,6 +25,7 @@ declare(strict_types=1);
namespace FireflyIII\Repositories\PiggyBank; namespace FireflyIII\Repositories\PiggyBank;
use FireflyIII\Events\Model\PiggyBank\ChangedAmount; use FireflyIII\Events\Model\PiggyBank\ChangedAmount;
use FireflyIII\Events\Model\PiggyBank\ChangedName;
use FireflyIII\Exceptions\FireflyException; use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Factory\PiggyBankFactory; use FireflyIII\Factory\PiggyBankFactory;
use FireflyIII\Models\Account; use FireflyIII\Models\Account;
@@ -66,7 +67,7 @@ trait ModifiesPiggyBanks
{ {
$currentAmount = $this->getCurrentAmount($piggyBank, $account); $currentAmount = $this->getCurrentAmount($piggyBank, $account);
$pivot = $piggyBank->accounts()->where('accounts.id', $account->id)->first()->pivot; $pivot = $piggyBank->accounts()->where('accounts.id', $account->id)->first()->pivot;
$pivot->current_amount = bcsub((string) $currentAmount, $amount); $pivot->current_amount = bcsub((string)$currentAmount, $amount);
$pivot->native_current_amount = null; $pivot->native_current_amount = null;
// also update native_current_amount. // also update native_current_amount.
@@ -89,7 +90,7 @@ trait ModifiesPiggyBanks
{ {
$currentAmount = $this->getCurrentAmount($piggyBank, $account); $currentAmount = $this->getCurrentAmount($piggyBank, $account);
$pivot = $piggyBank->accounts()->where('accounts.id', $account->id)->first()->pivot; $pivot = $piggyBank->accounts()->where('accounts.id', $account->id)->first()->pivot;
$pivot->current_amount = bcadd((string) $currentAmount, $amount); $pivot->current_amount = bcadd((string)$currentAmount, $amount);
$pivot->native_current_amount = null; $pivot->native_current_amount = null;
// also update native_current_amount. // also update native_current_amount.
@@ -121,13 +122,13 @@ trait ModifiesPiggyBanks
if (0 !== bccomp($piggyBank->target_amount, '0')) { if (0 !== bccomp($piggyBank->target_amount, '0')) {
$leftToSave = bcsub($piggyBank->target_amount, (string) $savedSoFar); $leftToSave = bcsub($piggyBank->target_amount, (string)$savedSoFar);
$maxAmount = 1 === bccomp((string) $leftOnAccount, $leftToSave) ? $leftToSave : $leftOnAccount; $maxAmount = 1 === bccomp((string)$leftOnAccount, $leftToSave) ? $leftToSave : $leftOnAccount;
Log::debug(sprintf('Left to save: %s', $leftToSave)); Log::debug(sprintf('Left to save: %s', $leftToSave));
Log::debug(sprintf('Maximum amount: %s', $maxAmount)); Log::debug(sprintf('Maximum amount: %s', $maxAmount));
} }
$compare = bccomp($amount, (string) $maxAmount); $compare = bccomp($amount, (string)$maxAmount);
$result = $compare <= 0; $result = $compare <= 0;
Log::debug(sprintf('Compare <= 0? %d, so canAddAmount is %s', $compare, var_export($result, true))); Log::debug(sprintf('Compare <= 0? %d, so canAddAmount is %s', $compare, var_export($result, true)));
@@ -139,7 +140,7 @@ trait ModifiesPiggyBanks
{ {
$savedSoFar = $this->getCurrentAmount($piggyBank, $account); $savedSoFar = $this->getCurrentAmount($piggyBank, $account);
return bccomp($amount, (string) $savedSoFar) <= 0; return bccomp($amount, (string)$savedSoFar) <= 0;
} }
/** /**
@@ -170,7 +171,7 @@ trait ModifiesPiggyBanks
if (1 === bccomp($amount, $max) && 0 !== bccomp($piggyBank->target_amount, '0')) { if (1 === bccomp($amount, $max) && 0 !== bccomp($piggyBank->target_amount, '0')) {
$amount = $max; $amount = $max;
} }
$difference = bcsub($amount, (string) $repetition->current_amount); $difference = bcsub($amount, (string)$repetition->current_amount);
$repetition->current_amount = $amount; $repetition->current_amount = $amount;
$repetition->save(); $repetition->save();
@@ -211,12 +212,12 @@ trait ModifiesPiggyBanks
{ {
$piggyBank = $this->updateProperties($piggyBank, $data); $piggyBank = $this->updateProperties($piggyBank, $data);
if (array_key_exists('notes', $data)) { if (array_key_exists('notes', $data)) {
$this->updateNote($piggyBank, (string) $data['notes']); $this->updateNote($piggyBank, (string)$data['notes']);
} }
// update the order of the piggy bank: // update the order of the piggy bank:
$oldOrder = $piggyBank->order; $oldOrder = $piggyBank->order;
$newOrder = (int) ($data['order'] ?? $oldOrder); $newOrder = (int)($data['order'] ?? $oldOrder);
if ($oldOrder !== $newOrder) { if ($oldOrder !== $newOrder) {
$this->setOrder($piggyBank, $newOrder); $this->setOrder($piggyBank, $newOrder);
} }
@@ -233,9 +234,9 @@ trait ModifiesPiggyBanks
// if the piggy bank is now smaller than the sum of the money saved, // if the piggy bank is now smaller than the sum of the money saved,
// remove money from all accounts until the piggy bank is the right amount. // remove money from all accounts until the piggy bank is the right amount.
$currentAmount = $this->getCurrentAmount($piggyBank); $currentAmount = $this->getCurrentAmount($piggyBank);
if (1 === bccomp((string) $currentAmount, (string) $piggyBank->target_amount) && 0 !== bccomp((string) $piggyBank->target_amount, '0')) { if (1 === bccomp((string)$currentAmount, (string)$piggyBank->target_amount) && 0 !== bccomp((string)$piggyBank->target_amount, '0')) {
Log::debug(sprintf('Current amount is %s, target amount is %s', $currentAmount, $piggyBank->target_amount)); Log::debug(sprintf('Current amount is %s, target amount is %s', $currentAmount, $piggyBank->target_amount));
$difference = bcsub((string) $piggyBank->target_amount, (string) $currentAmount); $difference = bcsub((string)$piggyBank->target_amount, (string)$currentAmount);
// an amount will be removed, create "negative" event: // an amount will be removed, create "negative" event:
// Log::debug(sprintf('ChangedAmount: is triggered with difference "%s"', $difference)); // Log::debug(sprintf('ChangedAmount: is triggered with difference "%s"', $difference));
@@ -248,7 +249,7 @@ trait ModifiesPiggyBanks
// update using name: // update using name:
if (array_key_exists('object_group_title', $data)) { if (array_key_exists('object_group_title', $data)) {
$objectGroupTitle = (string) $data['object_group_title']; $objectGroupTitle = (string)$data['object_group_title'];
if ('' !== $objectGroupTitle) { if ('' !== $objectGroupTitle) {
$objectGroup = $this->findOrCreateObjectGroup($objectGroupTitle); $objectGroup = $this->findOrCreateObjectGroup($objectGroupTitle);
if (null !== $objectGroup) { if (null !== $objectGroup) {
@@ -264,7 +265,7 @@ trait ModifiesPiggyBanks
// try also with ID: // try also with ID:
if (array_key_exists('object_group_id', $data)) { if (array_key_exists('object_group_id', $data)) {
$objectGroupId = (int) ($data['object_group_id'] ?? 0); $objectGroupId = (int)($data['object_group_id'] ?? 0);
if (0 !== $objectGroupId) { if (0 !== $objectGroupId) {
$objectGroup = $this->findObjectGroupById($objectGroupId); $objectGroup = $this->findObjectGroupById($objectGroupId);
if (null !== $objectGroup) { if (null !== $objectGroup) {
@@ -282,6 +283,7 @@ trait ModifiesPiggyBanks
private function updateProperties(PiggyBank $piggyBank, array $data): PiggyBank private function updateProperties(PiggyBank $piggyBank, array $data): PiggyBank
{ {
if (array_key_exists('name', $data) && '' !== $data['name']) { if (array_key_exists('name', $data) && '' !== $data['name']) {
event(new ChangedName($piggyBank, $piggyBank->name, $data['name']));
$piggyBank->name = $data['name']; $piggyBank->name = $data['name'];
} }
if (array_key_exists('transaction_currency_id', $data) && is_int($data['transaction_currency_id'])) { if (array_key_exists('transaction_currency_id', $data) && is_int($data['transaction_currency_id'])) {
@@ -366,14 +368,14 @@ trait ModifiesPiggyBanks
foreach ($piggyBank->accounts as $account) { foreach ($piggyBank->accounts as $account) {
$current = $account->pivot->current_amount; $current = $account->pivot->current_amount;
// if this account contains more than the amount, remove the amount and return. // if this account contains more than the amount, remove the amount and return.
if (1 === bccomp((string) $current, $amount)) { if (1 === bccomp((string)$current, $amount)) {
$this->removeAmount($piggyBank, $account, $amount); $this->removeAmount($piggyBank, $account, $amount);
return; return;
} }
// if this account contains less than the amount, remove the current amount, update the amount and continue. // if this account contains less than the amount, remove the current amount, update the amount and continue.
$this->removeAmount($piggyBank, $account, $current); $this->removeAmount($piggyBank, $account, $current);
$amount = bcsub($amount, (string) $current); $amount = bcsub($amount, (string)$current);
} }
} }
} }

View File

@@ -23,6 +23,7 @@ declare(strict_types=1);
namespace FireflyIII\Repositories\PiggyBank; namespace FireflyIII\Repositories\PiggyBank;
use FireflyIII\Events\Model\PiggyBank\ChangedAmount;
use FireflyIII\User; use FireflyIII\User;
use Carbon\Carbon; use Carbon\Carbon;
use FireflyIII\Exceptions\FireflyException; use FireflyIII\Exceptions\FireflyException;
@@ -438,4 +439,14 @@ class PiggyBankRepository implements PiggyBankRepositoryInterface, UserGroupInte
return $search->take($limit)->get(['piggy_banks.*']); return $search->take($limit)->get(['piggy_banks.*']);
} }
public function resetHistory(PiggyBank $piggyBank): void
{
$piggyBank->piggyBankEvents()->delete();
foreach ($piggyBank->accounts as $account) {
if (0 !== bccomp('0', $account->pivot->current_amount)) {
event(new ChangedAmount($piggyBank, $account->pivot->current_amount, null, null));
}
}
}
} }

View File

@@ -47,6 +47,8 @@ use Illuminate\Support\Collection;
*/ */
interface PiggyBankRepositoryInterface interface PiggyBankRepositoryInterface
{ {
public function resetHistory(PiggyBank $piggyBank): void;
public function addAmount(PiggyBank $piggyBank, Account $account, string $amount, ?TransactionJournal $journal = null): bool; public function addAmount(PiggyBank $piggyBank, Account $account, string $amount, ?TransactionJournal $journal = null): bool;
public function addAmountToPiggyBank(PiggyBank $piggyBank, string $amount, TransactionJournal $journal): void; public function addAmountToPiggyBank(PiggyBank $piggyBank, string $amount, TransactionJournal $journal): void;

View File

@@ -217,7 +217,7 @@ class TransactionGroupRepository implements TransactionGroupRepositoryInterface,
'link' => $entry->outward, 'link' => $entry->outward,
'group' => $entry->destination->transaction_group_id, 'group' => $entry->destination->transaction_group_id,
'description' => $entry->destination->description, 'description' => $entry->destination->description,
'editable' => 1 === (int) $entry->editable, 'editable' => 1 === (int)$entry->editable,
'amount' => $amount, 'amount' => $amount,
'foreign_amount' => $foreignAmount, 'foreign_amount' => $foreignAmount,
]; ];
@@ -230,7 +230,7 @@ class TransactionGroupRepository implements TransactionGroupRepositoryInterface,
'link' => $entry->inward, 'link' => $entry->inward,
'group' => $entry->source->transaction_group_id, 'group' => $entry->source->transaction_group_id,
'description' => $entry->source->description, 'description' => $entry->source->description,
'editable' => 1 === (int) $entry->editable, 'editable' => 1 === (int)$entry->editable,
'amount' => $amount, 'amount' => $amount,
'foreign_amount' => $foreignAmount, 'foreign_amount' => $foreignAmount,
]; ];
@@ -264,7 +264,7 @@ class TransactionGroupRepository implements TransactionGroupRepositoryInterface,
if (null === $transaction->foreign_amount || '' === $transaction->foreign_amount) { if (null === $transaction->foreign_amount || '' === $transaction->foreign_amount) {
return ''; return '';
} }
if (0 === bccomp('0', (string) $transaction->foreign_amount)) { if (0 === bccomp('0', (string)$transaction->foreign_amount)) {
return ''; return '';
} }
$currency = $transaction->foreignCurrency; $currency = $transaction->foreignCurrency;
@@ -305,7 +305,7 @@ class TransactionGroupRepository implements TransactionGroupRepositoryInterface,
$return = []; $return = [];
foreach ($query as $row) { foreach ($query as $row) {
$return[$row->name] = new Carbon(json_decode((string) $row->data, true, 512, JSON_THROW_ON_ERROR)); $return[$row->name] = new Carbon(json_decode((string)$row->data, true, 512, JSON_THROW_ON_ERROR));
} }
return new NullArrayObject($return); return new NullArrayObject($return);
@@ -325,7 +325,7 @@ class TransactionGroupRepository implements TransactionGroupRepositoryInterface,
$return = []; $return = [];
foreach ($query as $row) { foreach ($query as $row) {
$return[$row->name] = json_decode((string) $row->data); $return[$row->name] = json_decode((string)$row->data);
} }
return new NullArrayObject($return); return new NullArrayObject($return);
@@ -433,20 +433,22 @@ class TransactionGroupRepository implements TransactionGroupRepositoryInterface,
return $service->update($transactionGroup, $data); return $service->update($transactionGroup, $data);
} }
public function getTotalAmount(TransactionGroup $group): string public function getCompareHash(TransactionGroup $group): string
{ {
$sum = '0'; $sum = '0';
$names = '';
/** @var TransactionJournal $journal */ /** @var TransactionJournal $journal */
foreach ($group->transactionJournals as $journal) { foreach ($group->transactionJournals as $journal) {
/** @var Transaction $transaction */ /** @var Transaction $transaction */
foreach ($journal->transactions as $transaction) { foreach ($journal->transactions as $transaction) {
if (-1 === bccomp('0', (string) $transaction->amount)) { if (-1 === bccomp('0', (string)$transaction->amount)) {
$sum = bcadd($sum, $transaction->amount); $sum = bcadd($sum, $transaction->amount);
$names = sprintf('%s%s', $names, $transaction->account->name);
} }
} }
} }
return $sum; return hash('sha256', sprintf('%s-%s', $names, $sum));
} }
} }

View File

@@ -49,7 +49,10 @@ interface TransactionGroupRepositoryInterface
{ {
public function countAttachments(int $journalId): int; public function countAttachments(int $journalId): int;
public function getTotalAmount(TransactionGroup $group): string; /**
* Small method that returns a hash that can be used to compare two transaction groups.
*/
public function getCompareHash(TransactionGroup $group): string;
public function destroy(TransactionGroup $group): void; public function destroy(TransactionGroup $group): void;

View File

@@ -43,6 +43,7 @@ use FireflyIII\Repositories\Bill\BillRepositoryInterface;
use FireflyIII\Repositories\Budget\BudgetRepositoryInterface; use FireflyIII\Repositories\Budget\BudgetRepositoryInterface;
use FireflyIII\Repositories\Category\CategoryRepositoryInterface; use FireflyIII\Repositories\Category\CategoryRepositoryInterface;
use FireflyIII\Repositories\Currency\CurrencyRepositoryInterface; use FireflyIII\Repositories\Currency\CurrencyRepositoryInterface;
use FireflyIII\Repositories\TransactionGroup\TransactionGroupRepositoryInterface;
use FireflyIII\Services\Internal\Support\JournalServiceTrait; use FireflyIII\Services\Internal\Support\JournalServiceTrait;
use FireflyIII\Support\Facades\FireflyConfig; use FireflyIII\Support\Facades\FireflyConfig;
use FireflyIII\Support\NullArrayObject; use FireflyIII\Support\NullArrayObject;
@@ -60,6 +61,7 @@ class JournalUpdateService
private BillRepositoryInterface $billRepository; private BillRepositoryInterface $billRepository;
private CurrencyRepositoryInterface $currencyRepository; private CurrencyRepositoryInterface $currencyRepository;
private TransactionGroupRepositoryInterface $transactionGroupRepository;
private array $data; private array $data;
private ?Account $destinationAccount; private ?Account $destinationAccount;
private ?Transaction $destinationTransaction; private ?Transaction $destinationTransaction;
@@ -69,26 +71,27 @@ class JournalUpdateService
private ?Transaction $sourceTransaction; private ?Transaction $sourceTransaction;
private ?TransactionGroup $transactionGroup; private ?TransactionGroup $transactionGroup;
private ?TransactionJournal $transactionJournal; private ?TransactionJournal $transactionJournal;
private bool $amountChanged = false; private string $startCompareHash = '';
/** /**
* JournalUpdateService constructor. * JournalUpdateService constructor.
*/ */
public function __construct() public function __construct()
{ {
$this->destinationAccount = null; $this->destinationAccount = null;
$this->destinationTransaction = null; $this->destinationTransaction = null;
$this->sourceAccount = null; $this->sourceAccount = null;
$this->sourceTransaction = null; $this->sourceTransaction = null;
$this->transactionGroup = null; $this->transactionGroup = null;
$this->transactionJournal = null; $this->transactionJournal = null;
$this->billRepository = app(BillRepositoryInterface::class); $this->billRepository = app(BillRepositoryInterface::class);
$this->categoryRepository = app(CategoryRepositoryInterface::class); $this->categoryRepository = app(CategoryRepositoryInterface::class);
$this->budgetRepository = app(BudgetRepositoryInterface::class); $this->budgetRepository = app(BudgetRepositoryInterface::class);
$this->tagFactory = app(TagFactory::class); $this->tagFactory = app(TagFactory::class);
$this->accountRepository = app(AccountRepositoryInterface::class); $this->accountRepository = app(AccountRepositoryInterface::class);
$this->currencyRepository = app(CurrencyRepositoryInterface::class); $this->currencyRepository = app(CurrencyRepositoryInterface::class);
$this->metaString = [ $this->transactionGroupRepository = app(TransactionGroupRepositoryInterface::class);
$this->metaString = [
'sepa_cc', 'sepa_cc',
'sepa_ct_op', 'sepa_ct_op',
'sepa_ct_id', 'sepa_ct_id',
@@ -103,7 +106,7 @@ class JournalUpdateService
'external_id', 'external_id',
'external_url', 'external_url',
]; ];
$this->metaDate = ['interest_date', 'book_date', 'process_date', 'due_date', 'payment_date', $this->metaDate = ['interest_date', 'book_date', 'process_date', 'due_date', 'payment_date',
'invoice_date', ]; 'invoice_date', ];
} }
@@ -120,10 +123,12 @@ class JournalUpdateService
$this->budgetRepository->setUser($transactionGroup->user); $this->budgetRepository->setUser($transactionGroup->user);
$this->tagFactory->setUser($transactionGroup->user); $this->tagFactory->setUser($transactionGroup->user);
$this->accountRepository->setUser($transactionGroup->user); $this->accountRepository->setUser($transactionGroup->user);
$this->transactionGroupRepository->setUser($transactionGroup->user);
$this->destinationAccount = null; $this->destinationAccount = null;
$this->destinationTransaction = null; $this->destinationTransaction = null;
$this->sourceAccount = null; $this->sourceAccount = null;
$this->sourceTransaction = null; $this->sourceTransaction = null;
$this->startCompareHash = $this->transactionGroupRepository->getCompareHash($transactionGroup);
} }
public function setTransactionJournal(TransactionJournal $transactionJournal): void public function setTransactionJournal(TransactionJournal $transactionJournal): void
@@ -675,7 +680,6 @@ class JournalUpdateService
return; return;
} }
$origSourceTransaction = $this->getSourceTransaction(); $origSourceTransaction = $this->getSourceTransaction();
$this->amountChanged = 0 !== bccomp($origSourceTransaction->amount, app('steam')->negative($amount));
$origSourceTransaction->amount = app('steam')->negative($amount); $origSourceTransaction->amount = app('steam')->negative($amount);
$origSourceTransaction->balance_dirty = true; $origSourceTransaction->balance_dirty = true;
$origSourceTransaction->save(); $origSourceTransaction->save();
@@ -818,8 +822,13 @@ class JournalUpdateService
return false; return false;
} }
public function isAmountChanged(): bool public function isCompareHashChanged(): bool
{ {
return $this->amountChanged; Log::debug(sprintf('Now in %s', __METHOD__));
$compareHash = $this->transactionGroupRepository->getCompareHash($this->transactionGroup);
Log::debug(sprintf('Compare hash is "%s".', $compareHash));
Log::debug(sprintf('Start compare hash is "%s".', $this->startCompareHash));
return $compareHash !== $this->startCompareHash;
} }
} }

View File

@@ -79,12 +79,12 @@ class ExchangeRateConverter
public function getCurrencyRate(TransactionCurrency $from, TransactionCurrency $to, Carbon $date): string public function getCurrencyRate(TransactionCurrency $from, TransactionCurrency $to, Carbon $date): string
{ {
if (false === $this->enabled()) { if (false === $this->enabled()) {
Log::debug('ExchangeRateConverter: disabled, return "1".'); // Log::debug('ExchangeRateConverter: disabled, return "1".');
return '1'; return '1';
} }
if ($from->id === $to->id) { if ($from->id === $to->id) {
Log::debug('ExchangeRateConverter: From and to are the same, return "1".'); // Log::debug('ExchangeRateConverter: From and to are the same, return "1".');
return '1'; return '1';
} }

View File

@@ -35,6 +35,7 @@ use FireflyIII\Repositories\Account\AccountRepositoryInterface;
use FireflyIII\Repositories\Journal\JournalRepositoryInterface; use FireflyIII\Repositories\Journal\JournalRepositoryInterface;
use FireflyIII\Support\CacheProperties; use FireflyIII\Support\CacheProperties;
use FireflyIII\Support\Debug\Timer; use FireflyIII\Support\Debug\Timer;
use Illuminate\Support\Facades\Log;
/** /**
* Trait PeriodOverview. * Trait PeriodOverview.
@@ -77,6 +78,7 @@ trait PeriodOverview
*/ */
protected function getAccountPeriodOverview(Account $account, Carbon $start, Carbon $end): array protected function getAccountPeriodOverview(Account $account, Carbon $start, Carbon $end): array
{ {
Log::debug('Now in getAccountPeriodOverview()');
Timer::start('account-period-total'); Timer::start('account-period-total');
$this->accountRepository = app(AccountRepositoryInterface::class); $this->accountRepository = app(AccountRepositoryInterface::class);
$range = app('navigation')->getViewRange(true); $range = app('navigation')->getViewRange(true);
@@ -100,6 +102,7 @@ trait PeriodOverview
$transactions = $this->accountRepository->periodCollection($account, $start, $end); $transactions = $this->accountRepository->periodCollection($account, $start, $end);
// loop dates // loop dates
Log::debug(sprintf('Count of loops: %d', count($dates)));
foreach ($dates as $currentDate) { foreach ($dates as $currentDate) {
$title = app('navigation')->periodShow($currentDate['start'], $currentDate['period']); $title = app('navigation')->periodShow($currentDate['start'], $currentDate['period']);
[$transactions, $spent] = $this->filterTransactionsByType(TransactionTypeEnum::WITHDRAWAL, $transactions, $currentDate['start'], $currentDate['end']); [$transactions, $spent] = $this->filterTransactionsByType(TransactionTypeEnum::WITHDRAWAL, $transactions, $currentDate['start'], $currentDate['end']);
@@ -119,6 +122,7 @@ trait PeriodOverview
} }
$cache->store($entries); $cache->store($entries);
Timer::stop('account-period-total'); Timer::stop('account-period-total');
Log::debug('End of getAccountPeriodOverview()');
return $entries; return $entries;
} }

View File

@@ -67,6 +67,16 @@ class UpdatePiggyBank implements ActionInterface
Log::debug(sprintf('Found piggy bank #%d ("%s")', $piggyBank->id, $piggyBank->name)); Log::debug(sprintf('Found piggy bank #%d ("%s")', $piggyBank->id, $piggyBank->name));
// piggy bank already has an event for this transaction journal?
if ($this->alreadyEventPresent($piggyBank, $journal)) {
Log::info(sprintf('Piggy bank #%d ("%s") already has an event for transaction journal #%d, so no action will be taken.', $piggyBank->id, $piggyBank->name, $journalObj->id));
event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.cannot_find_piggy', ['name' => $actionValue])));
return false;
}
/** @var Transaction $destination */ /** @var Transaction $destination */
$destination = $journalObj->transactions()->where('amount', '>', 0)->first(); $destination = $journalObj->transactions()->where('amount', '>', 0)->first();
@@ -231,4 +241,9 @@ class UpdatePiggyBank implements ActionInterface
$repository->addAmount($piggyBank, $account, $amount, $journal); $repository->addAmount($piggyBank, $account, $amount, $journal);
} }
private function alreadyEventPresent(PiggyBank $piggyBank, array $journal): bool
{
return $piggyBank->piggyBankEvents()->where('transaction_journal_id', $journal['transaction_journal_id'])->exists();
}
} }

77
composer.lock generated
View File

@@ -129,16 +129,16 @@
}, },
{ {
"name": "brick/math", "name": "brick/math",
"version": "0.12.3", "version": "0.13.1",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/brick/math.git", "url": "https://github.com/brick/math.git",
"reference": "866551da34e9a618e64a819ee1e01c20d8a588ba" "reference": "fc7ed316430118cc7836bf45faff18d5dfc8de04"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/brick/math/zipball/866551da34e9a618e64a819ee1e01c20d8a588ba", "url": "https://api.github.com/repos/brick/math/zipball/fc7ed316430118cc7836bf45faff18d5dfc8de04",
"reference": "866551da34e9a618e64a819ee1e01c20d8a588ba", "reference": "fc7ed316430118cc7836bf45faff18d5dfc8de04",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@@ -177,7 +177,7 @@
], ],
"support": { "support": {
"issues": "https://github.com/brick/math/issues", "issues": "https://github.com/brick/math/issues",
"source": "https://github.com/brick/math/tree/0.12.3" "source": "https://github.com/brick/math/tree/0.13.1"
}, },
"funding": [ "funding": [
{ {
@@ -185,7 +185,7 @@
"type": "github" "type": "github"
} }
], ],
"time": "2025-02-28T13:11:00+00:00" "time": "2025-03-29T13:50:30+00:00"
}, },
{ {
"name": "carbonphp/carbon-doctrine-types", "name": "carbonphp/carbon-doctrine-types",
@@ -939,16 +939,16 @@
}, },
{ {
"name": "filp/whoops", "name": "filp/whoops",
"version": "2.18.0", "version": "2.18.1",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/filp/whoops.git", "url": "https://github.com/filp/whoops.git",
"reference": "a7de6c3c6c3c022f5cfc337f8ede6a14460cf77e" "reference": "8fcc6a862f2e7b94eb4221fd0819ddba3d30ab26"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/filp/whoops/zipball/a7de6c3c6c3c022f5cfc337f8ede6a14460cf77e", "url": "https://api.github.com/repos/filp/whoops/zipball/8fcc6a862f2e7b94eb4221fd0819ddba3d30ab26",
"reference": "a7de6c3c6c3c022f5cfc337f8ede6a14460cf77e", "reference": "8fcc6a862f2e7b94eb4221fd0819ddba3d30ab26",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@@ -998,7 +998,7 @@
], ],
"support": { "support": {
"issues": "https://github.com/filp/whoops/issues", "issues": "https://github.com/filp/whoops/issues",
"source": "https://github.com/filp/whoops/tree/2.18.0" "source": "https://github.com/filp/whoops/tree/2.18.1"
}, },
"funding": [ "funding": [
{ {
@@ -1006,7 +1006,7 @@
"type": "github" "type": "github"
} }
], ],
"time": "2025-03-15T12:00:00+00:00" "time": "2025-06-03T18:56:14+00:00"
}, },
{ {
"name": "firebase/php-jwt", "name": "firebase/php-jwt",
@@ -1879,20 +1879,20 @@
}, },
{ {
"name": "laravel/framework", "name": "laravel/framework",
"version": "v12.16.0", "version": "v12.17.0",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/laravel/framework.git", "url": "https://github.com/laravel/framework.git",
"reference": "293bb1c70224faebfd3d4328e201c37115da055f" "reference": "8729d084510480fdeec9b6ad198180147d4a7f06"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/laravel/framework/zipball/293bb1c70224faebfd3d4328e201c37115da055f", "url": "https://api.github.com/repos/laravel/framework/zipball/8729d084510480fdeec9b6ad198180147d4a7f06",
"reference": "293bb1c70224faebfd3d4328e201c37115da055f", "reference": "8729d084510480fdeec9b6ad198180147d4a7f06",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
"brick/math": "^0.11|^0.12", "brick/math": "^0.11|^0.12|^0.13",
"composer-runtime-api": "^2.2", "composer-runtime-api": "^2.2",
"doctrine/inflector": "^2.0.5", "doctrine/inflector": "^2.0.5",
"dragonmantank/cron-expression": "^3.4", "dragonmantank/cron-expression": "^3.4",
@@ -2090,7 +2090,7 @@
"issues": "https://github.com/laravel/framework/issues", "issues": "https://github.com/laravel/framework/issues",
"source": "https://github.com/laravel/framework" "source": "https://github.com/laravel/framework"
}, },
"time": "2025-05-27T15:49:44+00:00" "time": "2025-06-03T14:04:18+00:00"
}, },
{ {
"name": "laravel/passport", "name": "laravel/passport",
@@ -3473,22 +3473,22 @@
}, },
{ {
"name": "mailersend/laravel-driver", "name": "mailersend/laravel-driver",
"version": "v2.9.1", "version": "v2.11.0",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/mailersend/mailersend-laravel-driver.git", "url": "https://github.com/mailersend/mailersend-laravel-driver.git",
"reference": "87fd5ab76808bbaac9221be0d306baef13e98725" "reference": "63acebb5064745076df27b1a80423986b6d7b69e"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/mailersend/mailersend-laravel-driver/zipball/87fd5ab76808bbaac9221be0d306baef13e98725", "url": "https://api.github.com/repos/mailersend/mailersend-laravel-driver/zipball/63acebb5064745076df27b1a80423986b6d7b69e",
"reference": "87fd5ab76808bbaac9221be0d306baef13e98725", "reference": "63acebb5064745076df27b1a80423986b6d7b69e",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
"ext-json": "*", "ext-json": "*",
"illuminate/support": "^9.0 || ^10.0 || ^11.0 || ^12.0", "illuminate/support": "^9.0 || ^10.0 || ^11.0 || ^12.0",
"mailersend/mailersend": "^0.31.0", "mailersend/mailersend": "^0.34.0",
"nyholm/psr7": "^1.5", "nyholm/psr7": "^1.5",
"php": ">=8.0", "php": ">=8.0",
"php-http/guzzle7-adapter": "^1.0", "php-http/guzzle7-adapter": "^1.0",
@@ -3536,29 +3536,28 @@
], ],
"support": { "support": {
"issues": "https://github.com/mailersend/mailersend-laravel-driver/issues", "issues": "https://github.com/mailersend/mailersend-laravel-driver/issues",
"source": "https://github.com/mailersend/mailersend-laravel-driver/tree/v2.9.1" "source": "https://github.com/mailersend/mailersend-laravel-driver/tree/v2.11.0"
}, },
"time": "2025-04-09T09:33:07+00:00" "time": "2025-06-04T08:47:41+00:00"
}, },
{ {
"name": "mailersend/mailersend", "name": "mailersend/mailersend",
"version": "v0.31.0", "version": "v0.34.0",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/mailersend/mailersend-php.git", "url": "https://github.com/mailersend/mailersend-php.git",
"reference": "513ff83ee768526055ad52987cde401ea7218c67" "reference": "1cb8c42e5569e7455b1e0e794dcbf68e3b7898ab"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/mailersend/mailersend-php/zipball/513ff83ee768526055ad52987cde401ea7218c67", "url": "https://api.github.com/repos/mailersend/mailersend-php/zipball/1cb8c42e5569e7455b1e0e794dcbf68e3b7898ab",
"reference": "513ff83ee768526055ad52987cde401ea7218c67", "reference": "1cb8c42e5569e7455b1e0e794dcbf68e3b7898ab",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
"beberlei/assert": "^3.2", "beberlei/assert": "^3.2",
"ext-json": "*", "ext-json": "*",
"illuminate/collections": "^8.0 || ^9.0 || ^10.0 || ^11.0 || ^12.0", "php": "^7.4 || ^8.0 <8.5",
"php": "^7.4|^8.0",
"php-http/client-common": "^2.2", "php-http/client-common": "^2.2",
"php-http/discovery": "^1.9", "php-http/discovery": "^1.9",
"php-http/httplug": "^2.1", "php-http/httplug": "^2.1",
@@ -3603,9 +3602,9 @@
], ],
"support": { "support": {
"issues": "https://github.com/mailersend/mailersend-php/issues", "issues": "https://github.com/mailersend/mailersend-php/issues",
"source": "https://github.com/mailersend/mailersend-php/tree/v0.31.0" "source": "https://github.com/mailersend/mailersend-php/tree/v0.34.0"
}, },
"time": "2025-04-03T12:16:11+00:00" "time": "2025-06-04T07:53:52+00:00"
}, },
{ {
"name": "monolog/monolog", "name": "monolog/monolog",
@@ -3880,16 +3879,16 @@
}, },
{ {
"name": "nette/utils", "name": "nette/utils",
"version": "v4.0.6", "version": "v4.0.7",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/nette/utils.git", "url": "https://github.com/nette/utils.git",
"reference": "ce708655043c7050eb050df361c5e313cf708309" "reference": "e67c4061eb40b9c113b218214e42cb5a0dda28f2"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/nette/utils/zipball/ce708655043c7050eb050df361c5e313cf708309", "url": "https://api.github.com/repos/nette/utils/zipball/e67c4061eb40b9c113b218214e42cb5a0dda28f2",
"reference": "ce708655043c7050eb050df361c5e313cf708309", "reference": "e67c4061eb40b9c113b218214e42cb5a0dda28f2",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@@ -3960,9 +3959,9 @@
], ],
"support": { "support": {
"issues": "https://github.com/nette/utils/issues", "issues": "https://github.com/nette/utils/issues",
"source": "https://github.com/nette/utils/tree/v4.0.6" "source": "https://github.com/nette/utils/tree/v4.0.7"
}, },
"time": "2025-03-30T21:06:30+00:00" "time": "2025-06-03T04:55:08+00:00"
}, },
{ {
"name": "nunomaduro/collision", "name": "nunomaduro/collision",

View File

@@ -78,7 +78,7 @@ return [
'running_balance_column' => env('USE_RUNNING_BALANCE', false), 'running_balance_column' => env('USE_RUNNING_BALANCE', false),
// see cer.php for exchange rates feature flag. // see cer.php for exchange rates feature flag.
], ],
'version' => 'develop/2025-06-01', 'version' => 'develop/2025-06-04',
'api_version' => '2.1.0', // field is no longer used. 'api_version' => '2.1.0', // field is no longer used.
'db_version' => 25, 'db_version' => 25,

62
package-lock.json generated
View File

@@ -43,9 +43,9 @@
} }
}, },
"node_modules/@babel/compat-data": { "node_modules/@babel/compat-data": {
"version": "7.27.3", "version": "7.27.5",
"resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.27.3.tgz", "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.27.5.tgz",
"integrity": "sha512-V42wFfx1ymFte+ecf6iXghnnP8kWTO+ZLXIyZq+1LAXHHvTZdVxicn4yiVYdYMGaCO3tmqub11AorKkv+iodqw==", "integrity": "sha512-KiRAp/VoJaWkkte84TvUd9qjdbZAdiqyvMxrGl1N6vzFogKmaLgoM3L1kgtLicp2HP5fBJS8JrZKLVIZGVJAVg==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"engines": { "engines": {
@@ -94,13 +94,13 @@
} }
}, },
"node_modules/@babel/generator": { "node_modules/@babel/generator": {
"version": "7.27.3", "version": "7.27.5",
"resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.27.3.tgz", "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.27.5.tgz",
"integrity": "sha512-xnlJYj5zepml8NXtjkG0WquFUv8RskFqyFcVgTBp5k+NaA/8uw/K+OSVf8AMGw5e9HKP2ETd5xpK5MLZQD6b4Q==", "integrity": "sha512-ZGhA37l0e/g2s1Cnzdix0O3aLYm66eF8aufiVteOgnwxgnRP8GoyMj7VWsgWnQbVKXyge7hqrFh2K2TQM6t1Hw==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@babel/parser": "^7.27.3", "@babel/parser": "^7.27.5",
"@babel/types": "^7.27.3", "@babel/types": "^7.27.3",
"@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/gen-mapping": "^0.3.5",
"@jridgewell/trace-mapping": "^0.3.25", "@jridgewell/trace-mapping": "^0.3.25",
@@ -406,9 +406,9 @@
} }
}, },
"node_modules/@babel/parser": { "node_modules/@babel/parser": {
"version": "7.27.4", "version": "7.27.5",
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.27.4.tgz", "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.27.5.tgz",
"integrity": "sha512-BRmLHGwpUqLFR2jzx9orBuX/ABDkj2jLKOXrHDTN2aOKL+jFDDKaRNo9nyYsIl9h/UE/7lMKdDjKQQyxKKDZ7g==", "integrity": "sha512-OsQd175SxWkGlzbny8J3K8TnnDD0N3lrIUtB92xwyRpzaenGZhxDvxN/JgU00U3CDZNj9tPuDJ5H0WS4Nt3vKg==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
@@ -683,9 +683,9 @@
} }
}, },
"node_modules/@babel/plugin-transform-block-scoping": { "node_modules/@babel/plugin-transform-block-scoping": {
"version": "7.27.3", "version": "7.27.5",
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.27.3.tgz", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.27.5.tgz",
"integrity": "sha512-+F8CnfhuLhwUACIJMLWnjz6zvzYM2r0yeIHKlbgfw7ml8rOMJsXNXV/hyRcb3nb493gRs4WvYpQAndWj/qQmkQ==", "integrity": "sha512-JF6uE2s67f0y2RZcm2kpAUEbD50vH62TyWVebxwHAlbSdM49VqPz8t4a1uIjp4NIOIZ4xzLfjY5emt/RCyC7TQ==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
@@ -1255,9 +1255,9 @@
} }
}, },
"node_modules/@babel/plugin-transform-regenerator": { "node_modules/@babel/plugin-transform-regenerator": {
"version": "7.27.4", "version": "7.27.5",
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.27.4.tgz", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.27.5.tgz",
"integrity": "sha512-Glp/0n8xuj+E1588otw5rjJkTXfzW7FjH3IIUrfqiZOPQCd2vbg8e+DQE8jK9g4V5/zrxFW+D9WM9gboRPELpQ==", "integrity": "sha512-uhB8yHerfe3MWnuLAhEbeQ4afVoqv8BQsPqrTv7e/jZ9y00kJL6l9a/f4OWaKxotmjzewfEyXE1vgDJenkQ2/Q==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
@@ -4417,9 +4417,9 @@
} }
}, },
"node_modules/caniuse-lite": { "node_modules/caniuse-lite": {
"version": "1.0.30001720", "version": "1.0.30001721",
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001720.tgz", "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001721.tgz",
"integrity": "sha512-Ec/2yV2nNPwb4DnTANEV99ZWwm3ZWfdlfkQbWSDDt+PsXEVYwlhPH8tdMaPunYTKKmz7AnHi2oNEi1GcmKCD8g==", "integrity": "sha512-cOuvmUVtKrtEaoKiO0rSc29jcjwMwX5tOHDy4MgVFEWiUXj4uBMJkwI8MDySkgXidpMiHUcviogAvFi4pA2hDQ==",
"dev": true, "dev": true,
"funding": [ "funding": [
{ {
@@ -5632,9 +5632,9 @@
"license": "MIT" "license": "MIT"
}, },
"node_modules/electron-to-chromium": { "node_modules/electron-to-chromium": {
"version": "1.5.161", "version": "1.5.165",
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.161.tgz", "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.165.tgz",
"integrity": "sha512-hwtetwfKNZo/UlwHIVBlKZVdy7o8bIZxxKs0Mv/ROPiQQQmDgdm5a+KvKtBsxM8ZjFzTaCeLoodZ8jiBE3o9rA==", "integrity": "sha512-naiMx1Z6Nb2TxPU6fiFrUrDTjyPMLdTtaOd2oLmG8zVSg2hCWGkhPyxwk+qRmZ1ytwVqUv0u7ZcDA5+ALhaUtw==",
"dev": true, "dev": true,
"license": "ISC" "license": "ISC"
}, },
@@ -7713,9 +7713,9 @@
} }
}, },
"node_modules/laravel-vite-plugin": { "node_modules/laravel-vite-plugin": {
"version": "1.2.0", "version": "1.3.0",
"resolved": "https://registry.npmjs.org/laravel-vite-plugin/-/laravel-vite-plugin-1.2.0.tgz", "resolved": "https://registry.npmjs.org/laravel-vite-plugin/-/laravel-vite-plugin-1.3.0.tgz",
"integrity": "sha512-R0pJ+IcTVeqEMoKz/B2Ij57QVq3sFTABiFmb06gAwFdivbOgsUtuhX6N2MGLEArajrS3U5JbberzwOe7uXHMHQ==", "integrity": "sha512-P5qyG56YbYxM8OuYmK2OkhcKe0AksNVJUjq9LUZ5tOekU9fBn9LujYyctI4t9XoLjuMvHJXXpCoPntY1oKltuA==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
@@ -10414,9 +10414,9 @@
} }
}, },
"node_modules/shell-quote": { "node_modules/shell-quote": {
"version": "1.8.2", "version": "1.8.3",
"resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.2.tgz", "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.3.tgz",
"integrity": "sha512-AzqKpGKjrj7EM6rKVQEPpB288oCfnrEIuyoT9cyF4nmGa7V8Zk6f7RRqYisX8X9m+Q7bd632aZW4ky7EhbQztA==", "integrity": "sha512-ObmnIF4hXNg1BqhnHmgbDETF8dLPCggZWBjkQfhZpbszZnYur5DUljTcCHii5LC3J5E0yeO/1LIMyH+UvHQgyw==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"engines": { "engines": {
@@ -12081,9 +12081,9 @@
} }
}, },
"node_modules/webpack/node_modules/webpack-sources": { "node_modules/webpack/node_modules/webpack-sources": {
"version": "3.3.0", "version": "3.3.2",
"resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.3.0.tgz", "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.3.2.tgz",
"integrity": "sha512-77R0RDmJfj9dyv5p3bM5pOHa+X8/ZkO9c7kpDstigkC4nIDobadsfSGCwB4bKhMVxqAok8tajaoR8rirM7+VFQ==", "integrity": "sha512-ykKKus8lqlgXX/1WjudpIEjqsafjOTcOJqxnAbMLAu/KCsDCJ6GBtvscewvTkrn24HsnvFwrSCbenFrhtcCsAA==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"engines": { "engines": {

View File

@@ -24,4 +24,12 @@ $(function () {
if (typeof(lineChart) === 'function' && typeof(piggyBankID) !== 'undefined') { if (typeof(lineChart) === 'function' && typeof(piggyBankID) !== 'undefined') {
lineChart('chart/piggy-bank/' + piggyBankID, 'piggy-bank-history'); lineChart('chart/piggy-bank/' + piggyBankID, 'piggy-bank-history');
} }
});
// on submit of logout button:
$('.reset-link').click(function(e) {
console.log('here we are');
e.preventDefault();
document.getElementById('reset-form').submit();
return false;
});
});

View File

@@ -490,7 +490,6 @@ let transactions = function () {
// addedSplit, is called from the HTML // addedSplit, is called from the HTML
// for source account // for source account
const renderAccount = function (item, b, c) { const renderAccount = function (item, b, c) {
console.log('render account');
return item.name_with_balance + '<br><small class="text-muted">' + i18next.t('firefly.account_type_' + item.type) + '</small>'; return item.name_with_balance + '<br><small class="text-muted">' + i18next.t('firefly.account_type_' + item.type) + '</small>';
}; };
console.log('here we are in'); console.log('here we are in');
@@ -499,7 +498,7 @@ let transactions = function () {
serverUrl: urls.account, serverUrl: urls.account,
onRenderItem: renderAccount, onRenderItem: renderAccount,
valueField: 'id', valueField: 'id',
labelField: 'title', labelField: 'name_with_balance',
onChange: changeSourceAccount, onChange: changeSourceAccount,
onSelectItem: selectSourceAccount, onSelectItem: selectSourceAccount,
hiddenValue: this.entries[count].source_account.alpine_name hiddenValue: this.entries[count].source_account.alpine_name
@@ -509,7 +508,7 @@ let transactions = function () {
serverUrl: urls.account, serverUrl: urls.account,
account_types: this.filters.destination, account_types: this.filters.destination,
valueField: 'id', valueField: 'id',
labelField: 'title', labelField: 'name_with_balance',
onRenderItem: renderAccount, onRenderItem: renderAccount,
onChange: changeDestinationAccount, onChange: changeDestinationAccount,
onSelectItem: selectDestinationAccount onSelectItem: selectDestinationAccount

View File

@@ -45,7 +45,7 @@ export function addAutocomplete(options) {
liveServer: true, liveServer: true,
}; };
if (typeof options.account_types !== 'undefined' && options.account_types.length > 0) { if (typeof options.account_types !== 'undefined' && options.account_types.length > 0) {
params.serverParams['filter[account_types]'] = options.account_types; params.serverParams['types'] = options.account_types;
} }
if (typeof options.onRenderItem !== 'undefined' && null !== options.onRenderItem) { if (typeof options.onRenderItem !== 'undefined' && null !== options.onRenderItem) {
console.log('overrule onRenderItem.'); console.log('overrule onRenderItem.');

View File

@@ -55,7 +55,7 @@ export function changeDestinationAccount(item, ac) {
export function selectDestinationAccount(item, ac) { export function selectDestinationAccount(item, ac) {
const index = parseInt(ac._searchInput.attributes['data-index'].value); const index = parseInt(ac._searchInput.attributes['data-index'].value);
document.querySelector('#form')._x_dataStack[0].$data.entries[index].destination_account = { document.querySelector('#form')._x_dataStack[0].$data.entries[index].destination_account = {
id: item.id, name: item.title, alpine_name: item.title, type: item.meta.type, currency_code: item.meta.currency_code, id: item.id, name: item.name, alpine_name: item.name, type: item.type, currency_code: item.currency_code,
}; };
document.querySelector('#form')._x_dataStack[0].changedDestinationAccount(); document.querySelector('#form')._x_dataStack[0].changedDestinationAccount();
} }
@@ -78,7 +78,7 @@ export function changeSourceAccount(item, ac) {
export function selectSourceAccount(item, ac) { export function selectSourceAccount(item, ac) {
const index = parseInt(ac._searchInput.attributes['data-index'].value); const index = parseInt(ac._searchInput.attributes['data-index'].value);
document.querySelector('#form')._x_dataStack[0].$data.entries[index].source_account = { document.querySelector('#form')._x_dataStack[0].$data.entries[index].source_account = {
id: item.id, name: item.title, alpine_name: item.title, type: item.meta.type, currency_code: item.meta.currency_code, id: item.id, name: item.name, alpine_name: item.name, type: item.type, currency_code: item.currency_code,
}; };
document.querySelector('#form')._x_dataStack[0].changedSourceAccount(); document.querySelector('#form')._x_dataStack[0].changedSourceAccount();
} }

View File

@@ -36,8 +36,8 @@ export default defineConfig(({command, mode, isSsrBuild, isPreview}) => {
let https = null; let https = null;
if (command === 'serve') { if (command === 'serve') {
https = { https = {
key: fs.readFileSync(`/Users/sander/Sites/vm/tls-certificates/wildcard.sd.internal.key`), key: fs.readFileSync(`/vagrant/tls-certificates/wildcard.sd.internal.key`),
cert: fs.readFileSync(`/Users/sander/Sites/vm/tls-certificates/wildcard.sd.internal.crt`), cert: fs.readFileSync(`/vagrant/tls-certificates/wildcard.sd.internal.crt`),
}; };
} }
@@ -83,7 +83,7 @@ export default defineConfig(({command, mode, isSsrBuild, isPreview}) => {
server: { server: {
cors: true, cors: true,
origin: 'https://localhost:5173', origin: 'https://192.168.96.154:5173',
watch: { watch: {
usePolling: true, usePolling: true,
}, },

View File

@@ -70,7 +70,7 @@ return [
// known user login attempt // known user login attempt
'failed_login_subject' => 'Firefly III detected a failed login attempt', 'failed_login_subject' => 'Firefly III detected a failed login attempt',
'failed_login_body' => 'Firefly III detected that somebody (you?) failed to login with your account ":email". Please verify that this was you.', 'failed_login_body' => 'Firefly III detected that somebody (you?) failed to login with your account ":email". Please verify that this was you.',
'failed_login_message' => 'A failed login attempt on your Firefly III account ":email" was detected.', 'failed_login_message' => 'A failed login attempt (:ip) on your Firefly III account ":email" was detected.',
'failed_login_warning' => 'If you recognize this IP address or the login attempt, you can ignore this message. If you didn\'t try to login, of if you have no idea what this is about, verify your password security, change it, and log out all other sessions. To do this, go to your profile page. Of course you have 2FA enabled already, right? Stay safe!', 'failed_login_warning' => 'If you recognize this IP address or the login attempt, you can ignore this message. If you didn\'t try to login, of if you have no idea what this is about, verify your password security, change it, and log out all other sessions. To do this, go to your profile page. Of course you have 2FA enabled already, right? Stay safe!',
// registered // registered

View File

@@ -2486,6 +2486,10 @@ return [
'left_for_piggy_banks' => 'Left for piggy banks', 'left_for_piggy_banks' => 'Left for piggy banks',
'sum_of_piggy_banks' => 'Sum of piggy banks', 'sum_of_piggy_banks' => 'Sum of piggy banks',
'saved_so_far' => 'Saved so far', 'saved_so_far' => 'Saved so far',
'saved_so_far_total' => 'Saved so far in total',
'reset_history' => 'reset history',
'reset_history_confirm' => 'Are you sure you want to reset the history of this piggy bank? This will make the chart match the piggy bank\'s amount again.',
'piggy_history_reset' => 'The piggy bank history has been reset',
'left_to_save' => 'Left to save', 'left_to_save' => 'Left to save',
'suggested_amount' => 'Suggested monthly amount to save', 'suggested_amount' => 'Suggested monthly amount to save',
'add_money_to_piggy_title' => 'Add money to piggy bank ":name"', 'add_money_to_piggy_title' => 'Add money to piggy bank ":name"',

View File

@@ -68,6 +68,7 @@ return [
'cannot_find_piggy' => 'Firefly III can\'t find a piggy bank named ":name"', 'cannot_find_piggy' => 'Firefly III can\'t find a piggy bank named ":name"',
'no_link_piggy' => 'This transaction\'s accounts are not linked to the piggy bank, so no action will be taken', 'no_link_piggy' => 'This transaction\'s accounts are not linked to the piggy bank, so no action will be taken',
'both_link_piggy' => 'This transaction\'s accounts are both linked to the piggy bank, so no action will be taken', 'both_link_piggy' => 'This transaction\'s accounts are both linked to the piggy bank, so no action will be taken',
'already_linked' => 'This transaction is already linked to piggy bank ":name"',
'cannot_unlink_tag' => 'Tag ":tag" isn\'t linked to this transaction', 'cannot_unlink_tag' => 'Tag ":tag" isn\'t linked to this transaction',
'cannot_find_budget' => 'Firefly III can\'t find budget ":name"', 'cannot_find_budget' => 'Firefly III can\'t find budget ":name"',
'cannot_find_category' => 'Firefly III can\'t find category ":name"', 'cannot_find_category' => 'Firefly III can\'t find category ":name"',

View File

@@ -163,6 +163,7 @@
{% endif %} {% endif %}
</td> </td>
<td style="{{ style|raw }};text-align:right"> <td style="{{ style|raw }};text-align:right">
{# deposit #} {# deposit #}
{% if transaction.transaction_type_type == 'Deposit' %} {% if transaction.transaction_type_type == 'Deposit' %}
{# amount of deposit #} {# amount of deposit #}

View File

@@ -54,8 +54,20 @@
</td> </td>
</tr> </tr>
{% endif %} {% endif %}
{% for account in piggy.accounts %}
<tr>
<td>
{{ 'saved_so_far'|_ }}
(<a href="{{ route('accounts.show', account.id) }}">{{ account.name }}</a>)
</td>
<td>
{{ formatAmountBySymbol(account.current_amount, piggy.currency_symbol, piggy.currency_decimal_places) }}
</td>
</tr>
{% endfor %}
<tr> <tr>
<td>{{ 'saved_so_far'|_ }}</td> <td>{{ 'saved_so_far_total'|_ }}</td>
<td> <td>
{{ formatAmountBySymbol(piggy.current_amount, piggy.currency_symbol, piggy.currency_decimal_places) }} {{ formatAmountBySymbol(piggy.current_amount, piggy.currency_symbol, piggy.currency_decimal_places) }}
</td> </td>
@@ -102,7 +114,7 @@
</div> </div>
<div class="box"> <div class="box">
<div class="box-header with-border"> <div class="box-header with-border">
<h3 class="box-title">{{ 'event_history'|_ }}</h3> <h3 class="box-title">{{ 'event_history'|_ }} (<a onclick="return confirm('{{ 'reset_history_confirm'|_|escape('js') }}');" class="reset-link" href="#">{{ 'reset_history'|_ }}</a>)</h3>
</div> </div>
<div class="box-body no-padding" id="piggyEvents"> <div class="box-body no-padding" id="piggyEvents">
{% include 'list/piggy-bank-events' %} {% include 'list/piggy-bank-events' %}
@@ -140,6 +152,9 @@
</div> </div>
{% endif %} {% endif %}
</div> </div>
<form id="reset-form" action="{{ route('piggy-banks.reset', [piggyBank.id]) }}" method="POST" style="display: none;">
<input type="hidden" name="_token" value="{{ csrf_token() }}"/>
</form>
{% endblock %} {% endblock %}
{% block scripts %} {% block scripts %}

View File

@@ -803,6 +803,7 @@ Route::group(
Route::post('store', ['uses' => 'PiggyBank\CreateController@store', 'as' => 'store']); Route::post('store', ['uses' => 'PiggyBank\CreateController@store', 'as' => 'store']);
Route::post('update/{piggyBank}', ['uses' => 'PiggyBank\EditController@update', 'as' => 'update']); Route::post('update/{piggyBank}', ['uses' => 'PiggyBank\EditController@update', 'as' => 'update']);
Route::post('destroy/{piggyBank}', ['uses' => 'PiggyBank\DeleteController@destroy', 'as' => 'destroy']); Route::post('destroy/{piggyBank}', ['uses' => 'PiggyBank\DeleteController@destroy', 'as' => 'destroy']);
Route::post('reset-history/{piggyBank}', ['uses' => 'PiggyBank\EditController@resetHistory', 'as' => 'reset']);
Route::post('add/{piggyBank}', ['uses' => 'PiggyBank\AmountController@postAdd', 'as' => 'add']); Route::post('add/{piggyBank}', ['uses' => 'PiggyBank\AmountController@postAdd', 'as' => 'add']);
Route::post('remove/{piggyBank}', ['uses' => 'PiggyBank\AmountController@postRemove', 'as' => 'remove']); Route::post('remove/{piggyBank}', ['uses' => 'PiggyBank\AmountController@postRemove', 'as' => 'remove']);