Compare commits

..

72 Commits

Author SHA1 Message Date
github-actions[bot]
023a4f178d Merge pull request #10477 from firefly-iii/release-1750393987
🤖 Automatically merge the PR into the develop branch.
2025-06-20 06:33:14 +02:00
JC5
ef254995ad 🤖 Auto commit for release 'develop' on 2025-06-20 2025-06-20 06:33:07 +02:00
James Cole
faeb74634b Merge branch 'main' into develop 2025-06-20 06:28:53 +02:00
James Cole
b5baae373f Move changelog. 2025-06-20 06:28:42 +02:00
James Cole
63de711cda Update changelog. 2025-06-20 06:24:19 +02:00
James Cole
bd28d116cb Fix currency seeder, add some debug logs. 2025-06-20 05:59:44 +02:00
github-actions[bot]
7efc2861bc Merge pull request #10468 from firefly-iii/release-1750044727
🤖 Automatically merge the PR into the develop branch.
2025-06-16 05:32:16 +02:00
JC5
5c689a2ca2 🤖 Auto commit for release 'develop' on 2025-06-16 2025-06-16 05:32:08 +02:00
github-actions[bot]
d5d65df76f Merge pull request #10459 from firefly-iii/release-1749897382
🤖 Automatically merge the PR into the develop branch.
2025-06-14 12:36:32 +02:00
JC5
df7d4f700c 🤖 Auto commit for release 'develop' on 2025-06-14 2025-06-14 12:36:22 +02:00
James Cole
efebe3cb41 Remove debug letters. 2025-06-14 12:31:54 +02:00
James Cole
2ba5b6ae49 Fix #10454 2025-06-14 10:18:58 +02:00
github-actions[bot]
31d93efab2 Merge pull request #10451 from firefly-iii/release-1749735701
🤖 Automatically merge the PR into the develop branch.
2025-06-12 15:41:49 +02:00
JC5
657b95485c 🤖 Auto commit for release 'develop' on 2025-06-12 2025-06-12 15:41:41 +02:00
Sander Dorigo
3bfc12f93b Try to be more clear about OAuth errors 2025-06-12 15:36:38 +02:00
James Cole
ccfd2f2ac3 Update fr.yml
Signed-off-by: James Cole <james@firefly-iii.org>
2025-06-12 13:12:50 +02:00
James Cole
fb3fe0d87b Update bug.yml
Signed-off-by: James Cole <james@firefly-iii.org>
2025-06-12 13:11:55 +02:00
James Cole
c80c6d52fe Update config.yml
Signed-off-by: James Cole <james@firefly-iii.org>
2025-06-12 13:02:14 +02:00
Sander Dorigo
0fb3c0c7bf Add missing variable name 2025-06-11 15:51:39 +02:00
github-actions[bot]
43c625bee2 Merge pull request #10444 from firefly-iii/develop
🤖 Automatically merge the PR into the main branch.
2025-06-11 14:07:28 +02:00
github-actions[bot]
967a5dd256 Merge pull request #10443 from firefly-iii/release-1749643634
🤖 Automatically merge the PR into the develop branch.
2025-06-11 14:07:22 +02:00
JC5
3c9d1bcaa1 🤖 Auto commit for release 'v6.2.17' on 2025-06-11 2025-06-11 14:07:14 +02:00
github-actions[bot]
8cd2de14a9 Merge pull request #10442 from firefly-iii/release-1749642296
🤖 Automatically merge the PR into the develop branch.
2025-06-11 13:45:03 +02:00
JC5
7728a35e04 🤖 Auto commit for release 'develop' on 2025-06-11 2025-06-11 13:44:56 +02:00
Sander Dorigo
49000da123 Update changelog. 2025-06-11 13:40:11 +02:00
Sander Dorigo
32a8f89875 Fix #10419 2025-06-11 13:29:36 +02:00
Sander Dorigo
49e5a81bd3 Fix 10382 2025-06-11 13:23:51 +02:00
Sander Dorigo
a8efe86af0 Fix #10229 2025-06-11 13:19:53 +02:00
Sander Dorigo
4f07b089d2 Fix #10441 2025-06-11 09:28:56 +02:00
James Cole
e786bf47c2 Small code quality things. 2025-06-09 07:06:42 +02:00
github-actions[bot]
be11778c53 Merge pull request #10438 from firefly-iii/release-1749444033
🤖 Automatically merge the PR into the develop branch.
2025-06-09 06:40:42 +02:00
JC5
3d7325424c 🤖 Auto commit for release 'develop' on 2025-06-09 2025-06-09 06:40:34 +02:00
James Cole
f4ffed99ef Change some code. 2025-06-09 06:35:58 +02:00
github-actions[bot]
52dfe9a738 Merge pull request #10437 from firefly-iii/release-1749442144
🤖 Automatically merge the PR into the develop branch.
2025-06-09 06:09:10 +02:00
JC5
a0bc8b2ba2 🤖 Auto commit for release 'develop' on 2025-06-09 2025-06-09 06:09:04 +02:00
James Cole
4ffca9e2ca Some fixable code. 2025-06-09 06:05:18 +02:00
James Cole
a6afec9afa Merge branch 'main' into develop 2025-06-09 06:04:58 +02:00
James Cole
5d859222f8 Add debug info to release script. 2025-06-09 06:04:50 +02:00
github-actions[bot]
0e3ba14666 Merge pull request #10436 from firefly-iii/release-1749441527
🤖 Automatically merge the PR into the develop branch.
2025-06-09 05:58:55 +02:00
JC5
860767fe5a 🤖 Auto commit for release 'develop' on 2025-06-09 2025-06-09 05:58:47 +02:00
James Cole
91b0ad625c Fix #10433 2025-06-09 05:53:11 +02:00
James Cole
67bab2561a Fix https://github.com/firefly-iii/firefly-iii/issues/10432 2025-06-09 05:50:23 +02:00
github-actions[bot]
b7908ebcb4 Merge pull request #10434 from firefly-iii/release-1749439923
🤖 Automatically merge the PR into the develop branch.
2025-06-09 05:32:12 +02:00
JC5
a20601ea85 🤖 Auto commit for release 'develop' on 2025-06-09 2025-06-09 05:32:04 +02:00
James Cole
14622329a8 Replace app calls with static calls. 2025-06-08 15:20:22 +02:00
github-actions[bot]
5577be7b08 Merge pull request #10428 from firefly-iii/release-1749383237
🤖 Automatically merge the PR into the develop branch.
2025-06-08 13:47:27 +02:00
JC5
e3bab9d7d8 🤖 Auto commit for release 'develop' on 2025-06-08 2025-06-08 13:47:17 +02:00
James Cole
e0512bee3d Fix #10427 2025-06-08 13:17:01 +02:00
James Cole
20827a5fd1 Fix #10426 2025-06-08 05:55:22 +02:00
James Cole
8819dac1e1 Merge branches 'develop' and 'develop' of github.com:firefly-iii/firefly-iii into develop 2025-06-07 06:31:19 +02:00
James Cole
da0a07262c Fix # 2025-06-07 06:31:14 +02:00
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
github-actions[bot]
0e7d72023d Merge pull request #10398 from firefly-iii/release-1748774144
🤖 Automatically merge the PR into the develop branch.
2025-06-01 12:35:50 +02:00
JC5
314f91ff24 🤖 Auto commit for release 'develop' on 2025-06-01 2025-06-01 12:35:44 +02:00
James Cole
ed54a5c9a4 Fix cache issue in recurring cron job report. 2025-06-01 12:24:02 +02:00
github-actions[bot]
adbf785aba Merge pull request #10381 from firefly-iii/develop
🤖 Automatically merge the PR into the main branch.
2025-05-27 18:27:32 +02:00
github-actions[bot]
1fae39a49d Merge pull request #10366 from firefly-iii/develop
🤖 Automatically merge the PR into the main branch.
2025-05-25 07:11:22 +02:00
github-actions[bot]
659240a98b Merge pull request #10363 from firefly-iii/develop
🤖 Automatically merge the PR into the main branch.
2025-05-25 06:49:54 +02:00
github-actions[bot]
e9644c9679 Merge pull request #10345 from firefly-iii/develop
🤖 Automatically merge the PR into the main branch.
2025-05-24 06:31:42 +02:00
116 changed files with 3717 additions and 3478 deletions

View File

@@ -1,4 +1,4 @@
name: Bug Report name: Bug report
description: Report a bug in Firefly III (or associated tools) description: Report a bug in Firefly III (or associated tools)
body: body:
- type: checkboxes - type: checkboxes
@@ -12,7 +12,7 @@ body:
- type: checkboxes - type: checkboxes
attributes: attributes:
label: I've found a bug and checked that ... label: I've found a bug and checked that ...
description: Make sure that your request fulfills all of the following requirements. If one requirement cannot be satisfied, explain in detail why. description: Make sure that your request fulfills all of the following requirements. If one requirement cannot be satisfied, please explain why.
options: options:
- label: ... [the documentation](https://docs.firefly-iii.org/) does not mention anything about my problem - label: ... [the documentation](https://docs.firefly-iii.org/) does not mention anything about my problem
- label: ... there are no open or closed issues that are related to my problem - label: ... there are no open or closed issues that are related to my problem
@@ -33,13 +33,6 @@ body:
validations: validations:
required: true required: true
- type: textarea
attributes:
label: Expected behaviour
description: Please describe precisely what you'd expect to happen. Be specific.
validations:
required: false
- type: textarea - type: textarea
attributes: attributes:
label: Steps to reproduce label: Steps to reproduce
@@ -54,4 +47,4 @@ body:
- type: textarea - type: textarea
attributes: attributes:
label: Additional info label: Additional info
description: Please provide any additional information that seem useful. description: Please provide any additional information that seems useful.

View File

@@ -3,3 +3,6 @@ contact_links:
- name: Ask a question - name: Ask a question
url: https://github.com/firefly-iii/firefly-iii/discussions url: https://github.com/firefly-iii/firefly-iii/discussions
about: Please ask and answer questions here. about: Please ask and answer questions here.
- name: I need support!
url: https://github.com/firefly-iii/firefly-iii/discussions
about: I think I broke something...

View File

@@ -1,4 +1,4 @@
name: Feature Request name: Feature request
description: Request a feature or enhancement in Firefly III (or associated tools) description: Request a feature or enhancement in Firefly III (or associated tools)
body: body:
- type: checkboxes - type: checkboxes
@@ -31,11 +31,6 @@ body:
validations: validations:
required: true required: true
- type: textarea
attributes:
label: What are alternatives?
description: Please describe what alternatives currently exist.
- type: textarea - type: textarea
attributes: attributes:
label: Additional context label: Additional context

View File

@@ -279,10 +279,20 @@ jobs:
if [[ "develop" != "$version" ]] && [[ "$version" != branch* ]] && [[ "$version" != *alpha* ]] && [[ "$version" != *beta* ]]; then if [[ "develop" != "$version" ]] && [[ "$version" != branch* ]] && [[ "$version" != *alpha* ]] && [[ "$version" != *beta* ]]; then
echo 'Describe the latest release' echo 'Describe the latest release'
sudo chown -R runner:docker output.txt sudo chown -R runner:docker output.txt
# the changelog is in output.txt
mv output.txt output2.txt
touch output.txt touch output.txt
echo '' >> output.txt echo '' >> output.txt
echo "Welcome to release $version of Firefly III. It contains the the latest fixes, translations and features. Docker users can find this release under the \`latest\` tag." >> output.txt echo "Welcome to release $version of Firefly III. It contains the the latest fixes, translations and features. Docker users can find this release under the \`latest\` tag." >> output.txt
echo '' >> output.txt echo '' >> output.txt
# add changelog to file.
cat output2.txt >> output.txt
echo '' >> output.txt
rm -f output2.txt
echo '### Instructions' >> output.txt echo '### Instructions' >> output.txt
echo '' >> output.txt echo '' >> output.txt
echo "* Installation instructions for [Docker](https://docs.firefly-iii.org/how-to/firefly-iii/installation/docker/), [Portainer](https://docs.firefly-iii.org/how-to/firefly-iii/installation/portainer/), [Kubernetes](https://docs.firefly-iii.org/how-to/firefly-iii/installation/kubernetes/) or [self-managed servers](https://docs.firefly-iii.org/how-to/firefly-iii/installation/self-managed/)" >> output.txt echo "* Installation instructions for [Docker](https://docs.firefly-iii.org/how-to/firefly-iii/installation/docker/), [Portainer](https://docs.firefly-iii.org/how-to/firefly-iii/installation/portainer/), [Kubernetes](https://docs.firefly-iii.org/how-to/firefly-iii/installation/kubernetes/) or [self-managed servers](https://docs.firefly-iii.org/how-to/firefly-iii/installation/self-managed/)" >> output.txt
@@ -465,20 +475,27 @@ jobs:
- name: Upload artifacts - name: Upload artifacts
run: | run: |
# add zip file to release. # add zip file to release.
echo "Upload $zipName to $releaseName"
gh release upload $releaseName $zipName gh release upload $releaseName $zipName
echo "Upload $tarName to $releaseName"
gh release upload $releaseName $tarName gh release upload $releaseName $tarName
# add sha256 sum to release # add sha256 sum to release
echo "Upload $zipName.sha256 to $releaseName"
gh release upload $releaseName $zipName.sha256 gh release upload $releaseName $zipName.sha256
echo "Upload $tarName.sha256 to $releaseName"
gh release upload $releaseName $tarName.sha256 gh release upload $releaseName $tarName.sha256
# add signatures to release # add signatures to release
echo "Upload $zipName.asc to $releaseName"
gh release upload $releaseName $zipName.asc gh release upload $releaseName $zipName.asc
echo "Upload $tarName.asc to $releaseName"
gh release upload $releaseName $tarName.asc gh release upload $releaseName $tarName.asc
# get current HEAD and add as file to the release # get current HEAD and add as file to the release
HEAD=$(git rev-parse HEAD) HEAD=$(git rev-parse HEAD)
echo $HEAD > HEAD.txt echo $HEAD > HEAD.txt
echo "Upload HEAD.txt to $releaseName"
gh release upload $releaseName HEAD.txt gh release upload $releaseName HEAD.txt
# remove all temporary files # remove all temporary files

View File

@@ -24,6 +24,7 @@ declare(strict_types=1);
namespace FireflyIII\Api\V1\Controllers\Autocomplete; namespace FireflyIII\Api\V1\Controllers\Autocomplete;
use Deprecated;
use FireflyIII\Api\V1\Controllers\Controller; use FireflyIII\Api\V1\Controllers\Controller;
use FireflyIII\Api\V1\Requests\Autocomplete\AutocompleteRequest; use FireflyIII\Api\V1\Requests\Autocomplete\AutocompleteRequest;
use FireflyIII\Models\TransactionCurrency; use FireflyIII\Models\TransactionCurrency;
@@ -83,9 +84,8 @@ class CurrencyController extends Controller
/** /**
* Documentation for this endpoint is at: * Documentation for this endpoint is at:
* https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/autocomplete/getCurrenciesCodeAC * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/autocomplete/getCurrenciesCodeAC
*
* @deprecated
*/ */
#[Deprecated]
public function currenciesWithCode(AutocompleteRequest $request): JsonResponse public function currenciesWithCode(AutocompleteRequest $request): JsonResponse
{ {
$data = $request->getData(); $data = $request->getData();

View File

@@ -24,6 +24,7 @@ declare(strict_types=1);
namespace FireflyIII\Api\V1\Controllers\Chart; namespace FireflyIII\Api\V1\Controllers\Chart;
use FireflyIII\Exceptions\ValidationException;
use FireflyIII\Models\TransactionCurrency; use FireflyIII\Models\TransactionCurrency;
use Carbon\Carbon; use Carbon\Carbon;
use FireflyIII\Api\V1\Controllers\Controller; use FireflyIII\Api\V1\Controllers\Controller;
@@ -35,6 +36,7 @@ use FireflyIII\Models\Account;
use FireflyIII\Models\Preference; use FireflyIII\Models\Preference;
use FireflyIII\Repositories\Account\AccountRepositoryInterface; use FireflyIII\Repositories\Account\AccountRepositoryInterface;
use FireflyIII\Support\Chart\ChartData; use FireflyIII\Support\Chart\ChartData;
use FireflyIII\Support\Facades\Preferences;
use FireflyIII\Support\Facades\Steam; use FireflyIII\Support\Facades\Steam;
use FireflyIII\Support\Http\Api\ApiSupport; use FireflyIII\Support\Http\Api\ApiSupport;
use FireflyIII\Support\Http\Api\CollectsAccountsFromFilter; use FireflyIII\Support\Http\Api\CollectsAccountsFromFilter;
@@ -139,7 +141,7 @@ class AccountController extends Controller
* This endpoint is documented at: * This endpoint is documented at:
* https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/charts/getChartAccountOverview * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/charts/getChartAccountOverview
* *
* @throws FireflyException * @throws ValidationException
*/ */
public function overview(DateRequest $request): JsonResponse public function overview(DateRequest $request): JsonResponse
{ {
@@ -160,7 +162,7 @@ class AccountController extends Controller
$defaultSet = $this->repository->getAccountsByType([AccountTypeEnum::ASSET->value])->pluck('id')->toArray(); $defaultSet = $this->repository->getAccountsByType([AccountTypeEnum::ASSET->value])->pluck('id')->toArray();
/** @var Preference $frontpage */ /** @var Preference $frontpage */
$frontpage = app('preferences')->get('frontpageAccounts', $defaultSet); $frontpage = Preferences::get('frontpageAccounts', $defaultSet);
if (!(is_array($frontpage->data) && count($frontpage->data) > 0)) { if (!(is_array($frontpage->data) && count($frontpage->data) > 0)) {
$frontpage->data = $defaultSet; $frontpage->data = $defaultSet;

View File

@@ -212,7 +212,7 @@ class BudgetController extends Controller
*/ */
private function budgetLimits(Budget $budget, Collection $limits): array private function budgetLimits(Budget $budget, Collection $limits): array
{ {
app('log')->debug(sprintf('Now in budgetLimits(#%d)', $budget->id)); Log::debug(sprintf('Now in budgetLimits(#%d)', $budget->id));
$data = []; $data = [];
/** @var BudgetLimit $limit */ /** @var BudgetLimit $limit */
@@ -233,16 +233,11 @@ class BudgetController extends Controller
$end->endOfDay(); $end->endOfDay();
$spent = $this->opsRepository->listExpenses($limit->start_date, $end, null, new Collection([$budget])); $spent = $this->opsRepository->listExpenses($limit->start_date, $end, null, new Collection([$budget]));
$limitCurrencyId = $limit->transaction_currency_id; $limitCurrencyId = $limit->transaction_currency_id;
$filtered = [];
/** @var array $entry */ /** @var array $entry */
foreach ($spent as $currencyId => $entry) { // only spent the entry where the entry's currency matches the budget limit's currency
// only spent the entry where the entry's currency matches the budget limit's currency // so $filtered will only have 1 or 0 entries
// so $filtered will only have 1 or 0 entries $filtered = array_filter($spent, fn ($entry) => $entry['currency_id'] === $limitCurrencyId);
if ($entry['currency_id'] === $limitCurrencyId) {
$filtered[$currencyId] = $entry;
}
}
$result = $this->processExpenses($budget->id, $filtered, $limit->start_date, $end); $result = $this->processExpenses($budget->id, $filtered, $limit->start_date, $end);
if (1 === count($result)) { if (1 === count($result)) {
$compare = bccomp($limit->amount, (string) app('steam')->positive($result[$limitCurrencyId]['spent'])); $compare = bccomp($limit->amount, (string) app('steam')->positive($result[$limitCurrencyId]['spent']));

View File

@@ -127,7 +127,6 @@ abstract class Controller extends BaseController
Log::error(sprintf('Request field "%s" contains a non-scalar value. Value set to NULL.', $field)); Log::error(sprintf('Request field "%s" contains a non-scalar value. Value set to NULL.', $field));
Log::error($e->getMessage()); Log::error($e->getMessage());
Log::error($e->getTraceAsString()); Log::error($e->getTraceAsString());
$value = null;
} }
$obj = null; $obj = null;
if (null !== $date) { if (null !== $date) {

View File

@@ -64,7 +64,7 @@ class DestroyController extends Controller
public function destroy(DestroyRequest $request): JsonResponse public function destroy(DestroyRequest $request): JsonResponse
{ {
$objects = $request->getObjects(); $objects = $request->getObjects();
$this->unused = $request->boolean('unused', false); $this->unused = $request->boolean('unused');
$allExceptAssets = [AccountTypeEnum::BENEFICIARY->value, AccountTypeEnum::CASH->value, AccountTypeEnum::CREDITCARD->value, AccountTypeEnum::DEFAULT->value, AccountTypeEnum::EXPENSE->value, AccountTypeEnum::IMPORT->value, AccountTypeEnum::INITIAL_BALANCE->value, AccountTypeEnum::LIABILITY_CREDIT->value, AccountTypeEnum::RECONCILIATION->value, AccountTypeEnum::REVENUE->value]; $allExceptAssets = [AccountTypeEnum::BENEFICIARY->value, AccountTypeEnum::CASH->value, AccountTypeEnum::CREDITCARD->value, AccountTypeEnum::DEFAULT->value, AccountTypeEnum::EXPENSE->value, AccountTypeEnum::IMPORT->value, AccountTypeEnum::INITIAL_BALANCE->value, AccountTypeEnum::LIABILITY_CREDIT->value, AccountTypeEnum::RECONCILIATION->value, AccountTypeEnum::REVENUE->value];
$all = [AccountTypeEnum::ASSET->value, AccountTypeEnum::BENEFICIARY->value, AccountTypeEnum::CASH->value, AccountTypeEnum::CREDITCARD->value, AccountTypeEnum::DEBT->value, AccountTypeEnum::DEFAULT->value, AccountTypeEnum::EXPENSE->value, AccountTypeEnum::IMPORT->value, AccountTypeEnum::INITIAL_BALANCE->value, AccountTypeEnum::LIABILITY_CREDIT->value, AccountTypeEnum::LOAN->value, AccountTypeEnum::MORTGAGE->value, AccountTypeEnum::RECONCILIATION->value]; $all = [AccountTypeEnum::ASSET->value, AccountTypeEnum::BENEFICIARY->value, AccountTypeEnum::CASH->value, AccountTypeEnum::CREDITCARD->value, AccountTypeEnum::DEBT->value, AccountTypeEnum::DEFAULT->value, AccountTypeEnum::EXPENSE->value, AccountTypeEnum::IMPORT->value, AccountTypeEnum::INITIAL_BALANCE->value, AccountTypeEnum::LIABILITY_CREDIT->value, AccountTypeEnum::LOAN->value, AccountTypeEnum::MORTGAGE->value, AccountTypeEnum::RECONCILIATION->value];
@@ -176,14 +176,14 @@ class DestroyController extends Controller
foreach ($collection as $account) { foreach ($collection as $account) {
$count = $account->transactions()->count(); $count = $account->transactions()->count();
if (true === $this->unused && 0 === $count) { if (true === $this->unused && 0 === $count) {
app('log')->info(sprintf('Deleted unused account #%d "%s"', $account->id, $account->name)); Log::info(sprintf('Deleted unused account #%d "%s"', $account->id, $account->name));
Log::channel('audit')->info(sprintf('Deleted unused account #%d "%s"', $account->id, $account->name)); Log::channel('audit')->info(sprintf('Deleted unused account #%d "%s"', $account->id, $account->name));
$service->destroy($account, null); $service->destroy($account, null);
continue; continue;
} }
if (false === $this->unused) { if (false === $this->unused) {
app('log')->info(sprintf('Deleting account #%d "%s"', $account->id, $account->name)); Log::info(sprintf('Deleting account #%d "%s"', $account->id, $account->name));
Log::channel('audit')->warning(sprintf('Deleted account #%d "%s"', $account->id, $account->name)); Log::channel('audit')->warning(sprintf('Deleted account #%d "%s"', $account->id, $account->name));
$service->destroy($account, null); $service->destroy($account, null);
} }

View File

@@ -29,6 +29,7 @@ use FireflyIII\Api\V1\Requests\Data\Export\ExportRequest;
use FireflyIII\Exceptions\FireflyException; use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Support\Export\ExportDataGenerator; use FireflyIII\Support\Export\ExportDataGenerator;
use Illuminate\Http\Response as LaravelResponse; use Illuminate\Http\Response as LaravelResponse;
use Safe\Exceptions\DatetimeException;
use function Safe\date; use function Safe\date;
@@ -72,6 +73,7 @@ class ExportController extends Controller
/** /**
* @throws FireflyException * @throws FireflyException
* @throws DatetimeException
*/ */
private function returnExport(string $key): LaravelResponse private function returnExport(string $key): LaravelResponse
{ {

View File

@@ -32,6 +32,7 @@ use FireflyIII\Support\JsonApi\Enrichments\AccountEnrichment;
use FireflyIII\Transformers\AccountTransformer; use FireflyIII\Transformers\AccountTransformer;
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;
/** /**
@@ -67,7 +68,7 @@ class UpdateController extends Controller
*/ */
public function update(UpdateRequest $request, Account $account): JsonResponse public function update(UpdateRequest $request, Account $account): JsonResponse
{ {
app('log')->debug(sprintf('Now in %s', __METHOD__)); Log::debug(sprintf('Now in %s', __METHOD__));
$data = $request->getUpdateData(); $data = $request->getUpdateData();
$data['type'] = config('firefly.shortNamesByFullName.'.$account->accountType->type); $data['type'] = config('firefly.shortNamesByFullName.'.$account->accountType->type);
$account = $this->repository->update($account, $data); $account = $this->repository->update($account, $data);

View File

@@ -80,7 +80,7 @@ class StoreController extends Controller
throw new NotFoundHttpException(); throw new NotFoundHttpException();
} }
app('log')->debug(sprintf('Now in %s', __METHOD__)); Log::debug(sprintf('Now in %s', __METHOD__));
$data = $request->getAll(); $data = $request->getAll();
$attachment = $this->repository->store($data); $attachment = $this->repository->store($data);
$manager = $this->getManager(); $manager = $this->getManager();
@@ -109,13 +109,13 @@ class StoreController extends Controller
$helper = app(AttachmentHelperInterface::class); $helper = app(AttachmentHelperInterface::class);
$body = $request->getContent(); $body = $request->getContent();
if ('' === $body) { if ('' === $body) {
app('log')->error('Body of attachment is empty.'); Log::error('Body of attachment is empty.');
return response()->json([], 422); return response()->json([], 422);
} }
$result = $helper->saveAttachmentFromApi($attachment, $body); $result = $helper->saveAttachmentFromApi($attachment, $body);
if (false === $result) { if (false === $result) {
app('log')->error('Could not save attachment from API.'); Log::error('Could not save attachment from API.');
return response()->json([], 422); return response()->json([], 422);
} }

View File

@@ -69,7 +69,6 @@ class StoreController extends Controller
$data = $request->getAll(); $data = $request->getAll();
$data['start_date'] = $data['start']; $data['start_date'] = $data['start'];
$data['end_date'] = $data['end']; $data['end_date'] = $data['end'];
$data['notes'] = $data['notes'];
$data['budget_id'] = $budget->id; $data['budget_id'] = $budget->id;
$budgetLimit = $this->blRepository->store($data); $budgetLimit = $this->blRepository->store($data);

View File

@@ -34,6 +34,7 @@ use FireflyIII\Repositories\Journal\JournalRepositoryInterface;
use FireflyIII\Repositories\TransactionGroup\TransactionGroupRepository; use FireflyIII\Repositories\TransactionGroup\TransactionGroupRepository;
use FireflyIII\User; use FireflyIII\User;
use Illuminate\Http\JsonResponse; use Illuminate\Http\JsonResponse;
use Illuminate\Support\Facades\Log;
/** /**
* Class DestroyController * Class DestroyController
@@ -73,7 +74,7 @@ class DestroyController extends Controller
*/ */
public function destroy(TransactionGroup $transactionGroup): JsonResponse public function destroy(TransactionGroup $transactionGroup): JsonResponse
{ {
app('log')->debug(sprintf('Now in %s', __METHOD__)); Log::debug(sprintf('Now in %s', __METHOD__));
// grab asset account(s) from group: // grab asset account(s) from group:
$accounts = []; $accounts = [];
@@ -95,7 +96,7 @@ class DestroyController extends Controller
/** @var Account $account */ /** @var Account $account */
foreach ($accounts as $account) { foreach ($accounts as $account) {
app('log')->debug(sprintf('Now going to trigger updated account event for account #%d', $account->id)); Log::debug(sprintf('Now going to trigger updated account event for account #%d', $account->id));
event(new UpdatedAccount($account)); event(new UpdatedAccount($account));
} }

View File

@@ -84,7 +84,7 @@ class StoreController extends Controller
*/ */
public function store(StoreRequest $request): JsonResponse public function store(StoreRequest $request): JsonResponse
{ {
app('log')->debug('Now in API StoreController::store()'); Log::debug('Now in API StoreController::store()');
$data = $request->getAll(); $data = $request->getAll();
$data['user'] = auth()->user(); $data['user'] = auth()->user();
$data['user_group'] = $this->userGroup; $data['user_group'] = $this->userGroup;
@@ -95,13 +95,13 @@ class StoreController extends Controller
try { try {
$transactionGroup = $this->groupRepository->store($data); $transactionGroup = $this->groupRepository->store($data);
} catch (DuplicateTransactionException $e) { } catch (DuplicateTransactionException $e) {
app('log')->warning('Caught a duplicate transaction. Return error message.'); Log::warning('Caught a duplicate transaction. Return error message.');
$validator = Validator::make(['transactions' => [['description' => $e->getMessage()]]], ['transactions.0.description' => new IsDuplicateTransaction()]); $validator = Validator::make(['transactions' => [['description' => $e->getMessage()]]], ['transactions.0.description' => new IsDuplicateTransaction()]);
throw new ValidationException($validator); throw new ValidationException($validator);
} catch (FireflyException $e) { } catch (FireflyException $e) {
app('log')->warning('Caught an exception. Return error message.'); Log::warning('Caught an exception. Return error message.');
app('log')->error($e->getMessage()); Log::error($e->getMessage());
$message = sprintf('Internal exception: %s', $e->getMessage()); $message = sprintf('Internal exception: %s', $e->getMessage());
$validator = Validator::make(['transactions' => [['description' => $message]]], ['transactions.0.description' => new IsDuplicateTransaction()]); $validator = Validator::make(['transactions' => [['description' => $message]]], ['transactions.0.description' => new IsDuplicateTransaction()]);

View File

@@ -72,27 +72,25 @@ 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'); 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 +99,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

@@ -32,6 +32,7 @@ use FireflyIII\Repositories\Currency\CurrencyRepositoryInterface;
use FireflyIII\Repositories\User\UserRepositoryInterface; use FireflyIII\Repositories\User\UserRepositoryInterface;
use FireflyIII\User; use FireflyIII\User;
use Illuminate\Http\JsonResponse; use Illuminate\Http\JsonResponse;
use Illuminate\Validation\ValidationException;
/** /**
* Class DestroyController * Class DestroyController
@@ -65,6 +66,7 @@ class DestroyController extends Controller
* Remove the specified resource from storage. * Remove the specified resource from storage.
* *
* @throws FireflyException * @throws FireflyException
* @throws ValidationException
*/ */
public function destroy(TransactionCurrency $currency): JsonResponse public function destroy(TransactionCurrency $currency): JsonResponse
{ {

View File

@@ -250,10 +250,8 @@ class ListController extends Controller
$collection = $unfiltered->filter( $collection = $unfiltered->filter(
static function (Recurrence $recurrence) use ($currency) { // @phpstan-ignore-line static function (Recurrence $recurrence) use ($currency) { // @phpstan-ignore-line
/** @var RecurrenceTransaction $transaction */ /** @var RecurrenceTransaction $transaction */
foreach ($recurrence->recurrenceTransactions as $transaction) { if (array_any($recurrence->recurrenceTransactions, fn ($transaction) => $transaction->transaction_currency_id === $currency->id || $transaction->foreign_currency_id === $currency->id)) {
if ($transaction->transaction_currency_id === $currency->id || $transaction->foreign_currency_id === $currency->id) { return $recurrence;
return $recurrence;
}
} }
return null; return null;
@@ -297,10 +295,8 @@ class ListController extends Controller
$collection = $unfiltered->filter( $collection = $unfiltered->filter(
static function (Rule $rule) use ($currency) { // @phpstan-ignore-line static function (Rule $rule) use ($currency) { // @phpstan-ignore-line
/** @var RuleTrigger $trigger */ /** @var RuleTrigger $trigger */
foreach ($rule->ruleTriggers as $trigger) { if (array_any($rule->ruleTriggers, fn ($trigger) => 'currency_is' === $trigger->trigger_type && $currency->name === $trigger->trigger_value)) {
if ('currency_is' === $trigger->trigger_type && $currency->name === $trigger->trigger_value) { return $rule;
return $rule;
}
} }
return null; return null;

View File

@@ -27,13 +27,13 @@ namespace FireflyIII\Api\V1\Controllers\Models\TransactionLinkType;
use Illuminate\Support\Facades\Validator; use Illuminate\Support\Facades\Validator;
use FireflyIII\Api\V1\Controllers\Controller; use FireflyIII\Api\V1\Controllers\Controller;
use FireflyIII\Api\V1\Requests\Models\TransactionLinkType\StoreRequest; use FireflyIII\Api\V1\Requests\Models\TransactionLinkType\StoreRequest;
use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Repositories\LinkType\LinkTypeRepositoryInterface; use FireflyIII\Repositories\LinkType\LinkTypeRepositoryInterface;
use FireflyIII\Repositories\User\UserRepositoryInterface; use FireflyIII\Repositories\User\UserRepositoryInterface;
use FireflyIII\Support\Http\Api\TransactionFilter; use FireflyIII\Support\Http\Api\TransactionFilter;
use FireflyIII\Transformers\LinkTypeTransformer; use FireflyIII\Transformers\LinkTypeTransformer;
use FireflyIII\User; use FireflyIII\User;
use Illuminate\Http\JsonResponse; use Illuminate\Http\JsonResponse;
use Illuminate\Validation\ValidationException;
use League\Fractal\Resource\Item; use League\Fractal\Resource\Item;
/** /**
@@ -71,7 +71,7 @@ class StoreController extends Controller
* *
* Store new object. * Store new object.
* *
* @throws FireflyException * @throws ValidationException
*/ */
public function store(StoreRequest $request): JsonResponse public function store(StoreRequest $request): JsonResponse
{ {

View File

@@ -35,6 +35,7 @@ use FireflyIII\Support\Http\Api\TransactionFilter;
use FireflyIII\Transformers\LinkTypeTransformer; use FireflyIII\Transformers\LinkTypeTransformer;
use FireflyIII\User; use FireflyIII\User;
use Illuminate\Http\JsonResponse; use Illuminate\Http\JsonResponse;
use Illuminate\Validation\ValidationException;
use League\Fractal\Resource\Item; use League\Fractal\Resource\Item;
/** /**
@@ -73,6 +74,7 @@ class UpdateController extends Controller
* Update object. * Update object.
* *
* @throws FireflyException * @throws FireflyException
* @throws ValidationException
*/ */
public function update(UpdateRequest $request, LinkType $linkType): JsonResponse public function update(UpdateRequest $request, LinkType $linkType): JsonResponse
{ {

View File

@@ -30,6 +30,7 @@ use FireflyIII\Models\UserGroup;
use FireflyIII\Repositories\UserGroup\UserGroupRepositoryInterface; use FireflyIII\Repositories\UserGroup\UserGroupRepositoryInterface;
use FireflyIII\Transformers\UserGroupTransformer; use FireflyIII\Transformers\UserGroupTransformer;
use Illuminate\Http\JsonResponse; use Illuminate\Http\JsonResponse;
use Illuminate\Support\Facades\Log;
class UpdateController extends Controller class UpdateController extends Controller
{ {
@@ -54,7 +55,7 @@ class UpdateController extends Controller
public function update(UpdateRequest $request, UserGroup $userGroup): JsonResponse public function update(UpdateRequest $request, UserGroup $userGroup): JsonResponse
{ {
app('log')->debug(sprintf('Now in %s', __METHOD__)); Log::debug(sprintf('Now in %s', __METHOD__));
$data = $request->getData(); $data = $request->getData();
$userGroup = $this->repository->update($userGroup, $data); $userGroup = $this->repository->update($userGroup, $data);
$userGroup->refresh(); $userGroup->refresh();

View File

@@ -34,6 +34,7 @@ use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request; use Illuminate\Http\Request;
use Illuminate\Http\Response; use Illuminate\Http\Response;
use Illuminate\Pagination\LengthAwarePaginator; use Illuminate\Pagination\LengthAwarePaginator;
use Illuminate\Support\Facades\Log;
use League\Fractal\Pagination\IlluminatePaginatorAdapter; use League\Fractal\Pagination\IlluminatePaginatorAdapter;
use League\Fractal\Resource\Collection as FractalCollection; use League\Fractal\Resource\Collection as FractalCollection;
@@ -71,7 +72,7 @@ class AccountController extends Controller
if ('' === $query || !in_array($field, $this->validFields, true)) { if ('' === $query || !in_array($field, $this->validFields, true)) {
return response(null, 422); return response(null, 422);
} }
app('log')->debug(sprintf('Now in account search("%s", "%s")', $field, $query)); Log::debug(sprintf('Now in account search("%s", "%s")', $field, $query));
$types = $this->mapAccountTypes($type); $types = $this->mapAccountTypes($type);
/** @var AccountSearch $search */ /** @var AccountSearch $search */

View File

@@ -130,8 +130,6 @@ class BasicController extends Controller
$convertToNative = Amount::convertToNative(); $convertToNative = Amount::convertToNative();
$default = Amount::getNativeCurrency(); $default = Amount::getNativeCurrency();
// prep some arrays: // prep some arrays:
$incomes = [];
$expenses = [];
$sums = []; $sums = [];
$return = []; $return = [];
$currencies = [ $currencies = [

View File

@@ -24,6 +24,8 @@ declare(strict_types=1);
namespace FireflyIII\Api\V1\Controllers\System; namespace FireflyIII\Api\V1\Controllers\System;
use FireflyIII\Support\Facades\FireflyConfig;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Validator; use Illuminate\Support\Facades\Validator;
use FireflyIII\Api\V1\Controllers\Controller; use FireflyIII\Api\V1\Controllers\Controller;
use FireflyIII\Api\V1\Requests\System\UpdateRequest; use FireflyIII\Api\V1\Requests\System\UpdateRequest;
@@ -31,6 +33,7 @@ use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Repositories\User\UserRepositoryInterface; use FireflyIII\Repositories\User\UserRepositoryInterface;
use FireflyIII\Support\Binder\EitherConfigKey; use FireflyIII\Support\Binder\EitherConfigKey;
use Illuminate\Http\JsonResponse; use Illuminate\Http\JsonResponse;
use Illuminate\Validation\ValidationException;
/** /**
* Class ConfigurationController * Class ConfigurationController
@@ -65,8 +68,8 @@ class ConfigurationController extends Controller
try { try {
$dynamicData = $this->getDynamicConfiguration(); $dynamicData = $this->getDynamicConfiguration();
} catch (FireflyException $e) { } catch (FireflyException $e) {
app('log')->error($e->getMessage()); Log::error($e->getMessage());
app('log')->error($e->getTraceAsString()); Log::error($e->getTraceAsString());
throw new FireflyException('200030: Could not load config variables.', 0, $e); throw new FireflyException('200030: Could not load config variables.', 0, $e);
} }
@@ -92,13 +95,15 @@ class ConfigurationController extends Controller
/** /**
* Get all config values. * Get all config values.
*
* @throws FireflyException
*/ */
private function getDynamicConfiguration(): array private function getDynamicConfiguration(): array
{ {
$isDemoSite = app('fireflyconfig')->get('is_demo_site'); $isDemoSite = FireflyConfig::get('is_demo_site');
$updateCheck = app('fireflyconfig')->get('permission_update_check'); $updateCheck = FireflyConfig::get('permission_update_check');
$lastCheck = app('fireflyconfig')->get('last_update_check'); $lastCheck = FireflyConfig::get('last_update_check');
$singleUser = app('fireflyconfig')->get('single_user_mode'); $singleUser = FireflyConfig::get('single_user_mode');
return [ return [
'is_demo_site' => $isDemoSite?->data, 'is_demo_site' => $isDemoSite?->data,
@@ -153,6 +158,7 @@ class ConfigurationController extends Controller
* Update the configuration. * Update the configuration.
* *
* @throws FireflyException * @throws FireflyException
* @throws ValidationException
*/ */
public function update(UpdateRequest $request, string $name): JsonResponse public function update(UpdateRequest $request, string $name): JsonResponse
{ {
@@ -164,7 +170,7 @@ class ConfigurationController extends Controller
$data = $request->getAll(); $data = $request->getAll();
$shortName = str_replace('configuration.', '', $name); $shortName = str_replace('configuration.', '', $name);
app('fireflyconfig')->set($shortName, $data['value']); FireflyConfig::set($shortName, $data['value']);
// get updated config: // get updated config:
$newConfig = $this->getDynamicConfiguration(); $newConfig = $this->getDynamicConfiguration();

View File

@@ -28,6 +28,7 @@ use FireflyIII\Api\V1\Controllers\Controller;
use FireflyIII\Api\V1\Requests\System\CronRequest; use FireflyIII\Api\V1\Requests\System\CronRequest;
use FireflyIII\Support\Http\Controllers\CronRunner; use FireflyIII\Support\Http\Controllers\CronRunner;
use Illuminate\Http\JsonResponse; use Illuminate\Http\JsonResponse;
use Illuminate\Support\Facades\Log;
/** /**
* Class CronController * Class CronController
@@ -44,8 +45,8 @@ class CronController extends Controller
{ {
$config = $request->getAll(); $config = $request->getAll();
app('log')->debug(sprintf('Now in %s', __METHOD__)); Log::debug(sprintf('Now in %s', __METHOD__));
app('log')->debug(sprintf('Date is %s', $config['date']->toIsoString())); Log::debug(sprintf('Date is %s', $config['date']->toIsoString()));
$return = []; $return = [];
$return['recurring_transactions'] = $this->runRecurring($config['force'], $config['date']); $return['recurring_transactions'] = $this->runRecurring($config['force'], $config['date']);
$return['auto_budgets'] = $this->runAutoBudget($config['force'], $config['date']); $return['auto_budgets'] = $this->runAutoBudget($config['force'], $config['date']);

View File

@@ -33,6 +33,7 @@ use FireflyIII\Transformers\UserTransformer;
use FireflyIII\User; use FireflyIII\User;
use Illuminate\Http\JsonResponse; use Illuminate\Http\JsonResponse;
use Illuminate\Pagination\LengthAwarePaginator; use Illuminate\Pagination\LengthAwarePaginator;
use Illuminate\Support\Facades\Log;
use League\Fractal\Pagination\IlluminatePaginatorAdapter; use League\Fractal\Pagination\IlluminatePaginatorAdapter;
use League\Fractal\Resource\Collection as FractalCollection; use League\Fractal\Resource\Collection as FractalCollection;
use League\Fractal\Resource\Item; use League\Fractal\Resource\Item;
@@ -174,7 +175,7 @@ class UserController extends Controller
// can only update 'blocked' when user is admin. // can only update 'blocked' when user is admin.
if (!$this->repository->hasRole(auth()->user(), 'owner')) { if (!$this->repository->hasRole(auth()->user(), 'owner')) {
app('log')->debug('Quietly drop fields "blocked" and "blocked_code" from request.'); Log::debug('Quietly drop fields "blocked" and "blocked_code" from request.');
unset($data['blocked'], $data['blocked_code']); unset($data['blocked'], $data['blocked_code']);
} }

View File

@@ -138,7 +138,7 @@ class ShowController extends Controller
throw new NotFoundHttpException('Webhooks are not enabled.'); throw new NotFoundHttpException('Webhooks are not enabled.');
} }
app('log')->debug(sprintf('Now in triggerTransaction(%d, %d)', $webhook->id, $group->id)); Log::debug(sprintf('Now in triggerTransaction(%d, %d)', $webhook->id, $group->id));
Log::channel('audit')->info(sprintf('User triggers webhook #%d on transaction group #%d.', $webhook->id, $group->id)); Log::channel('audit')->info(sprintf('User triggers webhook #%d on transaction group #%d.', $webhook->id, $group->id));
/** @var MessageGeneratorInterface $engine */ /** @var MessageGeneratorInterface $engine */
@@ -155,7 +155,7 @@ class ShowController extends Controller
$engine->generateMessages(); $engine->generateMessages();
// trigger event to send them: // trigger event to send them:
app('log')->debug('send event RequestedSendWebhookMessages'); Log::debug('send event RequestedSendWebhookMessages');
event(new RequestedSendWebhookMessages()); event(new RequestedSendWebhookMessages());
return response()->json([], 204); return response()->json([], 204);

View File

@@ -76,6 +76,6 @@ class SubmitController extends Controller
SendWebhookMessage::dispatch($message)->afterResponse(); SendWebhookMessage::dispatch($message)->afterResponse();
} }
return response()->json([]); return response()->json();
} }
} }

View File

@@ -54,8 +54,8 @@ class TransactionRequest extends FormRequest
'query' => json_decode($this->get('query'), true, 8, JSON_THROW_ON_ERROR), 'query' => json_decode($this->get('query'), true, 8, JSON_THROW_ON_ERROR),
]; ];
} catch (JsonException $e) { } catch (JsonException $e) {
// dont really care. the validation should catch invalid json. // don't really care. the validation should catch invalid json.
app('log')->error($e->getMessage()); Log::error($e->getMessage());
} }
return $data; return $data;

View File

@@ -47,7 +47,7 @@ class StoreRequest extends FormRequest
*/ */
public function getAll(): array public function getAll(): array
{ {
app('log')->debug('Raw fields in Bill StoreRequest', $this->all()); Log::debug('Raw fields in Bill StoreRequest', $this->all());
$fields = [ $fields = [
'name' => ['name', 'convertString'], 'name' => ['name', 'convertString'],
'amount_min' => ['amount_min', 'convertString'], 'amount_min' => ['amount_min', 'convertString'],
@@ -128,7 +128,6 @@ class StoreRequest extends FormRequest
$failed = $validator->fails(); $failed = $validator->fails();
} catch (TypeError $e) { } catch (TypeError $e) {
Log::error($e->getMessage()); Log::error($e->getMessage());
$failed = false;
} }
if ($failed) { if ($failed) {
Log::channel('audit')->error(sprintf('Validation errors in %s', self::class), $validator->errors()->toArray()); Log::channel('audit')->error(sprintf('Validation errors in %s', self::class), $validator->errors()->toArray());

View File

@@ -76,7 +76,7 @@ class StoreRequest extends FormRequest
/** /**
* Returns the transaction data as it is found in the submitted data. It's a complex method according to code * Returns the transaction data as it is found in the submitted data. It's a complex method according to code
* standards but it just has a lot of ??-statements because of the fields that may or may not exist. * standards, but it just has a lot of ??-statements because of the fields that may or may not exist.
*/ */
private function getTransactionData(): array private function getTransactionData(): array
{ {

View File

@@ -122,7 +122,7 @@ class UpdateRequest extends FormRequest
/** /**
* Returns the transaction data as it is found in the submitted data. It's a complex method according to code * Returns the transaction data as it is found in the submitted data. It's a complex method according to code
* standards but it just has a lot of ??-statements because of the fields that may or may not exist. * standards, but it just has a lot of ??-statements because of the fields that may or may not exist.
*/ */
private function getTransactionData(): array private function getTransactionData(): array
{ {

View File

@@ -58,7 +58,7 @@ class StoreRequest extends FormRequest
*/ */
public function getAll(): array public function getAll(): array
{ {
app('log')->debug('get all data in TransactionStoreRequest'); Log::debug('get all data in TransactionStoreRequest');
return [ return [
'group_title' => $this->convertString('group_title'), 'group_title' => $this->convertString('group_title'),
@@ -175,7 +175,7 @@ class StoreRequest extends FormRequest
*/ */
public function rules(): array public function rules(): array
{ {
app('log')->debug('Collect rules of TransactionStoreRequest'); Log::debug('Collect rules of TransactionStoreRequest');
$validProtocols = config('firefly.valid_url_protocols'); $validProtocols = config('firefly.valid_url_protocols');
$locationRules = Location::requestRules([]); $locationRules = Location::requestRules([]);
@@ -276,9 +276,9 @@ class StoreRequest extends FormRequest
$this->validateTransactionArray($validator); $this->validateTransactionArray($validator);
// must submit at least one transaction. // must submit at least one transaction.
app('log')->debug('Now going to validateOneTransaction'); Log::debug('Now going to validateOneTransaction');
$this->validateOneTransaction($validator); $this->validateOneTransaction($validator);
app('log')->debug('Now done with validateOneTransaction'); Log::debug('Now done with validateOneTransaction');
// all journals must have a description // all journals must have a description
$this->validateDescriptions($validator); $this->validateDescriptions($validator);

View File

@@ -183,7 +183,7 @@ class UpdateRequest extends FormRequest
foreach ($this->dateFields as $fieldName) { foreach ($this->dateFields as $fieldName) {
app('log')->debug(sprintf('Now at date field %s', $fieldName)); app('log')->debug(sprintf('Now at date field %s', $fieldName));
if (array_key_exists($fieldName, $transaction)) { if (array_key_exists($fieldName, $transaction)) {
app('log')->debug(sprintf('New value: "%s"', (string) $transaction[$fieldName])); Log::debug(sprintf('New value: "%s"', $transaction[$fieldName]));
$current[$fieldName] = $this->dateFromValue((string) $transaction[$fieldName]); $current[$fieldName] = $this->dateFromValue((string) $transaction[$fieldName]);
} }
} }

View File

@@ -24,7 +24,6 @@ declare(strict_types=1);
namespace FireflyIII\Api\V1\Requests\Models\UserGroup; namespace FireflyIII\Api\V1\Requests\Models\UserGroup;
use FireflyIII\Models\UserGroup;
use FireflyIII\Support\Request\ChecksLogin; use FireflyIII\Support\Request\ChecksLogin;
use FireflyIII\Support\Request\ConvertsDataTypes; use FireflyIII\Support\Request\ConvertsDataTypes;
use Illuminate\Foundation\Http\FormRequest; use Illuminate\Foundation\Http\FormRequest;
@@ -53,9 +52,6 @@ class UpdateRequest extends FormRequest
*/ */
public function rules(): array public function rules(): array
{ {
/** @var UserGroup $userGroup */
$userGroup = $this->route()->parameter('userGroup');
return [ return [
'title' => ['required', 'min:1', 'max:255'], 'title' => ['required', 'min:1', 'max:255'],
'native_currency_id' => 'exists:transaction_currencies,id', 'native_currency_id' => 'exists:transaction_currencies,id',

View File

@@ -39,7 +39,6 @@ use League\Fractal\Resource\Collection as FractalCollection;
use League\Fractal\Resource\Item; use League\Fractal\Resource\Item;
use League\Fractal\Serializer\JsonApiSerializer; use League\Fractal\Serializer\JsonApiSerializer;
use Psr\Container\ContainerExceptionInterface; use Psr\Container\ContainerExceptionInterface;
use Psr\Container\NotFoundExceptionInterface;
use Symfony\Component\HttpFoundation\Exception\BadRequestException; use Symfony\Component\HttpFoundation\Exception\BadRequestException;
use Symfony\Component\HttpFoundation\ParameterBag; use Symfony\Component\HttpFoundation\ParameterBag;
@@ -82,7 +81,7 @@ class Controller extends BaseController
try { try {
$page = (int) request()->get('page'); $page = (int) request()->get('page');
} catch (ContainerExceptionInterface|NotFoundExceptionInterface $e) { } catch (ContainerExceptionInterface) {
$page = 1; $page = 1;
} }

View File

@@ -53,7 +53,7 @@ class ListController extends Controller
*/ */
public function index(): JsonResponse public function index(): JsonResponse
{ {
return response()->json([]); return response()->json();
// throw new FireflyException('Needs refactoring, move to IndexController.'); // throw new FireflyException('Needs refactoring, move to IndexController.');
// $pageSize = $this->parameters->get('limit'); // $pageSize = $this->parameters->get('limit');
// $dates = $request->getAll(); // $dates = $request->getAll();

View File

@@ -69,9 +69,8 @@ class UpdateController extends Controller
$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;
event(new UpdatedTransactionGroup($transactionGroup, $applyRules, $fireWebhooks, $amountChanged)); event(new UpdatedTransactionGroup($transactionGroup, $applyRules, $fireWebhooks, true));
app('preferences')->mark(); app('preferences')->mark();
/** @var User $admin */ /** @var User $admin */

View File

@@ -31,6 +31,7 @@ use FireflyIII\Repositories\Currency\CurrencyRepositoryInterface;
use FireflyIII\Transformers\CurrencyTransformer; use FireflyIII\Transformers\CurrencyTransformer;
use Illuminate\Http\JsonResponse; use Illuminate\Http\JsonResponse;
use Illuminate\Pagination\LengthAwarePaginator; use Illuminate\Pagination\LengthAwarePaginator;
use Illuminate\Support\Collection;
class IndexController extends Controller class IndexController extends Controller
{ {
@@ -56,6 +57,7 @@ class IndexController extends Controller
public function index(IndexRequest $request): JsonResponse public function index(IndexRequest $request): JsonResponse
{ {
$settings = $request->getAll(); $settings = $request->getAll();
$currencies = new Collection();
if (true === $settings['enabled']) { if (true === $settings['enabled']) {
$currencies = $this->repository->get(); $currencies = $this->repository->get();
} }

View File

@@ -68,7 +68,7 @@ class UpdateRequest extends Request
#[Override] #[Override]
public function getAll(): array public function getAll(): array
{ {
app('log')->debug(sprintf('Now in %s', __METHOD__)); Log::debug(sprintf('Now in %s', __METHOD__));
$this->integerFields = ['order', 'currency_id', 'foreign_currency_id', 'transaction_journal_id', 'source_id', 'destination_id', 'budget_id', 'category_id', 'bill_id', 'recurrence_id']; $this->integerFields = ['order', 'currency_id', 'foreign_currency_id', 'transaction_journal_id', 'source_id', 'destination_id', 'budget_id', 'category_id', 'bill_id', 'recurrence_id'];
$this->dateFields = ['date', 'interest_date', 'book_date', 'process_date', 'due_date', 'payment_date', 'invoice_date']; $this->dateFields = ['date', 'interest_date', 'book_date', 'process_date', 'due_date', 'payment_date', 'invoice_date'];
$this->textareaFields = ['notes']; $this->textareaFields = ['notes'];
@@ -101,7 +101,7 @@ class UpdateRequest extends Request
*/ */
private function getTransactionData(): array private function getTransactionData(): array
{ {
app('log')->debug(sprintf('Now in %s', __METHOD__)); Log::debug(sprintf('Now in %s', __METHOD__));
$return = []; $return = [];
/** @var null|array $transactions */ /** @var null|array $transactions */
@@ -185,9 +185,9 @@ class UpdateRequest extends Request
private function getDateData(array $current, array $transaction): array private function getDateData(array $current, array $transaction): array
{ {
foreach ($this->dateFields as $fieldName) { foreach ($this->dateFields as $fieldName) {
app('log')->debug(sprintf('Now at date field %s', $fieldName)); Log::debug(sprintf('Now at date field %s', $fieldName));
if (array_key_exists($fieldName, $transaction)) { if (array_key_exists($fieldName, $transaction)) {
app('log')->debug(sprintf('New value: "%s"', (string) $transaction[$fieldName])); Log::debug(sprintf('New value: "%s"', $transaction[$fieldName]));
$current[$fieldName] = $this->dateFromValue((string) $transaction[$fieldName]); $current[$fieldName] = $this->dateFromValue((string) $transaction[$fieldName]);
} }
} }
@@ -252,7 +252,7 @@ class UpdateRequest extends Request
#[Override] #[Override]
public function rules(): array public function rules(): array
{ {
app('log')->debug(sprintf('Now in %s', __METHOD__)); Log::debug(sprintf('Now in %s', __METHOD__));
$validProtocols = config('firefly.valid_url_protocols'); $validProtocols = config('firefly.valid_url_protocols');
return [ return [
@@ -336,7 +336,7 @@ class UpdateRequest extends Request
#[Override] #[Override]
public function withValidator(Validator $validator): void public function withValidator(Validator $validator): void
{ {
app('log')->debug('Now in withValidator'); Log::debug('Now in withValidator');
/** @var TransactionGroup $transactionGroup */ /** @var TransactionGroup $transactionGroup */
$transactionGroup = $this->route()->parameter('userGroupTransaction'); $transactionGroup = $this->route()->parameter('userGroupTransaction');

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

@@ -30,6 +30,7 @@ use FireflyIII\Enums\TransactionTypeEnum;
use FireflyIII\Models\Transaction; use FireflyIII\Models\Transaction;
use FireflyIII\Models\TransactionJournal; use FireflyIII\Models\TransactionJournal;
use FireflyIII\Repositories\Account\AccountRepositoryInterface; use FireflyIII\Repositories\Account\AccountRepositoryInterface;
use FireflyIII\Support\Facades\Steam;
use FireflyIII\Support\Models\AccountBalanceCalculator; use FireflyIII\Support\Models\AccountBalanceCalculator;
use Illuminate\Console\Command; use Illuminate\Console\Command;
use Illuminate\Support\Facades\DB; use Illuminate\Support\Facades\DB;
@@ -140,6 +141,7 @@ class CorrectsUnevenAmount extends Command
/** @var stdClass $entry */ /** @var stdClass $entry */
foreach ($journals as $entry) { foreach ($journals as $entry) {
$sum = (string) $entry->the_sum; $sum = (string) $entry->the_sum;
$sum = Steam::floatalize($sum);
if (!is_numeric($sum) if (!is_numeric($sum)
|| '' === $sum // @phpstan-ignore-line || '' === $sum // @phpstan-ignore-line
|| str_contains($sum, 'e') || str_contains($sum, 'e')

View File

@@ -26,6 +26,7 @@ namespace FireflyIII\Console\Commands\Integrity;
use FireflyIII\Console\Commands\ShowsFriendlyMessages; use FireflyIII\Console\Commands\ShowsFriendlyMessages;
use FireflyIII\Repositories\User\UserRepositoryInterface; use FireflyIII\Repositories\User\UserRepositoryInterface;
use FireflyIII\Support\Facades\Steam;
use FireflyIII\User; use FireflyIII\User;
use Illuminate\Console\Command; use Illuminate\Console\Command;
@@ -60,6 +61,8 @@ class ReportsSums extends Command
$foreign = (string) $user->transactions()->selectRaw('SUM(foreign_amount) as total')->value('total'); $foreign = (string) $user->transactions()->selectRaw('SUM(foreign_amount) as total')->value('total');
$sum = '' === $sum ? '0' : $sum; $sum = '' === $sum ? '0' : $sum;
$foreign = '' === $foreign ? '0' : $foreign; $foreign = '' === $foreign ? '0' : $foreign;
$sum = Steam::floatalize($sum);
$foreign = Steam::floatalize($foreign);
$total = bcadd($sum, $foreign); $total = bcadd($sum, $foreign);
if (0 !== bccomp($total, '0')) { if (0 !== bccomp($total, '0')) {

View File

@@ -87,6 +87,12 @@ class UpgradesMultiPiggyBanks extends Command
private function upgradePiggyBank(PiggyBank $piggyBank): void private function upgradePiggyBank(PiggyBank $piggyBank): void
{ {
if (null === $piggyBank->account) {
// #10432 account has been deleted, delete piggy bank.
$piggyBank->delete();
return;
}
$this->repository->setUser($piggyBank->account->user); $this->repository->setUser($piggyBank->account->user);
$this->accountRepository->setUser($piggyBank->account->user); $this->accountRepository->setUser($piggyBank->account->user);
$repetition = $this->repository->getRepetition($piggyBank, true); $repetition = $this->repository->getRepetition($piggyBank, true);

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

@@ -58,11 +58,13 @@ use function Safe\parse_url;
*/ */
class Handler extends ExceptionHandler class Handler extends ExceptionHandler
{ {
public static ?Throwable $lastError = null;
/** /**
* @var array<int, class-string<Throwable>> * @var array<int, class-string<Throwable>>
*/ */
protected $dontReport protected $dontReport
= [ = [
AuthenticationException::class, AuthenticationException::class,
LaravelValidationException::class, LaravelValidationException::class,
NotFoundHttpException::class, NotFoundHttpException::class,
@@ -123,7 +125,7 @@ class Handler extends ExceptionHandler
// somehow Laravel handler does not catch this: // somehow Laravel handler does not catch this:
app('log')->debug('Return JSON unauthenticated error.'); app('log')->debug('Return JSON unauthenticated error.');
return response()->json(['message' => 'Unauthenticated', 'exception' => 'AuthenticationException'], 401); return response()->json(['message' => $e->getMessage(), 'exception' => 'AuthenticationException'], 401);
} }
if ($e instanceof OAuthServerException && $expectsJson) { if ($e instanceof OAuthServerException && $expectsJson) {
@@ -215,13 +217,14 @@ class Handler extends ExceptionHandler
#[Override] #[Override]
public function report(Throwable $e): void public function report(Throwable $e): void
{ {
$doMailError = (bool) config('firefly.send_error_message'); self::$lastError = $e;
$doMailError = (bool) config('firefly.send_error_message');
if ($this->shouldntReportLocal($e) || !$doMailError) { if ($this->shouldntReportLocal($e) || !$doMailError) {
parent::report($e); parent::report($e);
return; return;
} }
$userData = [ $userData = [
'id' => 0, 'id' => 0,
'email' => 'unknown@example.com', 'email' => 'unknown@example.com',
]; ];
@@ -230,9 +233,9 @@ class Handler extends ExceptionHandler
$userData['email'] = auth()->user()->email; $userData['email'] = auth()->user()->email;
} }
$headers = request()->headers->all(); $headers = request()->headers->all();
$data = [ $data = [
'class' => $e::class, 'class' => $e::class,
'errorMessage' => $e->getMessage(), 'errorMessage' => $e->getMessage(),
'time' => Carbon::now()->format('r'), 'time' => Carbon::now()->format('r'),
@@ -250,8 +253,8 @@ class Handler extends ExceptionHandler
]; ];
// create job that will mail. // create job that will mail.
$ipAddress = request()->ip() ?? '0.0.0.0'; $ipAddress = request()->ip() ?? '0.0.0.0';
$job = new MailError($userData, (string) config('firefly.site_owner'), $ipAddress, $data); $job = new MailError($userData, (string) config('firefly.site_owner'), $ipAddress, $data);
dispatch($job); dispatch($job);
parent::report($e); parent::report($e);

View File

@@ -46,9 +46,9 @@ class PiggyBankFactory
{ {
use CreatesObjectGroups; use CreatesObjectGroups;
public User $user; public User $user;
private AccountRepositoryInterface $accountRepository; private AccountRepositoryInterface $accountRepository;
private CurrencyRepositoryInterface $currencyRepository; private CurrencyRepositoryInterface $currencyRepository;
private PiggyBankRepositoryInterface $piggyBankRepository; private PiggyBankRepositoryInterface $piggyBankRepository;
public function __construct() public function __construct()
@@ -78,7 +78,7 @@ class PiggyBankFactory
unset($piggyBankData['object_group_title'], $piggyBankData['transaction_currency_code'], $piggyBankData['transaction_currency_id'], $piggyBankData['accounts'], $piggyBankData['object_group_id'], $piggyBankData['notes']); unset($piggyBankData['object_group_title'], $piggyBankData['transaction_currency_code'], $piggyBankData['transaction_currency_id'], $piggyBankData['accounts'], $piggyBankData['object_group_id'], $piggyBankData['notes']);
// validate amount: // validate amount:
if (array_key_exists('target_amount', $piggyBankData) && '' === (string) $piggyBankData['target_amount']) { if (array_key_exists('target_amount', $piggyBankData) && '' === (string)$piggyBankData['target_amount']) {
$piggyBankData['target_amount'] = '0'; $piggyBankData['target_amount'] = '0';
} }
@@ -108,7 +108,7 @@ class PiggyBankFactory
} }
} }
// try also with ID // try also with ID
$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 ($objectGroup instanceof ObjectGroup) { if ($objectGroup instanceof ObjectGroup) {
@@ -129,10 +129,10 @@ class PiggyBankFactory
$defaultCurrency = app('amount')->getNativeCurrency(); $defaultCurrency = app('amount')->getNativeCurrency();
$currency = null; $currency = null;
if (array_key_exists('transaction_currency_code', $data)) { if (array_key_exists('transaction_currency_code', $data)) {
$currency = $this->currencyRepository->findByCode((string) ($data['transaction_currency_code'] ?? '')); $currency = $this->currencyRepository->findByCode((string)($data['transaction_currency_code'] ?? ''));
} }
if (array_key_exists('transaction_currency_id', $data)) { if (array_key_exists('transaction_currency_id', $data)) {
$currency = $this->currencyRepository->find((int) ($data['transaction_currency_id'] ?? 0)); $currency = $this->currencyRepository->find((int)($data['transaction_currency_id'] ?? 0));
} }
$currency ??= $defaultCurrency; $currency ??= $defaultCurrency;
@@ -141,8 +141,8 @@ class PiggyBankFactory
public function find(?int $piggyBankId, ?string $piggyBankName): ?PiggyBank public function find(?int $piggyBankId, ?string $piggyBankName): ?PiggyBank
{ {
$piggyBankId = (int) $piggyBankId; $piggyBankId = (int)$piggyBankId;
$piggyBankName = (string) $piggyBankName; $piggyBankName = (string)$piggyBankName;
if ('' === $piggyBankName && 0 === $piggyBankId) { if ('' === $piggyBankName && 0 === $piggyBankId) {
return null; return null;
} }
@@ -221,7 +221,7 @@ class PiggyBankFactory
private function getMaxOrder(): int private function getMaxOrder(): int
{ {
return (int) $this->piggyBankRepository->getPiggyBanks()->max('order'); return (int)$this->piggyBankRepository->getPiggyBanks()->max('order');
} }
@@ -230,12 +230,13 @@ class PiggyBankFactory
Log::debug(sprintf('Linking piggy bank #%d to %d accounts.', $piggyBank->id, count($accounts)), $accounts); Log::debug(sprintf('Linking piggy bank #%d to %d accounts.', $piggyBank->id, count($accounts)), $accounts);
// collect current current_amount so the sync does not remove them. // collect current current_amount so the sync does not remove them.
// TODO this is a tedious check. Feels like a hack. // TODO this is a tedious check. Feels like a hack.
$toBeLinked = []; $toBeLinked = [];
$oldSavedAmount = $this->piggyBankRepository->getCurrentAmount($piggyBank);
foreach ($piggyBank->accounts as $account) { foreach ($piggyBank->accounts as $account) {
Log::debug(sprintf('Checking account #%d', $account->id)); Log::debug(sprintf('Checking account #%d', $account->id));
foreach ($accounts as $info) { foreach ($accounts as $info) {
Log::debug(sprintf(' Checking other account #%d', $info['account_id'])); Log::debug(sprintf(' Checking other account #%d', $info['account_id']));
if ((int) $account->id === (int) $info['account_id']) { if ((int)$account->id === (int)$info['account_id']) {
$toBeLinked[$account->id] = ['current_amount' => $account->pivot->current_amount ?? '0']; $toBeLinked[$account->id] = ['current_amount' => $account->pivot->current_amount ?? '0'];
Log::debug(sprintf('Prefilled for account #%d with amount %s', $account->id, $account->pivot->current_amount ?? '0')); Log::debug(sprintf('Prefilled for account #%d with amount %s', $account->id, $account->pivot->current_amount ?? '0'));
} }
@@ -244,9 +245,9 @@ class PiggyBankFactory
/** @var array $info */ /** @var array $info */
foreach ($accounts as $info) { foreach ($accounts as $info) {
$account = $this->accountRepository->find((int) ($info['account_id'] ?? 0)); $account = $this->accountRepository->find((int)($info['account_id'] ?? 0));
if (!$account instanceof Account) { if (!$account instanceof Account) {
Log::debug(sprintf('Account #%d not found, skipping.', (int) ($info['account_id'] ?? 0))); Log::debug(sprintf('Account #%d not found, skipping.', (int)($info['account_id'] ?? 0)));
continue; continue;
} }
@@ -290,7 +291,16 @@ class PiggyBankFactory
} }
Log::debug(sprintf('Link information: %s', json_encode($toBeLinked))); Log::debug(sprintf('Link information: %s', json_encode($toBeLinked)));
if (0 !== count($toBeLinked)) { if (0 !== count($toBeLinked)) {
Log::debug('Syncing accounts to piggy bank.');
$piggyBank->accounts()->sync($toBeLinked); $piggyBank->accounts()->sync($toBeLinked);
$piggyBank->refresh();
$newSavedAmount = $this->piggyBankRepository->getCurrentAmount($piggyBank);
Log::debug(sprintf('Old saved amount: %s, new saved amount is %s', $oldSavedAmount, $newSavedAmount));
if (0 !== bccomp($oldSavedAmount, $newSavedAmount)) {
Log::debug('Amount changed, will create event for it.');
// create event for difference.
event(new ChangedAmount($piggyBank, bcsub($newSavedAmount, $oldSavedAmount), null, null));
}
} }
if (0 === count($toBeLinked)) { if (0 === count($toBeLinked)) {
Log::warning('No accounts to link to piggy bank, will not change whatever is there now.'); Log::warning('No accounts to link to piggy bank, will not change whatever is there now.');

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

@@ -23,6 +23,7 @@ declare(strict_types=1);
namespace FireflyIII\Handlers\Events; namespace FireflyIII\Handlers\Events;
use Deprecated;
use Carbon\Carbon; use Carbon\Carbon;
use FireflyIII\Events\RequestedVersionCheckStatus; use FireflyIII\Events\RequestedVersionCheckStatus;
use FireflyIII\Exceptions\FireflyException; use FireflyIII\Exceptions\FireflyException;
@@ -42,9 +43,8 @@ class VersionCheckEventHandler
* Checks with GitHub to see if there is a new version. * Checks with GitHub to see if there is a new version.
* *
* @throws FireflyException * @throws FireflyException
*
* @deprecated ?
*/ */
#[Deprecated(message: '?')]
public function checkForUpdates(RequestedVersionCheckStatus $event): void public function checkForUpdates(RequestedVersionCheckStatus $event): void
{ {
Log::debug('Now in checkForUpdates()'); Log::debug('Now in checkForUpdates()');

View File

@@ -24,6 +24,7 @@ declare(strict_types=1);
namespace FireflyIII\Helpers\Report; namespace FireflyIII\Helpers\Report;
use Deprecated;
use Carbon\Carbon; use Carbon\Carbon;
use FireflyIII\Enums\AccountTypeEnum; use FireflyIII\Enums\AccountTypeEnum;
use FireflyIII\Exceptions\FireflyException; use FireflyIII\Exceptions\FireflyException;
@@ -134,9 +135,7 @@ class NetWorth implements NetWorthInterface
$this->currencyRepos->setUserGroup($this->userGroup); $this->currencyRepos->setUserGroup($this->userGroup);
} }
/** #[Deprecated]
* @deprecated
*/
public function sumNetWorthByCurrency(Carbon $date): array public function sumNetWorthByCurrency(Carbon $date): array
{ {
/** /**

View File

@@ -24,6 +24,7 @@ declare(strict_types=1);
namespace FireflyIII\Helpers\Report; namespace FireflyIII\Helpers\Report;
use Deprecated;
use Carbon\Carbon; use Carbon\Carbon;
use FireflyIII\Models\UserGroup; use FireflyIII\Models\UserGroup;
use FireflyIII\User; use FireflyIII\User;
@@ -53,8 +54,7 @@ interface NetWorthInterface
* TODO move to repository * TODO move to repository
* *
* Same as above but cleaner function with less dependencies. * Same as above but cleaner function with less dependencies.
*
* @deprecated
*/ */
#[Deprecated]
public function sumNetWorthByCurrency(Carbon $date): array; public function sumNetWorthByCurrency(Carbon $date): array;
} }

View File

@@ -61,12 +61,12 @@ class ForgotPasswordController extends Controller
* *
* @return Factory|RedirectResponse|View * @return Factory|RedirectResponse|View
*/ */
public function sendResetLinkEmail(Request $request, ?UserRepositoryInterface $repository = null) public function sendResetLinkEmail(Request $request, UserRepositoryInterface $repository)
{ {
app('log')->info('Start of sendResetLinkEmail()'); Log::info('Start of sendResetLinkEmail()');
if ('web' !== config('firefly.authentication_guard')) { if ('web' !== config('firefly.authentication_guard')) {
$message = sprintf('Cannot reset password when authenticating over "%s".', config('firefly.authentication_guard')); $message = sprintf('Cannot reset password when authenticating over "%s".', config('firefly.authentication_guard'));
app('log')->error($message); Log::error($message);
return view('error', compact('message')); return view('error', compact('message'));
} }
@@ -89,7 +89,7 @@ class ForgotPasswordController extends Controller
// need to show to the user. Finally, we'll send out a proper response. // need to show to the user. Finally, we'll send out a proper response.
$result = $this->broker()->sendResetLink($request->only('email')); $result = $this->broker()->sendResetLink($request->only('email'));
if ('passwords.throttled' === $result) { if ('passwords.throttled' === $result) {
app('log')->error(sprintf('Cowardly refuse to send a password reset message to user #%d because the reset button has been throttled.', $user->id)); Log::error(sprintf('Cowardly refuse to send a password reset message to user #%d because the reset button has been throttled.', $user->id));
} }
// always send the same response to the user: // always send the same response to the user:

View File

@@ -223,7 +223,7 @@ class LoginController extends Controller
* *
* @throws FireflyException * @throws FireflyException
*/ */
public function showLoginForm(?Request $request = null) public function showLoginForm(Request $request)
{ {
Log::channel('audit')->info('Show login form (1.1).'); Log::channel('audit')->info('Show login form (1.1).');

View File

@@ -91,7 +91,7 @@ class IndexController extends Controller
public function index(?Carbon $start = null, ?Carbon $end = null) public function index(?Carbon $start = null, ?Carbon $end = null)
{ {
$this->abRepository->cleanup(); $this->abRepository->cleanup();
app('log')->debug(sprintf('Start of IndexController::index("%s", "%s")', $start?->format('Y-m-d'), $end?->format('Y-m-d'))); Log::debug(sprintf('Start of IndexController::index("%s", "%s")', $start?->format('Y-m-d'), $end?->format('Y-m-d')));
// collect some basic vars: // collect some basic vars:
$range = app('navigation')->getViewRange(true); $range = app('navigation')->getViewRange(true);
@@ -199,12 +199,12 @@ class IndexController extends Controller
// get all budgets, and paginate them into $budgets. // get all budgets, and paginate them into $budgets.
$collection = $this->repository->getActiveBudgets(); $collection = $this->repository->getActiveBudgets();
$budgets = []; $budgets = [];
app('log')->debug(sprintf('7) Start is "%s", end is "%s"', $start->format('Y-m-d H:i:s'), $end->format('Y-m-d H:i:s'))); Log::debug(sprintf('(getAllBudgets) Start is "%s", end is "%s"', $start->format('Y-m-d H:i:s'), $end->format('Y-m-d H:i:s')));
// complement budget with budget limits in range, and expenses in currency X in range. // complement budget with budget limits in range, and expenses in currency X in range.
/** @var Budget $current */ /** @var Budget $current */
foreach ($collection as $current) { foreach ($collection as $current) {
app('log')->debug(sprintf('Working on budget #%d ("%s")', $current->id, $current->name)); Log::debug(sprintf('Working on budget #%d ("%s")', $current->id, $current->name));
$array = $current->toArray(); $array = $current->toArray();
$array['spent'] = []; $array['spent'] = [];
$array['spent_total'] = []; $array['spent_total'] = [];
@@ -215,7 +215,7 @@ class IndexController extends Controller
/** @var BudgetLimit $limit */ /** @var BudgetLimit $limit */
foreach ($budgetLimits as $limit) { foreach ($budgetLimits as $limit) {
app('log')->debug(sprintf('Working on budget limit #%d', $limit->id)); Log::debug(sprintf('Working on budget limit #%d', $limit->id));
$currency = $limit->transactionCurrency ?? $defaultCurrency; $currency = $limit->transactionCurrency ?? $defaultCurrency;
$amount = app('steam')->bcround($limit->amount, $currency->decimal_places); $amount = app('steam')->bcround($limit->amount, $currency->decimal_places);
$array['budgeted'][] = [ $array['budgeted'][] = [
@@ -225,14 +225,17 @@ class IndexController extends Controller
'start_date' => $limit->start_date->isoFormat($this->monthAndDayFormat), 'start_date' => $limit->start_date->isoFormat($this->monthAndDayFormat),
'end_date' => $limit->end_date->isoFormat($this->monthAndDayFormat), 'end_date' => $limit->end_date->isoFormat($this->monthAndDayFormat),
'in_range' => $limit->start_date->isSameDay($start) && $limit->end_date->isSameDay($end), 'in_range' => $limit->start_date->isSameDay($start) && $limit->end_date->isSameDay($end),
'total_days' => $limit->start_date->diffInDays($limit->end_date) + 1,
'currency_id' => $currency->id, 'currency_id' => $currency->id,
'currency_symbol' => $currency->symbol, 'currency_symbol' => $currency->symbol,
'currency_name' => $currency->name, 'currency_name' => $currency->name,
'currency_decimal_places' => $currency->decimal_places, 'currency_decimal_places' => $currency->decimal_places,
]; ];
app('log')->debug(sprintf('The amount budgeted for budget limit #%d is %s %s', $limit->id, $currency->code, $amount)); Log::debug(sprintf('The amount budgeted for budget limit #%d is %s %s', $limit->id, $currency->code, $amount));
} }
// #10463
/** @var TransactionCurrency $currency */ /** @var TransactionCurrency $currency */
foreach ($currencies as $currency) { foreach ($currencies as $currency) {
$spentArr = $this->opsRepository->sumExpenses($start, $end, null, new Collection([$current]), $currency, false); $spentArr = $this->opsRepository->sumExpenses($start, $end, null, new Collection([$current]), $currency, false);
@@ -259,6 +262,8 @@ class IndexController extends Controller
/** @var array $budget */ /** @var array $budget */
foreach ($budgets as $budget) { foreach ($budgets as $budget) {
Log::debug(sprintf('Now working on budget #%d ("%s")', $budget['id'], $budget['name']));
/** @var array $spent */ /** @var array $spent */
foreach ($budget['spent'] as $spent) { foreach ($budget['spent'] as $spent) {
$currencyId = $spent['currency_id']; $currencyId = $spent['currency_id'];

View File

@@ -23,6 +23,7 @@ declare(strict_types=1);
namespace FireflyIII\Http\Controllers\Json; namespace FireflyIII\Http\Controllers\Json;
use Deprecated;
use Carbon\Carbon; use Carbon\Carbon;
use FireflyIII\Enums\AccountTypeEnum; use FireflyIII\Enums\AccountTypeEnum;
use FireflyIII\Enums\TransactionTypeEnum; use FireflyIII\Enums\TransactionTypeEnum;
@@ -46,9 +47,8 @@ class BoxController extends Controller
/** /**
* Deprecated method, no longer in use. * Deprecated method, no longer in use.
*
* @deprecated
*/ */
#[Deprecated]
public function available(): JsonResponse public function available(): JsonResponse
{ {
return response()->json([]); return response()->json([]);

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

@@ -34,6 +34,7 @@ use FireflyIII\TransactionRules\Engine\RuleEngineInterface;
use FireflyIII\User; use FireflyIII\User;
use Illuminate\Contracts\View\Factory; use Illuminate\Contracts\View\Factory;
use Illuminate\Http\RedirectResponse; use Illuminate\Http\RedirectResponse;
use Illuminate\Support\Collection;
use Illuminate\View\View; use Illuminate\View\View;
/** /**
@@ -75,7 +76,6 @@ class ExecutionController extends Controller
$accounts = implode(',', $request->get('accounts')); $accounts = implode(',', $request->get('accounts'));
$startDate = new Carbon($request->get('start')); $startDate = new Carbon($request->get('start'));
$endDate = new Carbon($request->get('end')); $endDate = new Carbon($request->get('end'));
$rules = $this->ruleGroupRepository->getActiveRules($ruleGroup);
// create new rule engine: // create new rule engine:
$newRuleEngine = app(RuleEngineInterface::class); $newRuleEngine = app(RuleEngineInterface::class);
$newRuleEngine->setUser($user); $newRuleEngine->setUser($user);
@@ -86,7 +86,9 @@ class ExecutionController extends Controller
$newRuleEngine->addOperator(['type' => 'account_id', 'value' => $accounts]); $newRuleEngine->addOperator(['type' => 'account_id', 'value' => $accounts]);
// set rules: // set rules:
$newRuleEngine->setRules($rules); // #10427, file rule group and not the set of rules.
$collection = new Collection([$ruleGroup]);
$newRuleEngine->setRuleGroups($collection);
$newRuleEngine->fire(); $newRuleEngine->fire();
// Tell the user that the job is queued // Tell the user that the job is queued

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

@@ -178,7 +178,7 @@ class Kernel extends HttpKernel
'api' => [ 'api' => [
AcceptHeaders::class, AcceptHeaders::class,
EnsureFrontendRequestsAreStateful::class, EnsureFrontendRequestsAreStateful::class,
'auth:api,sanctum', 'auth:api',
'bindings', 'bindings',
], ],
// do only bindings, no auth // do only bindings, no auth

View File

@@ -26,10 +26,12 @@ namespace FireflyIII\Http\Middleware;
use Closure; use Closure;
use FireflyIII\Exceptions\FireflyException; use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Exceptions\Handler;
use FireflyIII\User; use FireflyIII\User;
use Illuminate\Auth\AuthenticationException; use Illuminate\Auth\AuthenticationException;
use Illuminate\Contracts\Auth\Factory as Auth; use Illuminate\Contracts\Auth\Factory as Auth;
use Illuminate\Http\Request; use Illuminate\Http\Request;
use League\OAuth2\Server\Exception\OAuthServerException;
/** /**
* Class Authenticate * Class Authenticate
@@ -84,6 +86,7 @@ class Authenticate
if ($this->auth->check()) { if ($this->auth->check()) {
// do an extra check on user object. // do an extra check on user object.
/** @noinspection PhpUndefinedMethodInspection */ /** @noinspection PhpUndefinedMethodInspection */
/** @var User $user */ /** @var User $user */
$user = $this->auth->authenticate(); $user = $this->auth->authenticate();
$this->validateBlockedUser($user, $guards); $this->validateBlockedUser($user, $guards);
@@ -94,9 +97,6 @@ class Authenticate
} }
foreach ($guards as $guard) { foreach ($guards as $guard) {
if ('api' !== $guard) {
$this->auth->guard($guard)->authenticate();
}
$result = $this->auth->guard($guard)->check(); $result = $this->auth->guard($guard)->check();
if ($result) { if ($result) {
$user = $this->auth->guard($guard)->user(); $user = $this->auth->guard($guard)->user();
@@ -107,7 +107,14 @@ class Authenticate
} }
} }
throw new AuthenticationException('Unauthenticated.', $guards); // this is a massive hack, but if the hander has the oauth exception
// at this point we can report its error instead of a generic one.
$message = 'Unauthenticated.';
if (Handler::$lastError instanceof OAuthServerException) {
$message = Handler::$lastError->getHint();
}
throw new AuthenticationException($message, $guards);
} }
/** /**

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

@@ -24,6 +24,7 @@ declare(strict_types=1);
namespace FireflyIII\Repositories\Budget; namespace FireflyIII\Repositories\Budget;
use Deprecated;
use Carbon\Carbon; use Carbon\Carbon;
use FireflyIII\Models\AvailableBudget; use FireflyIII\Models\AvailableBudget;
use FireflyIII\Models\TransactionCurrency; use FireflyIII\Models\TransactionCurrency;
@@ -205,9 +206,7 @@ class AvailableBudgetRepository implements AvailableBudgetRepositoryInterface, U
; ;
} }
/** #[Deprecated]
* @deprecated
*/
public function setAvailableBudget(TransactionCurrency $currency, Carbon $start, Carbon $end, string $amount): AvailableBudget public function setAvailableBudget(TransactionCurrency $currency, Carbon $start, Carbon $end, string $amount): AvailableBudget
{ {
/** @var null|AvailableBudget */ /** @var null|AvailableBudget */

View File

@@ -24,6 +24,7 @@ declare(strict_types=1);
namespace FireflyIII\Repositories\Budget; namespace FireflyIII\Repositories\Budget;
use Deprecated;
use Carbon\Carbon; use Carbon\Carbon;
use FireflyIII\Enums\UserRoleEnum; use FireflyIII\Enums\UserRoleEnum;
use FireflyIII\Models\AvailableBudget; use FireflyIII\Models\AvailableBudget;
@@ -66,9 +67,7 @@ interface AvailableBudgetRepositoryInterface
*/ */
public function get(?Carbon $start = null, ?Carbon $end = null): Collection; public function get(?Carbon $start = null, ?Carbon $end = null): Collection;
/** #[Deprecated]
* @deprecated
*/
public function getAvailableBudget(TransactionCurrency $currency, Carbon $start, Carbon $end): string; public function getAvailableBudget(TransactionCurrency $currency, Carbon $start, Carbon $end): string;
public function getAvailableBudgetWithCurrency(Carbon $start, Carbon $end): array; public function getAvailableBudgetWithCurrency(Carbon $start, Carbon $end): array;
@@ -90,9 +89,7 @@ interface AvailableBudgetRepositoryInterface
*/ */
public function getByCurrencyDate(Carbon $start, Carbon $end, TransactionCurrency $currency): ?AvailableBudget; public function getByCurrencyDate(Carbon $start, Carbon $end, TransactionCurrency $currency): ?AvailableBudget;
/** #[Deprecated]
* @deprecated
*/
public function setAvailableBudget(TransactionCurrency $currency, Carbon $start, Carbon $end, string $amount): AvailableBudget; public function setAvailableBudget(TransactionCurrency $currency, Carbon $start, Carbon $end, string $amount): AvailableBudget;
public function store(array $data): ?AvailableBudget; public function store(array $data): ?AvailableBudget;

View File

@@ -24,6 +24,7 @@ declare(strict_types=1);
namespace FireflyIII\Repositories\Budget; namespace FireflyIII\Repositories\Budget;
use Deprecated;
use Carbon\Carbon; use Carbon\Carbon;
use FireflyIII\Enums\UserRoleEnum; use FireflyIII\Enums\UserRoleEnum;
use FireflyIII\Models\TransactionCurrency; use FireflyIII\Models\TransactionCurrency;
@@ -44,9 +45,7 @@ use Illuminate\Support\Collection;
*/ */
interface NoBudgetRepositoryInterface interface NoBudgetRepositoryInterface
{ {
/** #[Deprecated]
* @deprecated
*/
public function getNoBudgetPeriodReport(Collection $accounts, Carbon $start, Carbon $end): array; public function getNoBudgetPeriodReport(Collection $accounts, Carbon $start, Carbon $end): array;
public function sumExpenses(Carbon $start, Carbon $end, ?Collection $accounts = null, ?TransactionCurrency $currency = null): array; public function sumExpenses(Carbon $start, Carbon $end, ?Collection $accounts = null, ?TransactionCurrency $currency = null): array;

View File

@@ -24,6 +24,7 @@ declare(strict_types=1);
namespace FireflyIII\Repositories\Budget; namespace FireflyIII\Repositories\Budget;
use Deprecated;
use Carbon\Carbon; use Carbon\Carbon;
use FireflyIII\Enums\TransactionTypeEnum; use FireflyIII\Enums\TransactionTypeEnum;
use FireflyIII\Helpers\Collector\GroupCollectorInterface; use FireflyIII\Helpers\Collector\GroupCollectorInterface;
@@ -74,9 +75,8 @@ class OperationsRepository implements OperationsRepositoryInterface, UserGroupIn
/** /**
* This method is being used to generate the budget overview in the year/multi-year report. Its used * This method is being used to generate the budget overview in the year/multi-year report. Its used
* in both the year/multi-year budget overview AND in the accompanying chart. * in both the year/multi-year budget overview AND in the accompanying chart.
*
* @deprecated
*/ */
#[Deprecated]
public function getBudgetPeriodReport(Collection $budgets, Collection $accounts, Carbon $start, Carbon $end): array public function getBudgetPeriodReport(Collection $budgets, Collection $accounts, Carbon $start, Carbon $end): array
{ {
$carbonFormat = app('navigation')->preferredCarbonFormat($start, $end); $carbonFormat = app('navigation')->preferredCarbonFormat($start, $end);
@@ -204,7 +204,7 @@ class OperationsRepository implements OperationsRepositoryInterface, UserGroupIn
?TransactionCurrency $currency = null, ?TransactionCurrency $currency = null,
bool $convertToNative = false bool $convertToNative = false
): array { ): array {
Log::debug(sprintf('Start of %s(date, date, array, array, "%s", "%s").', __METHOD__, $currency?->code, var_export($convertToNative, true))); Log::debug(sprintf('Start of %s(date, date, array, array, "%s", %s).', __METHOD__, $currency?->code, var_export($convertToNative, true)));
// this collector excludes all transfers TO liabilities (which are also withdrawals) // this collector excludes all transfers TO liabilities (which are also withdrawals)
// because those expenses only become expenses once they move from the liability to the friend. // because those expenses only become expenses once they move from the liability to the friend.
// 2024-12-24 disable the exclusion for now. // 2024-12-24 disable the exclusion for now.

View File

@@ -24,6 +24,7 @@ declare(strict_types=1);
namespace FireflyIII\Repositories\Budget; namespace FireflyIII\Repositories\Budget;
use Deprecated;
use Carbon\Carbon; use Carbon\Carbon;
use FireflyIII\Enums\UserRoleEnum; use FireflyIII\Enums\UserRoleEnum;
use FireflyIII\Models\Budget; use FireflyIII\Models\Budget;
@@ -51,9 +52,7 @@ interface OperationsRepositoryInterface
*/ */
public function budgetedPerDay(Budget $budget): string; public function budgetedPerDay(Budget $budget): string;
/** #[Deprecated]
* @deprecated
*/
public function getBudgetPeriodReport(Collection $budgets, Collection $accounts, Carbon $start, Carbon $end): array; public function getBudgetPeriodReport(Collection $budgets, Collection $accounts, Carbon $start, Carbon $end): array;
/** /**
@@ -71,6 +70,7 @@ interface OperationsRepositoryInterface
Carbon $end, Carbon $end,
?Collection $accounts = null, ?Collection $accounts = null,
?Collection $budgets = null, ?Collection $budgets = null,
?TransactionCurrency $currency = null ?TransactionCurrency $currency = null,
bool $convertToNative = false
): array; ): array;
} }

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;
@@ -275,7 +276,7 @@ class PiggyBankRepository implements PiggyBankRepositoryInterface, UserGroupInte
$amount = '' === $amount ? '0' : $amount; $amount = '' === $amount ? '0' : $amount;
$sum = bcadd($sum, $amount); $sum = bcadd($sum, $amount);
} }
// Log::debug(sprintf('Current amount in piggy bank #%d ("%s") is %s', $piggyBank->id, $piggyBank->name, $sum)); Log::debug(sprintf('Current amount in piggy bank #%d ("%s") is %s', $piggyBank->id, $piggyBank->name, $sum));
return $sum; return $sum;
} }
@@ -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', (string) $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, (string) $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

@@ -24,6 +24,7 @@ declare(strict_types=1);
namespace FireflyIII\Services\Internal\Support; namespace FireflyIII\Services\Internal\Support;
use Deprecated;
use Carbon\Carbon; use Carbon\Carbon;
use FireflyIII\Enums\AccountTypeEnum; use FireflyIII\Enums\AccountTypeEnum;
use FireflyIII\Exceptions\DuplicateTransactionException; use FireflyIII\Exceptions\DuplicateTransactionException;
@@ -193,8 +194,8 @@ trait AccountServiceTrait
/** /**
* @throws FireflyException * @throws FireflyException
* * * *
* @deprecated
*/ */
#[Deprecated]
protected function createOBGroup(Account $account, array $data): TransactionGroup protected function createOBGroup(Account $account, array $data): TransactionGroup
{ {
app('log')->debug('Now going to create an OB group.'); app('log')->debug('Now going to create an OB group.');

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,19 @@ 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 = hash('sha256', sprintf('%s', Carbon::now()->getTimestamp()));
if (!$this->transactionGroup instanceof TransactionGroup) {
$compareHash = $this->transactionGroupRepository->getCompareHash($this->transactionJournal->transactionGroup);
}
if ($this->transactionGroup instanceof TransactionGroup) {
$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

@@ -23,6 +23,7 @@ declare(strict_types=1);
namespace FireflyIII\Support; namespace FireflyIII\Support;
use Deprecated;
use FireflyIII\Exceptions\FireflyException; use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Models\Transaction; use FireflyIII\Models\Transaction;
use FireflyIII\Models\TransactionCurrency; use FireflyIII\Models\TransactionCurrency;
@@ -195,25 +196,19 @@ class Amount
return $user->currencies()->orderBy('code', 'ASC')->get(); return $user->currencies()->orderBy('code', 'ASC')->get();
} }
/** #[Deprecated]
* @deprecated
*/
public function getDefaultCurrency(): TransactionCurrency public function getDefaultCurrency(): TransactionCurrency
{ {
return $this->getNativeCurrency(); return $this->getNativeCurrency();
} }
/** #[Deprecated(message: 'use getDefaultCurrencyByUserGroup instead')]
* @deprecated use getDefaultCurrencyByUserGroup instead
*/
public function getDefaultCurrencyByUser(User $user): TransactionCurrency public function getDefaultCurrencyByUser(User $user): TransactionCurrency
{ {
return $this->getDefaultCurrencyByUserGroup($user->userGroup); return $this->getDefaultCurrencyByUserGroup($user->userGroup);
} }
/** #[Deprecated]
* @deprecated
*/
public function getDefaultCurrencyByUserGroup(UserGroup $userGroup): TransactionCurrency public function getDefaultCurrencyByUserGroup(UserGroup $userGroup): TransactionCurrency
{ {
return $this->getNativeCurrencyByUserGroup($userGroup); return $this->getNativeCurrencyByUserGroup($userGroup);

View File

@@ -28,6 +28,8 @@ use Carbon\Carbon;
use FireflyIII\Exceptions\FireflyException; use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Jobs\CreateRecurringTransactions; use FireflyIII\Jobs\CreateRecurringTransactions;
use FireflyIII\Models\Configuration; use FireflyIII\Models\Configuration;
use FireflyIII\Support\Facades\FireflyConfig;
use Illuminate\Support\Facades\Log;
/** /**
* Class RecurringCronjob * Class RecurringCronjob
@@ -39,34 +41,34 @@ class RecurringCronjob extends AbstractCronjob
*/ */
public function fire(): void public function fire(): void
{ {
app('log')->debug(sprintf('Now in %s', __METHOD__)); Log::debug(sprintf('Now in %s', __METHOD__));
/** @var Configuration $config */ /** @var Configuration $config */
$config = app('fireflyconfig')->get('last_rt_job', 0); $config = FireflyConfig::get('last_rt_job', 0);
$lastTime = (int) $config->data; $lastTime = (int) $config->data;
$diff = Carbon::now()->getTimestamp() - $lastTime; $diff = Carbon::now()->getTimestamp() - $lastTime;
$diffForHumans = today(config('app.timezone'))->diffForHumans(Carbon::createFromTimestamp($lastTime), null, true); $diffForHumans = today(config('app.timezone'))->diffForHumans(Carbon::createFromTimestamp($lastTime), null, true);
if (0 === $lastTime) { if (0 === $lastTime) {
app('log')->info('Recurring transactions cron-job has never fired before.'); Log::info('Recurring transactions cron-job has never fired before.');
} }
// less than half a day ago: // less than half a day ago:
if ($lastTime > 0 && $diff <= 43200) { if ($lastTime > 0 && $diff <= 43200) {
app('log')->info(sprintf('It has been %s since the recurring transactions cron-job has fired.', $diffForHumans)); Log::info(sprintf('It has been "%s" since the recurring transactions cron-job has fired.', $diffForHumans));
if (false === $this->force) { if (false === $this->force) {
app('log')->info('The cron-job will not fire now.'); Log::info('The cron-job will not fire now.');
$this->message = sprintf('It has been %s since the recurring transactions cron-job has fired. It will not fire now.', $diffForHumans); $this->message = sprintf('It has been "%s" since the recurring transactions cron-job has fired. It will not fire now.', $diffForHumans);
$this->jobFired = false; $this->jobFired = false;
$this->jobErrored = false; $this->jobErrored = false;
$this->jobSucceeded = false; $this->jobSucceeded = false;
return; return;
} }
app('log')->info('Execution of the recurring transaction cron-job has been FORCED.'); Log::info('Execution of the recurring transaction cron-job has been FORCED.');
} }
if ($lastTime > 0 && $diff > 43200) { if ($lastTime > 0 && $diff > 43200) {
app('log')->info(sprintf('It has been %s since the recurring transactions cron-job has fired. It will fire now!', $diffForHumans)); Log::info(sprintf('It has been "%s" since the recurring transactions cron-job has fired. It will fire now!', $diffForHumans));
} }
$this->fireRecurring(); $this->fireRecurring();
@@ -76,7 +78,7 @@ class RecurringCronjob extends AbstractCronjob
private function fireRecurring(): void private function fireRecurring(): void
{ {
app('log')->info(sprintf('Will now fire recurring cron job task for date "%s".', $this->date->format('Y-m-d H:i:s'))); Log::info(sprintf('Will now fire recurring cron job task for date "%s".', $this->date->format('Y-m-d H:i:s')));
$job = new CreateRecurringTransactions($this->date); $job = new CreateRecurringTransactions($this->date);
$job->setForce($this->force); $job->setForce($this->force);
@@ -88,8 +90,8 @@ class RecurringCronjob extends AbstractCronjob
$this->jobSucceeded = true; $this->jobSucceeded = true;
$this->message = 'Recurring transactions cron job fired successfully.'; $this->message = 'Recurring transactions cron job fired successfully.';
app('fireflyconfig')->set('last_rt_job', (int) $this->date->format('U')); FireflyConfig::set('last_rt_job', (int) $this->date->format('U'));
app('log')->info(sprintf('Marked the last time this job has run as "%s" (%d)', $this->date->format('Y-m-d H:i:s'), (int) $this->date->format('U'))); Log::info(sprintf('Marked the last time this job has run as "%s" (%d)', $this->date->format('Y-m-d H:i:s'), (int) $this->date->format('U')));
app('log')->info('Done with recurring cron job task.'); Log::info('Done with recurring cron job task.');
} }
} }

View File

@@ -39,7 +39,7 @@ class FireflyConfig
{ {
public function delete(string $name): void public function delete(string $name): void
{ {
$fullName = 'ff-config-'.$name; $fullName = 'ff3-config-'.$name;
if (Cache::has($fullName)) { if (Cache::has($fullName)) {
Cache::forget($fullName); Cache::forget($fullName);
} }
@@ -81,7 +81,7 @@ class FireflyConfig
*/ */
public function get(string $name, mixed $default = null): ?Configuration public function get(string $name, mixed $default = null): ?Configuration
{ {
$fullName = 'ff-config-'.$name; $fullName = 'ff3-config-'.$name;
if (Cache::has($fullName)) { if (Cache::has($fullName)) {
return Cache::get($fullName); return Cache::get($fullName);
} }

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

@@ -26,6 +26,7 @@ namespace FireflyIII\Support\Http\Controllers;
use Carbon\Carbon; use Carbon\Carbon;
use FireflyIII\Exceptions\FireflyException; use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Support\Facades\FireflyConfig;
use Illuminate\Support\Facades\Log; use Illuminate\Support\Facades\Log;
/** /**
@@ -205,7 +206,7 @@ trait GetConfigurationData
protected function verifyRecurringCronJob(): void protected function verifyRecurringCronJob(): void
{ {
$config = app('fireflyconfig')->get('last_rt_job', 0); $config = FireflyConfig::get('last_rt_job', 0);
$lastTime = (int) $config?->data; $lastTime = (int) $config?->data;
$now = Carbon::now()->getTimestamp(); $now = Carbon::now()->getTimestamp();
app('log')->debug(sprintf('verifyRecurringCronJob: last time is %d ("%s"), now is %d', $lastTime, $config?->data, $now)); app('log')->debug(sprintf('verifyRecurringCronJob: last time is %d ("%s"), now is %d', $lastTime, $config?->data, $now));

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

@@ -54,7 +54,7 @@ class TransactionGroupEnrichment implements EnrichmentInterface
private array $notes; // @phpstan-ignore-line private array $notes; // @phpstan-ignore-line
private array $tags; private array $tags;
private User $user; private User $user;
private TransactionCurrency $nativeCurrency; private readonly TransactionCurrency $nativeCurrency;
private UserGroup $userGroup; private UserGroup $userGroup;
public function __construct() public function __construct()

View File

@@ -51,7 +51,7 @@ class TransactionSummarizer
public function groupByCurrencyId(array $journals, string $method = 'negative', bool $includeForeign = true): array public function groupByCurrencyId(array $journals, string $method = 'negative', bool $includeForeign = true): array
{ {
Log::debug(sprintf('Now in groupByCurrencyId([%d journals], "%s")', count($journals), $method)); Log::debug(sprintf('Now in groupByCurrencyId([%d journals], "%s", %s)', count($journals), $method, var_export($includeForeign, true)));
$array = []; $array = [];
foreach ($journals as $journal) { foreach ($journals as $journal) {
$field = 'amount'; $field = 'amount';

View File

@@ -278,6 +278,8 @@ class Steam
$carbonKey = $carbon->format('Y-m-d'); $carbonKey = $carbon->format('Y-m-d');
// make sure sum is a string: // make sure sum is a string:
$sumOfDay = (string) ($entry->sum_of_day ?? '0'); $sumOfDay = (string) ($entry->sum_of_day ?? '0');
// #10426 make sure sum is not in scientific notation.
$sumOfDay = $this->floatalize($sumOfDay);
// find currency of this entry, does not have to exist. // find currency of this entry, does not have to exist.
$currencies[$entry->transaction_currency_id] ??= TransactionCurrency::find($entry->transaction_currency_id); $currencies[$entry->transaction_currency_id] ??= TransactionCurrency::find($entry->transaction_currency_id);

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();
}
} }

View File

@@ -64,13 +64,13 @@ class SearchRuleEngine implements RuleEngineInterface
public function addOperator(array $operator): void public function addOperator(array $operator): void
{ {
app('log')->debug('Add extra operator: ', $operator); Log::debug('Add extra operator: ', $operator);
$this->operators[] = $operator; $this->operators[] = $operator;
} }
public function find(): Collection public function find(): Collection
{ {
app('log')->debug('SearchRuleEngine::find()'); Log::debug('SearchRuleEngine::find()');
$collection = new Collection(); $collection = new Collection();
foreach ($this->rules as $rule) { foreach ($this->rules as $rule) {
$found = new Collection(); $found = new Collection();
@@ -83,7 +83,7 @@ class SearchRuleEngine implements RuleEngineInterface
$collection = $collection->merge($found); $collection = $collection->merge($found);
} }
$result = $collection->unique(); $result = $collection->unique();
app('log')->debug(sprintf('SearchRuleEngine::find() returns %d unique transactions.', $result->count())); Log::debug(sprintf('SearchRuleEngine::find() returns %d unique transactions.', $result->count()));
return $result; return $result;
} }
@@ -93,7 +93,7 @@ class SearchRuleEngine implements RuleEngineInterface
*/ */
private function findStrictRule(Rule $rule): Collection private function findStrictRule(Rule $rule): Collection
{ {
app('log')->debug(sprintf('Now in findStrictRule(#%d)', $rule->id ?? 0)); Log::debug(sprintf('Now in findStrictRule(#%d)', $rule->id ?? 0));
$searchArray = []; $searchArray = [];
$triggers = []; $triggers = [];
if ($this->refreshTriggers) { if ($this->refreshTriggers) {
@@ -116,18 +116,18 @@ class SearchRuleEngine implements RuleEngineInterface
// if the trigger needs no context, value is different: // if the trigger needs no context, value is different:
$needsContext = (bool) (config(sprintf('search.operators.%s.needs_context', $contextSearch)) ?? true); $needsContext = (bool) (config(sprintf('search.operators.%s.needs_context', $contextSearch)) ?? true);
if (false === $needsContext) { if (false === $needsContext) {
app('log')->debug(sprintf('SearchRuleEngine:: add a rule trigger (no context): %s:true', $ruleTrigger->trigger_type)); Log::debug(sprintf('SearchRuleEngine:: add a rule trigger (no context): %s:true', $ruleTrigger->trigger_type));
$searchArray[$ruleTrigger->trigger_type][] = 'true'; $searchArray[$ruleTrigger->trigger_type][] = 'true';
} }
if (true === $needsContext) { if (true === $needsContext) {
app('log')->debug(sprintf('SearchRuleEngine:: add a rule trigger (context): %s:"%s"', $ruleTrigger->trigger_type, $ruleTrigger->trigger_value)); Log::debug(sprintf('SearchRuleEngine:: add a rule trigger (context): %s:"%s"', $ruleTrigger->trigger_type, $ruleTrigger->trigger_value));
$searchArray[$ruleTrigger->trigger_type][] = sprintf('"%s"', $ruleTrigger->trigger_value); $searchArray[$ruleTrigger->trigger_type][] = sprintf('"%s"', $ruleTrigger->trigger_value);
} }
} }
// add local operators: // add local operators:
foreach ($this->operators as $operator) { foreach ($this->operators as $operator) {
app('log')->debug(sprintf('SearchRuleEngine:: add local added operator: %s:"%s"', $operator['type'], $operator['value'])); Log::debug(sprintf('SearchRuleEngine:: add local added operator: %s:"%s"', $operator['type'], $operator['value']));
$searchArray[$operator['type']][] = sprintf('"%s"', $operator['value']); $searchArray[$operator['type']][] = sprintf('"%s"', $operator['value']);
} }
$date = today(config('app.timezone')); $date = today(config('app.timezone'));
@@ -141,11 +141,11 @@ class SearchRuleEngine implements RuleEngineInterface
$searchEngine->setPage(1); $searchEngine->setPage(1);
$searchEngine->setLimit(31337); $searchEngine->setLimit(31337);
$searchEngine->setDate($date); $searchEngine->setDate($date);
app('log')->debug('Search array', $searchArray); Log::debug('Search array', $searchArray);
foreach ($searchArray as $type => $searches) { foreach ($searchArray as $type => $searches) {
foreach ($searches as $value) { foreach ($searches as $value) {
$query = sprintf('%s:%s', $type, $value); $query = sprintf('%s:%s', $type, $value);
app('log')->debug(sprintf('SearchRuleEngine:: add query "%s"', $query)); Log::debug(sprintf('SearchRuleEngine:: add query "%s"', $query));
$searchEngine->parseQuery($query); $searchEngine->parseQuery($query);
} }
} }
@@ -162,33 +162,33 @@ class SearchRuleEngine implements RuleEngineInterface
*/ */
private function hasSpecificJournalTrigger(array $array): bool private function hasSpecificJournalTrigger(array $array): bool
{ {
app('log')->debug('Now in hasSpecificJournalTrigger.'); Log::debug('Now in hasSpecificJournalTrigger.');
$journalTrigger = false; $journalTrigger = false;
$dateTrigger = false; $dateTrigger = false;
foreach ($array as $triggerName => $values) { foreach ($array as $triggerName => $values) {
if ('journal_id' === $triggerName && is_array($values) && 1 === count($values)) { if ('journal_id' === $triggerName && is_array($values) && 1 === count($values)) {
app('log')->debug('Found a journal_id trigger with 1 journal, true.'); Log::debug('Found a journal_id trigger with 1 journal, true.');
$journalTrigger = true; $journalTrigger = true;
} }
if (in_array($triggerName, ['date_is', 'date', 'on', 'date_before', 'before', 'date_after', 'after'], true)) { if (in_array($triggerName, ['date_is', 'date', 'on', 'date_before', 'before', 'date_after', 'after'], true)) {
app('log')->debug('Found a date related trigger, set to true.'); Log::debug('Found a date related trigger, set to true.');
$dateTrigger = true; $dateTrigger = true;
} }
} }
$result = $journalTrigger && $dateTrigger; $result = $journalTrigger && $dateTrigger;
app('log')->debug(sprintf('Result of hasSpecificJournalTrigger is %s.', var_export($result, true))); Log::debug(sprintf('Result of hasSpecificJournalTrigger is %s.', var_export($result, true)));
return $result; return $result;
} }
private function setDateFromJournalTrigger(array $array): Carbon private function setDateFromJournalTrigger(array $array): Carbon
{ {
app('log')->debug('Now in setDateFromJournalTrigger()'); Log::debug('Now in setDateFromJournalTrigger()');
$journalId = 0; $journalId = 0;
foreach ($array as $triggerName => $values) { foreach ($array as $triggerName => $values) {
if ('journal_id' === $triggerName && is_array($values) && 1 === count($values)) { if ('journal_id' === $triggerName && is_array($values) && 1 === count($values)) {
$journalId = (int) trim($values[0] ?? '"0"', '"'); // follows format "123". $journalId = (int) trim($values[0] ?? '"0"', '"'); // follows format "123".
app('log')->debug(sprintf('Found journal ID #%d', $journalId)); Log::debug(sprintf('Found journal ID #%d', $journalId));
} }
} }
if (0 !== $journalId) { if (0 !== $journalId) {
@@ -197,12 +197,12 @@ class SearchRuleEngine implements RuleEngineInterface
$journal = $repository->find($journalId); $journal = $repository->find($journalId);
if (null !== $journal) { if (null !== $journal) {
$date = $journal->date; $date = $journal->date;
app('log')->debug(sprintf('Found journal #%d with date %s.', $journal->id, $journal->date->format('Y-m-d'))); Log::debug(sprintf('Found journal #%d with date %s.', $journal->id, $journal->date->format('Y-m-d')));
return $date; return $date;
} }
} }
app('log')->debug('Found no journal, return default date.'); Log::debug('Found no journal, return default date.');
return today(config('app.timezone')); return today(config('app.timezone'));
} }
@@ -215,48 +215,48 @@ class SearchRuleEngine implements RuleEngineInterface
private function findNonStrictRule(Rule $rule): Collection private function findNonStrictRule(Rule $rule): Collection
{ {
app('log')->debug(sprintf('findNonStrictRule(#%d)', $rule->id)); Log::debug(sprintf('findNonStrictRule(#%d)', $rule->id));
// start a search query for individual each trigger: // start a search query for individual each trigger:
$total = new Collection(); $total = new Collection();
$count = 0; $count = 0;
$triggers = []; $triggers = [];
if ($this->refreshTriggers) { if ($this->refreshTriggers) {
app('log')->debug('Will refresh triggers.'); Log::debug('Will refresh triggers.');
$triggers = $rule->ruleTriggers()->orderBy('order', 'ASC')->get(); $triggers = $rule->ruleTriggers()->orderBy('order', 'ASC')->get();
} }
if (!$this->refreshTriggers) { if (!$this->refreshTriggers) {
app('log')->debug('Will not refresh triggers.'); Log::debug('Will not refresh triggers.');
$triggers = $rule->ruleTriggers; $triggers = $rule->ruleTriggers;
} }
app('log')->debug(sprintf('Will run %d trigger(s).', count($triggers))); Log::debug(sprintf('Will run %d trigger(s).', count($triggers)));
/** @var RuleTrigger $ruleTrigger */ /** @var RuleTrigger $ruleTrigger */
foreach ($triggers as $ruleTrigger) { foreach ($triggers as $ruleTrigger) {
app('log')->debug(sprintf('Now at rule trigger #%d: %s:"%s" (%s).', $ruleTrigger->id, $ruleTrigger->trigger_type, $ruleTrigger->trigger_value, var_export($ruleTrigger->stop_processing, true))); Log::debug(sprintf('Now at rule trigger #%d: %s:"%s" (%s).', $ruleTrigger->id, $ruleTrigger->trigger_type, $ruleTrigger->trigger_value, var_export($ruleTrigger->stop_processing, true)));
if (false === $ruleTrigger->active) { if (false === $ruleTrigger->active) {
app('log')->debug('Trigger is not active, continue.'); Log::debug('Trigger is not active, continue.');
continue; continue;
} }
if ('user_action' === $ruleTrigger->trigger_type) { if ('user_action' === $ruleTrigger->trigger_type) {
app('log')->debug('Skip trigger type. continue.'); Log::debug('Skip trigger type. continue.');
continue; continue;
} }
$searchArray = []; $searchArray = [];
$needsContext = config(sprintf('search.operators.%s.needs_context', $ruleTrigger->trigger_type)) ?? true; $needsContext = config(sprintf('search.operators.%s.needs_context', $ruleTrigger->trigger_type)) ?? true;
if (false === $needsContext) { if (false === $needsContext) {
app('log')->debug(sprintf('SearchRuleEngine:: non strict, will search for: %s:true', $ruleTrigger->trigger_type)); Log::debug(sprintf('SearchRuleEngine:: non strict, will search for: %s:true', $ruleTrigger->trigger_type));
$searchArray[$ruleTrigger->trigger_type] = 'true'; $searchArray[$ruleTrigger->trigger_type] = 'true';
} }
if (true === $needsContext) { if (true === $needsContext) {
app('log')->debug(sprintf('SearchRuleEngine:: non strict, will search for: %s:"%s"', $ruleTrigger->trigger_type, $ruleTrigger->trigger_value)); Log::debug(sprintf('SearchRuleEngine:: non strict, will search for: %s:"%s"', $ruleTrigger->trigger_type, $ruleTrigger->trigger_value));
$searchArray[$ruleTrigger->trigger_type] = sprintf('"%s"', $ruleTrigger->trigger_value); $searchArray[$ruleTrigger->trigger_type] = sprintf('"%s"', $ruleTrigger->trigger_value);
} }
// then, add local operators as well: // then, add local operators as well:
foreach ($this->operators as $operator) { foreach ($this->operators as $operator) {
app('log')->debug(sprintf('SearchRuleEngine:: add local added operator: %s:"%s"', $operator['type'], $operator['value'])); Log::debug(sprintf('SearchRuleEngine:: add local added operator: %s:"%s"', $operator['type'], $operator['value']));
$searchArray[$operator['type']] = sprintf('"%s"', $operator['value']); $searchArray[$operator['type']] = sprintf('"%s"', $operator['value']);
} }
@@ -272,19 +272,19 @@ class SearchRuleEngine implements RuleEngineInterface
$result = $searchEngine->searchTransactions(); $result = $searchEngine->searchTransactions();
$collection = $result->getCollection(); $collection = $result->getCollection();
app('log')->debug(sprintf('Found in this run, %d transactions', $collection->count())); Log::debug(sprintf('Found in this run, %d transactions', $collection->count()));
$total = $total->merge($collection); $total = $total->merge($collection);
app('log')->debug(sprintf('Total collection is now %d transactions', $total->count())); Log::debug(sprintf('Total collection is now %d transactions', $total->count()));
++$count; ++$count;
// if trigger says stop processing, do so. // if trigger says stop processing, do so.
if (true === $ruleTrigger->stop_processing && $result->count() > 0) { if (true === $ruleTrigger->stop_processing && $result->count() > 0) {
app('log')->debug('The trigger says to stop processing, so stop processing other triggers.'); Log::debug('The trigger says to stop processing, so stop processing other triggers.');
break; break;
} }
} }
app('log')->debug(sprintf('Total collection is now %d transactions', $total->count())); Log::debug(sprintf('Total collection is now %d transactions', $total->count()));
app('log')->debug(sprintf('Done running %d trigger(s)', $count)); Log::debug(sprintf('Done running %d trigger(s)', $count));
// make collection unique // make collection unique
$unique = $total->unique( $unique = $total->unique(
@@ -295,11 +295,11 @@ class SearchRuleEngine implements RuleEngineInterface
} }
return sprintf('%d%s', $group['id'], $str); return sprintf('%d%s', $group['id'], $str);
// app('log')->debug(sprintf('Return key: %s ', $key)); // Log::debug(sprintf('Return key: %s ', $key));
} }
); );
app('log')->debug(sprintf('SearchRuleEngine:: Found %d transactions using search engine.', $unique->count())); Log::debug(sprintf('SearchRuleEngine:: Found %d transactions using search engine.', $unique->count()));
return $unique; return $unique;
} }
@@ -310,28 +310,28 @@ class SearchRuleEngine implements RuleEngineInterface
public function fire(): void public function fire(): void
{ {
$this->resultCount = []; $this->resultCount = [];
app('log')->debug('SearchRuleEngine::fire()!'); Log::debug('SearchRuleEngine::fire()!');
// if rules and no rule groups, file each rule separately. // if rules and no rule groups, file each rule separately.
if (0 !== $this->rules->count()) { if (0 !== $this->rules->count()) {
app('log')->debug(sprintf('SearchRuleEngine:: found %d rule(s) to fire.', $this->rules->count())); Log::debug(sprintf('SearchRuleEngine:: found %d rule(s) to fire.', $this->rules->count()));
/** @var Rule $rule */ /** @var Rule $rule */
foreach ($this->rules as $rule) { foreach ($this->rules as $rule) {
$result = $this->fireRule($rule); $result = $this->fireRule($rule);
if (true === $result && true === $rule->stop_processing) { if (true === $result && true === $rule->stop_processing) {
app('log')->debug(sprintf('Rule #%d has triggered and executed, but calls to stop processing. Since not in the context of a group, do not stop.', $rule->id)); Log::debug(sprintf('Rule #%d has triggered and executed, but calls to stop processing. Since not in the context of a group, do not stop.', $rule->id));
} }
if (false === $result && true === $rule->stop_processing) { if (false === $result && true === $rule->stop_processing) {
app('log')->debug(sprintf('Rule #%d has triggered and changed nothing, but calls to stop processing. Do not stop.', $rule->id)); Log::debug(sprintf('Rule #%d has triggered and changed nothing, but calls to stop processing. Do not stop.', $rule->id));
} }
} }
app('log')->debug('SearchRuleEngine:: done processing all rules!'); Log::debug('SearchRuleEngine:: done processing all rules!');
return; return;
} }
if (0 !== $this->groups->count()) { if (0 !== $this->groups->count()) {
app('log')->debug(sprintf('SearchRuleEngine:: found %d rule group(s) to fire.', $this->groups->count())); Log::debug(sprintf('SearchRuleEngine:: found %d rule group(s) to fire.', $this->groups->count()));
// fire each group: // fire each group:
/** @var RuleGroup $group */ /** @var RuleGroup $group */
@@ -339,7 +339,7 @@ class SearchRuleEngine implements RuleEngineInterface
$this->fireGroup($group); $this->fireGroup($group);
} }
} }
app('log')->debug('SearchRuleEngine:: done processing all rules!'); Log::debug('SearchRuleEngine:: done processing all rules!');
} }
/** /**
@@ -349,18 +349,18 @@ class SearchRuleEngine implements RuleEngineInterface
*/ */
private function fireRule(Rule $rule): bool private function fireRule(Rule $rule): bool
{ {
app('log')->debug(sprintf('Now going to fire rule #%d', $rule->id)); Log::debug(sprintf('Now going to fire rule #%d', $rule->id));
if (false === $rule->active) { if (false === $rule->active) {
app('log')->debug(sprintf('Rule #%d is not active!', $rule->id)); Log::debug(sprintf('Rule #%d is not active!', $rule->id));
return false; return false;
} }
if (true === $rule->strict) { if (true === $rule->strict) {
app('log')->debug(sprintf('Rule #%d is a strict rule.', $rule->id)); Log::debug(sprintf('Rule #%d is a strict rule.', $rule->id));
return $this->fireStrictRule($rule); return $this->fireStrictRule($rule);
} }
app('log')->debug(sprintf('Rule #%d is not strict rule.', $rule->id)); Log::debug(sprintf('Rule #%d is not strict rule.', $rule->id));
return $this->fireNonStrictRule($rule); return $this->fireNonStrictRule($rule);
} }
@@ -372,19 +372,19 @@ class SearchRuleEngine implements RuleEngineInterface
*/ */
private function fireStrictRule(Rule $rule): bool private function fireStrictRule(Rule $rule): bool
{ {
app('log')->debug(sprintf('SearchRuleEngine::fireStrictRule(%d)!', $rule->id)); Log::debug(sprintf('SearchRuleEngine::fireStrictRule(%d)!', $rule->id));
$collection = $this->findStrictRule($rule); $collection = $this->findStrictRule($rule);
$this->processResults($rule, $collection); $this->processResults($rule, $collection);
app('log')->debug(sprintf('SearchRuleEngine:: done processing strict rule #%d', $rule->id)); Log::debug(sprintf('SearchRuleEngine:: done processing strict rule #%d', $rule->id));
$result = $collection->count() > 0; $result = $collection->count() > 0;
if (true === $result) { if (true === $result) {
app('log')->debug(sprintf('SearchRuleEngine:: rule #%d was triggered (on %d transaction(s)).', $rule->id, $collection->count())); Log::debug(sprintf('SearchRuleEngine:: rule #%d was triggered (on %d transaction(s)).', $rule->id, $collection->count()));
return true; return true;
} }
app('log')->debug(sprintf('SearchRuleEngine:: rule #%d was not triggered (on %d transaction(s)).', $rule->id, $collection->count())); Log::debug(sprintf('SearchRuleEngine:: rule #%d was not triggered (on %d transaction(s)).', $rule->id, $collection->count()));
return false; return false;
} }
@@ -394,7 +394,7 @@ class SearchRuleEngine implements RuleEngineInterface
*/ */
private function processResults(Rule $rule, Collection $collection): void private function processResults(Rule $rule, Collection $collection): void
{ {
app('log')->debug(sprintf('SearchRuleEngine:: Going to process %d results.', $collection->count())); Log::debug(sprintf('SearchRuleEngine:: Going to process %d results.', $collection->count()));
/** @var array $group */ /** @var array $group */
foreach ($collection as $group) { foreach ($collection as $group) {
@@ -407,7 +407,7 @@ class SearchRuleEngine implements RuleEngineInterface
*/ */
private function processTransactionGroup(Rule $rule, array $group): void private function processTransactionGroup(Rule $rule, array $group): void
{ {
app('log')->debug(sprintf('SearchRuleEngine:: Will now execute actions on transaction group #%d', $group['id'])); Log::debug(sprintf('SearchRuleEngine:: Will now execute actions on transaction group #%d', $group['id']));
/** @var array $transaction */ /** @var array $transaction */
foreach ($group['transactions'] as $transaction) { foreach ($group['transactions'] as $transaction) {
@@ -420,7 +420,7 @@ class SearchRuleEngine implements RuleEngineInterface
*/ */
private function processTransactionJournal(Rule $rule, array $transaction): void private function processTransactionJournal(Rule $rule, array $transaction): void
{ {
app('log')->debug(sprintf('SearchRuleEngine:: Will now execute actions on transaction journal #%d', $transaction['transaction_journal_id'])); Log::debug(sprintf('SearchRuleEngine:: Will now execute actions on transaction journal #%d', $transaction['transaction_journal_id']));
$actions = $rule->ruleActions()->orderBy('order', 'ASC')->get(); $actions = $rule->ruleActions()->orderBy('order', 'ASC')->get();
/** @var RuleAction $ruleAction */ /** @var RuleAction $ruleAction */
@@ -440,14 +440,14 @@ class SearchRuleEngine implements RuleEngineInterface
*/ */
private function processRuleAction(RuleAction $ruleAction, array $transaction): bool private function processRuleAction(RuleAction $ruleAction, array $transaction): bool
{ {
app('log')->debug(sprintf('Executing rule action "%s" with value "%s"', $ruleAction->action_type, $ruleAction->action_value)); Log::debug(sprintf('Executing rule action "%s" with value "%s"', $ruleAction->action_type, $ruleAction->action_value));
$transaction = $this->addNotes($transaction); $transaction = $this->addNotes($transaction);
$actionClass = ActionFactory::getAction($ruleAction); $actionClass = ActionFactory::getAction($ruleAction);
$result = $actionClass->actOnArray($transaction); $result = $actionClass->actOnArray($transaction);
$journalId = $transaction['transaction_journal_id'] ?? 0; $journalId = $transaction['transaction_journal_id'] ?? 0;
if (true === $result) { if (true === $result) {
$this->resultCount[$journalId] = array_key_exists($journalId, $this->resultCount) ? $this->resultCount[$journalId]++ : 1; $this->resultCount[$journalId] = array_key_exists($journalId, $this->resultCount) ? $this->resultCount[$journalId]++ : 1;
app('log')->debug( Log::debug(
sprintf( sprintf(
'Action "%s" on journal #%d was executed, so count a result. Updated transaction journal count is now %d.', 'Action "%s" on journal #%d was executed, so count a result. Updated transaction journal count is now %d.',
$ruleAction->action_type, $ruleAction->action_type,
@@ -457,17 +457,17 @@ class SearchRuleEngine implements RuleEngineInterface
); );
} }
if (false === $result) { if (false === $result) {
app('log')->debug(sprintf('Action "%s" reports NO changes were made.', $ruleAction->action_type)); Log::debug(sprintf('Action "%s" reports NO changes were made.', $ruleAction->action_type));
} }
// pick up from the action if it actually acted or not: // pick up from the action if it actually acted or not:
if (true === $ruleAction->stop_processing && true === $result) { if (true === $ruleAction->stop_processing && true === $result) {
app('log')->debug(sprintf('Rule action "%s" reports changes AND asks to break, so break!', $ruleAction->action_type)); Log::debug(sprintf('Rule action "%s" reports changes AND asks to break, so break!', $ruleAction->action_type));
return true; return true;
} }
if (true === $ruleAction->stop_processing && false === $result) { if (true === $ruleAction->stop_processing && false === $result) {
app('log')->debug(sprintf('Rule action "%s" reports NO changes AND asks to break, but we wont break!', $ruleAction->action_type)); Log::debug(sprintf('Rule action "%s" reports NO changes AND asks to break, but we wont break!', $ruleAction->action_type));
} }
return false; return false;
@@ -492,11 +492,11 @@ class SearchRuleEngine implements RuleEngineInterface
*/ */
private function fireNonStrictRule(Rule $rule): bool private function fireNonStrictRule(Rule $rule): bool
{ {
app('log')->debug(sprintf('SearchRuleEngine::fireNonStrictRule(%d)!', $rule->id)); Log::debug(sprintf('SearchRuleEngine::fireNonStrictRule(%d)!', $rule->id));
$collection = $this->findNonStrictRule($rule); $collection = $this->findNonStrictRule($rule);
$this->processResults($rule, $collection); $this->processResults($rule, $collection);
app('log')->debug(sprintf('SearchRuleEngine:: done processing non-strict rule #%d', $rule->id)); Log::debug(sprintf('SearchRuleEngine:: done processing non-strict rule #%d', $rule->id));
return $collection->count() > 0; return $collection->count() > 0;
} }
@@ -506,14 +506,14 @@ class SearchRuleEngine implements RuleEngineInterface
*/ */
private function fireGroup(RuleGroup $group): void private function fireGroup(RuleGroup $group): void
{ {
app('log')->debug(sprintf('Going to fire group #%d with %d rule(s)', $group->id, $group->rules->count())); Log::debug(sprintf('Going to fire group #%d with %d rule(s)', $group->id, $group->rules->count()));
/** @var Rule $rule */ /** @var Rule $rule */
foreach ($group->rules as $rule) { foreach ($group->rules as $rule) {
app('log')->debug(sprintf('Going to fire rule #%d from group #%d', $rule->id, $group->id)); Log::debug(sprintf('Going to fire rule #%d from group #%d', $rule->id, $group->id));
$result = $this->fireRule($rule); $result = $this->fireRule($rule);
if (true === $result && true === $rule->stop_processing) { if (true === $result && true === $rule->stop_processing) {
app('log')->debug(sprintf('The rule was triggered and rule->stop_processing = true, so group #%d will stop processing further rules.', $group->id)); Log::debug(sprintf('The rule was triggered and rule->stop_processing = true, so group #%d will stop processing further rules.', $group->id));
return; return;
} }
@@ -535,10 +535,10 @@ class SearchRuleEngine implements RuleEngineInterface
public function setRuleGroups(Collection $ruleGroups): void public function setRuleGroups(Collection $ruleGroups): void
{ {
app('log')->debug(__METHOD__); Log::debug(__METHOD__);
foreach ($ruleGroups as $group) { foreach ($ruleGroups as $group) {
if ($group instanceof RuleGroup) { if ($group instanceof RuleGroup) {
app('log')->debug(sprintf('Adding a rule group to the SearchRuleEngine: #%d ("%s")', $group->id, $group->title)); Log::debug(sprintf('Adding a rule group to the SearchRuleEngine: #%d ("%s")', $group->id, $group->title));
$this->groups->push($group); $this->groups->push($group);
} }
} }
@@ -546,10 +546,10 @@ class SearchRuleEngine implements RuleEngineInterface
public function setRules(Collection $rules): void public function setRules(Collection $rules): void
{ {
app('log')->debug(__METHOD__); Log::debug(__METHOD__);
foreach ($rules as $rule) { foreach ($rules as $rule) {
if ($rule instanceof Rule) { if ($rule instanceof Rule) {
app('log')->debug(sprintf('Adding a rule to the SearchRuleEngine: #%d ("%s")', $rule->id, $rule->title)); Log::debug(sprintf('Adding a rule to the SearchRuleEngine: #%d ("%s")', $rule->id, $rule->title));
$this->rules->push($rule); $this->rules->push($rule);
} }
} }

View File

@@ -73,8 +73,8 @@ class PiggyBankEventTransformer extends AbstractTransformer
return [ return [
'id' => (string) $event->id, 'id' => (string) $event->id,
'created_at' => $event->created_at->toAtomString(), 'created_at' => $event->created_at?->toAtomString(),
'updated_at' => $event->updated_at->toAtomString(), 'updated_at' => $event->updated_at?->toAtomString(),
'amount' => app('steam')->bcround($event->amount, $currency->decimal_places), 'amount' => app('steam')->bcround($event->amount, $currency->decimal_places),
'currency_id' => (string) $currency->id, 'currency_id' => (string) $currency->id,
'currency_code' => $currency->code, 'currency_code' => $currency->code,

View File

@@ -24,6 +24,7 @@ declare(strict_types=1);
namespace FireflyIII; namespace FireflyIII;
use Deprecated;
use FireflyIII\Enums\UserRoleEnum; use FireflyIII\Enums\UserRoleEnum;
use FireflyIII\Events\RequestedNewPassword; use FireflyIII\Events\RequestedNewPassword;
use FireflyIII\Exceptions\FireflyException; use FireflyIII\Exceptions\FireflyException;
@@ -188,9 +189,8 @@ class User extends Authenticatable
* Get the models LDAP domain. * Get the models LDAP domain.
* *
* @return string * @return string
*
* @deprecated
*/ */
#[Deprecated]
public function getLdapDomain() public function getLdapDomain()
{ {
return $this->{$this->getLdapDomainColumn()}; return $this->{$this->getLdapDomainColumn()};
@@ -200,9 +200,8 @@ class User extends Authenticatable
* Get the database column name of the domain. * Get the database column name of the domain.
* *
* @return string * @return string
*
* @deprecated
*/ */
#[Deprecated]
public function getLdapDomainColumn() public function getLdapDomainColumn()
{ {
return 'domain'; return 'domain';
@@ -212,9 +211,8 @@ class User extends Authenticatable
* Get the models LDAP GUID. * Get the models LDAP GUID.
* *
* @return string * @return string
*
* @deprecated
*/ */
#[Deprecated]
public function getLdapGuid() public function getLdapGuid()
{ {
return $this->{$this->getLdapGuidColumn()}; return $this->{$this->getLdapGuidColumn()};
@@ -224,9 +222,8 @@ class User extends Authenticatable
* Get the models LDAP GUID database column name. * Get the models LDAP GUID database column name.
* *
* @return string * @return string
*
* @deprecated
*/ */
#[Deprecated]
public function getLdapGuidColumn() public function getLdapGuidColumn()
{ {
return 'objectguid'; return 'objectguid';
@@ -468,9 +465,8 @@ class User extends Authenticatable
* Set the models LDAP domain. * Set the models LDAP domain.
* *
* @param string $domain * @param string $domain
*
* @deprecated
*/ */
#[Deprecated]
public function setLdapDomain($domain): void public function setLdapDomain($domain): void
{ {
$this->{$this->getLdapDomainColumn()} = $domain; $this->{$this->getLdapDomainColumn()} = $domain;
@@ -480,9 +476,8 @@ class User extends Authenticatable
* Set the models LDAP GUID. * Set the models LDAP GUID.
* *
* @param string $guid * @param string $guid
*
* @deprecated
*/ */
#[Deprecated]
public function setLdapGuid($guid): void public function setLdapGuid($guid): void
{ {
$this->{$this->getLdapGuidColumn()} = $guid; $this->{$this->getLdapGuidColumn()} = $guid;

View File

@@ -3,6 +3,45 @@
All notable changes to this project will be documented in this file. All notable changes to this project will be documented in this file.
This project adheres to [Semantic Versioning](http://semver.org/). This project adheres to [Semantic Versioning](http://semver.org/).
## 6.2.18 - 2025-06-20
### Changed
- Give more details about OAuth errors.
- Currency seeder adds "RMB" instead of "CNY" for Chinese Yuan.
### Fixed
- [Issue 10454](https://github.com/firefly-iii/firefly-iii/issues/10454) (Bulk edit individually give an error) reported by @Rick45
- [Issue 10470](https://github.com/firefly-iii/firefly-iii/issues/10470) (API endpoint `/api/v1/chart/account/overview` throws error) reported by @dreautall
- Remove some leftover debug info
## 6.2.17 - 2025-06-12
### Changed
- Firefly III will trim account numbers with spaces for better matching during imports
- Running balance is only updated when amounts or accounts change.
### Fixed
- Transactions can't be linked to a piggy bank twice.
- [Issue 10229](https://github.com/firefly-iii/firefly-iii/issues/10229) (Please show transaction ID in the Meta information) reported by @srikakulamts
- [Issue 10382](https://github.com/firefly-iii/firefly-iii/issues/10382) (Have a link to search page on mobile layout) reported by @sergeolkhovik
- [Issue 10399](https://github.com/firefly-iii/firefly-iii/issues/10399) (Can not apply rules to future dates) reported by @sanderr
- [Issue 10403](https://github.com/firefly-iii/firefly-iii/issues/10403) (Piggy banks over multiple accounts are too restrictive) reported by @sanderr
- [Issue 10413](https://github.com/firefly-iii/firefly-iii/issues/10413) (Piggy Bank math seems broken, deductions are not registered, Saved so far 0 but balance is positive) reported by @Skiri-ki
- [Issue 10419](https://github.com/firefly-iii/firefly-iii/issues/10419) (Add quarters to report view to prefill date ranges) reported by @othmar52
- [Issue 10426](https://github.com/firefly-iii/firefly-iii/issues/10426) (bcadd(): Argument #1 ($num1) is not well-formed) reported by @grgar
- [Discussion 10427](https://github.com/orgs/firefly-iii/discussions/10427) ("Stop processing" doesn't seem to work? (6.2.12)) started by @rarosalion
- [Issue 10432](https://github.com/firefly-iii/firefly-iii/issues/10432) (Error during upgrade 6.1.25 → 6.2.16: $piggyBank->account is null) reported by @Digi92
- [Issue 10433](https://github.com/firefly-iii/firefly-iii/issues/10433) (500 error when using forgot password link (running in Docker)) reported by @jegelandsdal
- [Issue 10441](https://github.com/firefly-iii/firefly-iii/issues/10441) (Missing spent per day info on mismatching budget limit periods) reported by @Kaotic3
### API
- Transactions now contain balance before/after.
## 6.2.16 - 2025-xx-xx ## 6.2.16 - 2025-xx-xx
### Fixed ### Fixed

297
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.3",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/filp/whoops.git", "url": "https://github.com/filp/whoops.git",
"reference": "a7de6c3c6c3c022f5cfc337f8ede6a14460cf77e" "reference": "59a123a3d459c5a23055802237cb317f609867e5"
}, },
"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/59a123a3d459c5a23055802237cb317f609867e5",
"reference": "a7de6c3c6c3c022f5cfc337f8ede6a14460cf77e", "reference": "59a123a3d459c5a23055802237cb317f609867e5",
"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.3"
}, },
"funding": [ "funding": [
{ {
@@ -1006,7 +1006,7 @@
"type": "github" "type": "github"
} }
], ],
"time": "2025-03-15T12:00:00+00:00" "time": "2025-06-16T00:02:10+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.19.3",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/laravel/framework.git", "url": "https://github.com/laravel/framework.git",
"reference": "293bb1c70224faebfd3d4328e201c37115da055f" "reference": "4e6ec689ef704bb4bd282f29d9dd658dfb4fb262"
}, },
"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/4e6ec689ef704bb4bd282f29d9dd658dfb4fb262",
"reference": "293bb1c70224faebfd3d4328e201c37115da055f", "reference": "4e6ec689ef704bb4bd282f29d9dd658dfb4fb262",
"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-18T12:56:23+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",
@@ -3712,16 +3711,16 @@
}, },
{ {
"name": "nesbot/carbon", "name": "nesbot/carbon",
"version": "3.9.1", "version": "3.10.0",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/CarbonPHP/carbon.git", "url": "https://github.com/CarbonPHP/carbon.git",
"reference": "ced71f79398ece168e24f7f7710462f462310d4d" "reference": "c1397390dd0a7e0f11660f0ae20f753d88c1f3d9"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/CarbonPHP/carbon/zipball/ced71f79398ece168e24f7f7710462f462310d4d", "url": "https://api.github.com/repos/CarbonPHP/carbon/zipball/c1397390dd0a7e0f11660f0ae20f753d88c1f3d9",
"reference": "ced71f79398ece168e24f7f7710462f462310d4d", "reference": "c1397390dd0a7e0f11660f0ae20f753d88c1f3d9",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@@ -3729,9 +3728,9 @@
"ext-json": "*", "ext-json": "*",
"php": "^8.1", "php": "^8.1",
"psr/clock": "^1.0", "psr/clock": "^1.0",
"symfony/clock": "^6.3 || ^7.0", "symfony/clock": "^6.3.12 || ^7.0",
"symfony/polyfill-mbstring": "^1.0", "symfony/polyfill-mbstring": "^1.0",
"symfony/translation": "^4.4.18 || ^5.2.1|| ^6.0 || ^7.0" "symfony/translation": "^4.4.18 || ^5.2.1 || ^6.0 || ^7.0"
}, },
"provide": { "provide": {
"psr/clock-implementation": "1.0" "psr/clock-implementation": "1.0"
@@ -3739,14 +3738,13 @@
"require-dev": { "require-dev": {
"doctrine/dbal": "^3.6.3 || ^4.0", "doctrine/dbal": "^3.6.3 || ^4.0",
"doctrine/orm": "^2.15.2 || ^3.0", "doctrine/orm": "^2.15.2 || ^3.0",
"friendsofphp/php-cs-fixer": "^3.57.2", "friendsofphp/php-cs-fixer": "^3.75.0",
"kylekatarnls/multi-tester": "^2.5.3", "kylekatarnls/multi-tester": "^2.5.3",
"ondrejmirtes/better-reflection": "^6.25.0.4",
"phpmd/phpmd": "^2.15.0", "phpmd/phpmd": "^2.15.0",
"phpstan/extension-installer": "^1.3.1", "phpstan/extension-installer": "^1.4.3",
"phpstan/phpstan": "^1.11.2", "phpstan/phpstan": "^2.1.17",
"phpunit/phpunit": "^10.5.20", "phpunit/phpunit": "^10.5.46",
"squizlabs/php_codesniffer": "^3.9.0" "squizlabs/php_codesniffer": "^3.13.0"
}, },
"bin": [ "bin": [
"bin/carbon" "bin/carbon"
@@ -3814,7 +3812,7 @@
"type": "tidelift" "type": "tidelift"
} }
], ],
"time": "2025-05-01T19:51:51+00:00" "time": "2025-06-12T10:24:28+00:00"
}, },
{ {
"name": "nette/schema", "name": "nette/schema",
@@ -3880,16 +3878,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,29 +3958,29 @@
], ],
"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",
"version": "v8.8.0", "version": "v8.8.1",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/nunomaduro/collision.git", "url": "https://github.com/nunomaduro/collision.git",
"reference": "4cf9f3b47afff38b139fb79ce54fc71799022ce8" "reference": "44ccb82e3e21efb5446748d2a3c81a030ac22bd5"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/nunomaduro/collision/zipball/4cf9f3b47afff38b139fb79ce54fc71799022ce8", "url": "https://api.github.com/repos/nunomaduro/collision/zipball/44ccb82e3e21efb5446748d2a3c81a030ac22bd5",
"reference": "4cf9f3b47afff38b139fb79ce54fc71799022ce8", "reference": "44ccb82e3e21efb5446748d2a3c81a030ac22bd5",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
"filp/whoops": "^2.18.0", "filp/whoops": "^2.18.1",
"nunomaduro/termwind": "^2.3.0", "nunomaduro/termwind": "^2.3.1",
"php": "^8.2.0", "php": "^8.2.0",
"symfony/console": "^7.2.5" "symfony/console": "^7.3.0"
}, },
"conflict": { "conflict": {
"laravel/framework": "<11.44.2 || >=13.0.0", "laravel/framework": "<11.44.2 || >=13.0.0",
@@ -3990,15 +3988,15 @@
}, },
"require-dev": { "require-dev": {
"brianium/paratest": "^7.8.3", "brianium/paratest": "^7.8.3",
"larastan/larastan": "^3.2", "larastan/larastan": "^3.4.2",
"laravel/framework": "^11.44.2 || ^12.6", "laravel/framework": "^11.44.2 || ^12.18",
"laravel/pint": "^1.21.2", "laravel/pint": "^1.22.1",
"laravel/sail": "^1.41.0", "laravel/sail": "^1.43.1",
"laravel/sanctum": "^4.0.8", "laravel/sanctum": "^4.1.1",
"laravel/tinker": "^2.10.1", "laravel/tinker": "^2.10.1",
"orchestra/testbench-core": "^9.12.0 || ^10.1", "orchestra/testbench-core": "^9.12.0 || ^10.4",
"pestphp/pest": "^3.8.0", "pestphp/pest": "^3.8.2",
"sebastian/environment": "^7.2.0 || ^8.0" "sebastian/environment": "^7.2.1 || ^8.0"
}, },
"type": "library", "type": "library",
"extra": { "extra": {
@@ -4061,7 +4059,7 @@
"type": "patreon" "type": "patreon"
} }
], ],
"time": "2025-04-03T14:33:09+00:00" "time": "2025-06-11T01:04:21+00:00"
}, },
{ {
"name": "nunomaduro/termwind", "name": "nunomaduro/termwind",
@@ -4807,16 +4805,16 @@
}, },
{ {
"name": "phpseclib/phpseclib", "name": "phpseclib/phpseclib",
"version": "3.0.43", "version": "3.0.44",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/phpseclib/phpseclib.git", "url": "https://github.com/phpseclib/phpseclib.git",
"reference": "709ec107af3cb2f385b9617be72af8cf62441d02" "reference": "1d0b5e7e1434678411787c5a0535e68907cf82d9"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/phpseclib/phpseclib/zipball/709ec107af3cb2f385b9617be72af8cf62441d02", "url": "https://api.github.com/repos/phpseclib/phpseclib/zipball/1d0b5e7e1434678411787c5a0535e68907cf82d9",
"reference": "709ec107af3cb2f385b9617be72af8cf62441d02", "reference": "1d0b5e7e1434678411787c5a0535e68907cf82d9",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@@ -4897,7 +4895,7 @@
], ],
"support": { "support": {
"issues": "https://github.com/phpseclib/phpseclib/issues", "issues": "https://github.com/phpseclib/phpseclib/issues",
"source": "https://github.com/phpseclib/phpseclib/tree/3.0.43" "source": "https://github.com/phpseclib/phpseclib/tree/3.0.44"
}, },
"funding": [ "funding": [
{ {
@@ -4913,7 +4911,7 @@
"type": "tidelift" "type": "tidelift"
} }
], ],
"time": "2024-12-14T21:12:59+00:00" "time": "2025-06-15T09:59:26+00:00"
}, },
{ {
"name": "pragmarx/google2fa", "name": "pragmarx/google2fa",
@@ -5747,20 +5745,20 @@
}, },
{ {
"name": "ramsey/uuid", "name": "ramsey/uuid",
"version": "4.7.6", "version": "4.8.1",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/ramsey/uuid.git", "url": "https://github.com/ramsey/uuid.git",
"reference": "91039bc1faa45ba123c4328958e620d382ec7088" "reference": "fdf4dd4e2ff1813111bd0ad58d7a1ddbb5b56c28"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/ramsey/uuid/zipball/91039bc1faa45ba123c4328958e620d382ec7088", "url": "https://api.github.com/repos/ramsey/uuid/zipball/fdf4dd4e2ff1813111bd0ad58d7a1ddbb5b56c28",
"reference": "91039bc1faa45ba123c4328958e620d382ec7088", "reference": "fdf4dd4e2ff1813111bd0ad58d7a1ddbb5b56c28",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
"brick/math": "^0.8.8 || ^0.9 || ^0.10 || ^0.11 || ^0.12", "brick/math": "^0.8.8 || ^0.9 || ^0.10 || ^0.11 || ^0.12 || ^0.13",
"ext-json": "*", "ext-json": "*",
"php": "^8.0", "php": "^8.0",
"ramsey/collection": "^1.2 || ^2.0" "ramsey/collection": "^1.2 || ^2.0"
@@ -5769,26 +5767,23 @@
"rhumsaa/uuid": "self.version" "rhumsaa/uuid": "self.version"
}, },
"require-dev": { "require-dev": {
"captainhook/captainhook": "^5.10", "captainhook/captainhook": "^5.25",
"captainhook/plugin-composer": "^5.3", "captainhook/plugin-composer": "^5.3",
"dealerdirect/phpcodesniffer-composer-installer": "^0.7.0", "dealerdirect/phpcodesniffer-composer-installer": "^1.0",
"doctrine/annotations": "^1.8", "ergebnis/composer-normalize": "^2.47",
"ergebnis/composer-normalize": "^2.15", "mockery/mockery": "^1.6",
"mockery/mockery": "^1.3",
"paragonie/random-lib": "^2", "paragonie/random-lib": "^2",
"php-mock/php-mock": "^2.2", "php-mock/php-mock": "^2.6",
"php-mock/php-mock-mockery": "^1.3", "php-mock/php-mock-mockery": "^1.5",
"php-parallel-lint/php-parallel-lint": "^1.1", "php-parallel-lint/php-parallel-lint": "^1.4.0",
"phpbench/phpbench": "^1.0", "phpbench/phpbench": "^1.2.14",
"phpstan/extension-installer": "^1.1", "phpstan/extension-installer": "^1.4",
"phpstan/phpstan": "^1.8", "phpstan/phpstan": "^2.1",
"phpstan/phpstan-mockery": "^1.1", "phpstan/phpstan-mockery": "^2.0",
"phpstan/phpstan-phpunit": "^1.1", "phpstan/phpstan-phpunit": "^2.0",
"phpunit/phpunit": "^8.5 || ^9", "phpunit/phpunit": "^9.6",
"ramsey/composer-repl": "^1.4", "slevomat/coding-standard": "^8.18",
"slevomat/coding-standard": "^8.4", "squizlabs/php_codesniffer": "^3.13"
"squizlabs/php_codesniffer": "^3.5",
"vimeo/psalm": "^4.9"
}, },
"suggest": { "suggest": {
"ext-bcmath": "Enables faster math with arbitrary-precision integers using BCMath.", "ext-bcmath": "Enables faster math with arbitrary-precision integers using BCMath.",
@@ -5823,19 +5818,9 @@
], ],
"support": { "support": {
"issues": "https://github.com/ramsey/uuid/issues", "issues": "https://github.com/ramsey/uuid/issues",
"source": "https://github.com/ramsey/uuid/tree/4.7.6" "source": "https://github.com/ramsey/uuid/tree/4.8.1"
}, },
"funding": [ "time": "2025-06-01T06:28:46+00:00"
{
"url": "https://github.com/ramsey",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/ramsey/uuid",
"type": "tidelift"
}
],
"time": "2024-04-27T21:32:50+00:00"
}, },
{ {
"name": "rcrowe/twigbridge", "name": "rcrowe/twigbridge",
@@ -10487,16 +10472,16 @@
}, },
{ {
"name": "larastan/larastan", "name": "larastan/larastan",
"version": "v3.4.0", "version": "v3.4.2",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/larastan/larastan.git", "url": "https://github.com/larastan/larastan.git",
"reference": "1042fa0c2ee490bb6da7381f3323f7292ad68222" "reference": "36706736a0c51d3337478fab9c919d78d2e03404"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/larastan/larastan/zipball/1042fa0c2ee490bb6da7381f3323f7292ad68222", "url": "https://api.github.com/repos/larastan/larastan/zipball/36706736a0c51d3337478fab9c919d78d2e03404",
"reference": "1042fa0c2ee490bb6da7381f3323f7292ad68222", "reference": "36706736a0c51d3337478fab9c919d78d2e03404",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@@ -10564,7 +10549,7 @@
], ],
"support": { "support": {
"issues": "https://github.com/larastan/larastan/issues", "issues": "https://github.com/larastan/larastan/issues",
"source": "https://github.com/larastan/larastan/tree/v3.4.0" "source": "https://github.com/larastan/larastan/tree/v3.4.2"
}, },
"funding": [ "funding": [
{ {
@@ -10572,7 +10557,7 @@
"type": "github" "type": "github"
} }
], ],
"time": "2025-04-22T09:44:59+00:00" "time": "2025-06-10T09:34:58+00:00"
}, },
{ {
"name": "laravel-json-api/testing", "name": "laravel-json-api/testing",
@@ -10784,16 +10769,16 @@
}, },
{ {
"name": "nikic/php-parser", "name": "nikic/php-parser",
"version": "v5.4.0", "version": "v5.5.0",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/nikic/PHP-Parser.git", "url": "https://github.com/nikic/PHP-Parser.git",
"reference": "447a020a1f875a434d62f2a401f53b82a396e494" "reference": "ae59794362fe85e051a58ad36b289443f57be7a9"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/447a020a1f875a434d62f2a401f53b82a396e494", "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/ae59794362fe85e051a58ad36b289443f57be7a9",
"reference": "447a020a1f875a434d62f2a401f53b82a396e494", "reference": "ae59794362fe85e051a58ad36b289443f57be7a9",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@@ -10836,9 +10821,9 @@
], ],
"support": { "support": {
"issues": "https://github.com/nikic/PHP-Parser/issues", "issues": "https://github.com/nikic/PHP-Parser/issues",
"source": "https://github.com/nikic/PHP-Parser/tree/v5.4.0" "source": "https://github.com/nikic/PHP-Parser/tree/v5.5.0"
}, },
"time": "2024-12-30T11:07:19+00:00" "time": "2025-05-31T08:24:38+00:00"
}, },
{ {
"name": "phar-io/manifest", "name": "phar-io/manifest",
@@ -11231,16 +11216,16 @@
}, },
{ {
"name": "phpunit/php-code-coverage", "name": "phpunit/php-code-coverage",
"version": "12.3.0", "version": "12.3.1",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/sebastianbergmann/php-code-coverage.git", "url": "https://github.com/sebastianbergmann/php-code-coverage.git",
"reference": "9075a8efc66e11bc55c319062e147bdb06777267" "reference": "ddec29dfc128eba9c204389960f2063f3b7fa170"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/9075a8efc66e11bc55c319062e147bdb06777267", "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/ddec29dfc128eba9c204389960f2063f3b7fa170",
"reference": "9075a8efc66e11bc55c319062e147bdb06777267", "reference": "ddec29dfc128eba9c204389960f2063f3b7fa170",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@@ -11296,7 +11281,7 @@
"support": { "support": {
"issues": "https://github.com/sebastianbergmann/php-code-coverage/issues", "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues",
"security": "https://github.com/sebastianbergmann/php-code-coverage/security/policy", "security": "https://github.com/sebastianbergmann/php-code-coverage/security/policy",
"source": "https://github.com/sebastianbergmann/php-code-coverage/tree/12.3.0" "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/12.3.1"
}, },
"funding": [ "funding": [
{ {
@@ -11316,7 +11301,7 @@
"type": "tidelift" "type": "tidelift"
} }
], ],
"time": "2025-05-23T15:49:03+00:00" "time": "2025-06-18T08:58:13+00:00"
}, },
{ {
"name": "phpunit/php-file-iterator", "name": "phpunit/php-file-iterator",
@@ -11565,16 +11550,16 @@
}, },
{ {
"name": "phpunit/phpunit", "name": "phpunit/phpunit",
"version": "12.1.6", "version": "12.2.2",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/sebastianbergmann/phpunit.git", "url": "https://github.com/sebastianbergmann/phpunit.git",
"reference": "2fdf0056c673c8f0f1eed00030be5f8243c1e6e0" "reference": "19e25c2da3f8071a683ee1e445b0e24bba25de61"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/2fdf0056c673c8f0f1eed00030be5f8243c1e6e0", "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/19e25c2da3f8071a683ee1e445b0e24bba25de61",
"reference": "2fdf0056c673c8f0f1eed00030be5f8243c1e6e0", "reference": "19e25c2da3f8071a683ee1e445b0e24bba25de61",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@@ -11588,7 +11573,7 @@
"phar-io/manifest": "^2.0.4", "phar-io/manifest": "^2.0.4",
"phar-io/version": "^3.2.1", "phar-io/version": "^3.2.1",
"php": ">=8.3", "php": ">=8.3",
"phpunit/php-code-coverage": "^12.2.1", "phpunit/php-code-coverage": "^12.3.0",
"phpunit/php-file-iterator": "^6.0.0", "phpunit/php-file-iterator": "^6.0.0",
"phpunit/php-invoker": "^6.0.0", "phpunit/php-invoker": "^6.0.0",
"phpunit/php-text-template": "^5.0.0", "phpunit/php-text-template": "^5.0.0",
@@ -11596,7 +11581,7 @@
"sebastian/cli-parser": "^4.0.0", "sebastian/cli-parser": "^4.0.0",
"sebastian/comparator": "^7.0.1", "sebastian/comparator": "^7.0.1",
"sebastian/diff": "^7.0.0", "sebastian/diff": "^7.0.0",
"sebastian/environment": "^8.0.1", "sebastian/environment": "^8.0.2",
"sebastian/exporter": "^7.0.0", "sebastian/exporter": "^7.0.0",
"sebastian/global-state": "^8.0.0", "sebastian/global-state": "^8.0.0",
"sebastian/object-enumerator": "^7.0.0", "sebastian/object-enumerator": "^7.0.0",
@@ -11610,7 +11595,7 @@
"type": "library", "type": "library",
"extra": { "extra": {
"branch-alias": { "branch-alias": {
"dev-main": "12.1-dev" "dev-main": "12.2-dev"
} }
}, },
"autoload": { "autoload": {
@@ -11642,7 +11627,7 @@
"support": { "support": {
"issues": "https://github.com/sebastianbergmann/phpunit/issues", "issues": "https://github.com/sebastianbergmann/phpunit/issues",
"security": "https://github.com/sebastianbergmann/phpunit/security/policy", "security": "https://github.com/sebastianbergmann/phpunit/security/policy",
"source": "https://github.com/sebastianbergmann/phpunit/tree/12.1.6" "source": "https://github.com/sebastianbergmann/phpunit/tree/12.2.2"
}, },
"funding": [ "funding": [
{ {
@@ -11666,20 +11651,20 @@
"type": "tidelift" "type": "tidelift"
} }
], ],
"time": "2025-05-21T12:36:31+00:00" "time": "2025-06-13T05:49:28+00:00"
}, },
{ {
"name": "rector/rector", "name": "rector/rector",
"version": "2.0.17", "version": "2.0.18",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/rectorphp/rector.git", "url": "https://github.com/rectorphp/rector.git",
"reference": "caa4ffda1d48bde44434e6ba95d132ec32e7fd40" "reference": "be3a452085b524a04056e3dfe72d861948711062"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/rectorphp/rector/zipball/caa4ffda1d48bde44434e6ba95d132ec32e7fd40", "url": "https://api.github.com/repos/rectorphp/rector/zipball/be3a452085b524a04056e3dfe72d861948711062",
"reference": "caa4ffda1d48bde44434e6ba95d132ec32e7fd40", "reference": "be3a452085b524a04056e3dfe72d861948711062",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@@ -11717,7 +11702,7 @@
], ],
"support": { "support": {
"issues": "https://github.com/rectorphp/rector/issues", "issues": "https://github.com/rectorphp/rector/issues",
"source": "https://github.com/rectorphp/rector/tree/2.0.17" "source": "https://github.com/rectorphp/rector/tree/2.0.18"
}, },
"funding": [ "funding": [
{ {
@@ -11725,7 +11710,7 @@
"type": "github" "type": "github"
} }
], ],
"time": "2025-05-30T10:59:08+00:00" "time": "2025-06-11T11:19:37+00:00"
}, },
{ {
"name": "sebastian/cli-parser", "name": "sebastian/cli-parser",
@@ -11786,16 +11771,16 @@
}, },
{ {
"name": "sebastian/comparator", "name": "sebastian/comparator",
"version": "7.0.1", "version": "7.1.0",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/sebastianbergmann/comparator.git", "url": "https://github.com/sebastianbergmann/comparator.git",
"reference": "b478f34614f934e0291598d0c08cbaba9644bee5" "reference": "03d905327dccc0851c9a08d6a979dfc683826b6f"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/b478f34614f934e0291598d0c08cbaba9644bee5", "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/03d905327dccc0851c9a08d6a979dfc683826b6f",
"reference": "b478f34614f934e0291598d0c08cbaba9644bee5", "reference": "03d905327dccc0851c9a08d6a979dfc683826b6f",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@@ -11806,7 +11791,7 @@
"sebastian/exporter": "^7.0" "sebastian/exporter": "^7.0"
}, },
"require-dev": { "require-dev": {
"phpunit/phpunit": "^12.0" "phpunit/phpunit": "^12.2"
}, },
"suggest": { "suggest": {
"ext-bcmath": "For comparing BcMath\\Number objects" "ext-bcmath": "For comparing BcMath\\Number objects"
@@ -11814,7 +11799,7 @@
"type": "library", "type": "library",
"extra": { "extra": {
"branch-alias": { "branch-alias": {
"dev-main": "7.0-dev" "dev-main": "7.1-dev"
} }
}, },
"autoload": { "autoload": {
@@ -11854,15 +11839,27 @@
"support": { "support": {
"issues": "https://github.com/sebastianbergmann/comparator/issues", "issues": "https://github.com/sebastianbergmann/comparator/issues",
"security": "https://github.com/sebastianbergmann/comparator/security/policy", "security": "https://github.com/sebastianbergmann/comparator/security/policy",
"source": "https://github.com/sebastianbergmann/comparator/tree/7.0.1" "source": "https://github.com/sebastianbergmann/comparator/tree/7.1.0"
}, },
"funding": [ "funding": [
{ {
"url": "https://github.com/sebastianbergmann", "url": "https://github.com/sebastianbergmann",
"type": "github" "type": "github"
},
{
"url": "https://liberapay.com/sebastianbergmann",
"type": "liberapay"
},
{
"url": "https://thanks.dev/u/gh/sebastianbergmann",
"type": "thanks_dev"
},
{
"url": "https://tidelift.com/funding/github/packagist/sebastian/comparator",
"type": "tidelift"
} }
], ],
"time": "2025-03-07T07:00:32+00:00" "time": "2025-06-17T07:41:58+00:00"
}, },
{ {
"name": "sebastian/complexity", "name": "sebastian/complexity",

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-05-31', 'version' => 'develop/2025-06-20',
'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,

View File

@@ -66,7 +66,8 @@ class TransactionCurrencySeeder extends Seeder
// asian currencies // asian currencies
$currencies[] = ['code' => 'JPY', 'name' => 'Japanese yen', 'symbol' => '¥', 'decimal_places' => 0]; $currencies[] = ['code' => 'JPY', 'name' => 'Japanese yen', 'symbol' => '¥', 'decimal_places' => 0];
$currencies[] = ['code' => 'CNY', 'name' => 'Chinese yuan', 'symbol' => '¥', 'decimal_places' => 2]; // $currencies[] = ['code' => 'CNY', 'name' => 'Chinese yuan', 'symbol' => '¥', 'decimal_places' => 2];
$currencies[] = ['code' => 'RMB', 'name' => 'Chinese yuan', 'symbol' => '¥', 'decimal_places' => 2];
$currencies[] = ['code' => 'RUB', 'name' => 'Russian ruble', 'symbol' => '₽', 'decimal_places' => 2]; $currencies[] = ['code' => 'RUB', 'name' => 'Russian ruble', 'symbol' => '₽', 'decimal_places' => 2];
$currencies[] = ['code' => 'INR', 'name' => 'Indian rupee', 'symbol' => '₹', 'decimal_places' => 2]; $currencies[] = ['code' => 'INR', 'name' => 'Indian rupee', 'symbol' => '₹', 'decimal_places' => 2];

435
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",
@@ -392,23 +392,23 @@
} }
}, },
"node_modules/@babel/helpers": { "node_modules/@babel/helpers": {
"version": "7.27.4", "version": "7.27.6",
"resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.27.4.tgz", "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.27.6.tgz",
"integrity": "sha512-Y+bO6U+I7ZKaM5G5rDUZiYfUvQPUibYmAFe7EnKdnKBbVXDZxvp+MWOH5gYciY0EPk4EScsuFMQBbEfpdRKSCQ==", "integrity": "sha512-muE8Tt8M22638HU31A3CgfSUciwz1fhATfoVai05aPXGor//CdWDCbnlY1yvBPo07njuVOCNGCSp/GTt12lIug==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@babel/template": "^7.27.2", "@babel/template": "^7.27.2",
"@babel/types": "^7.27.3" "@babel/types": "^7.27.6"
}, },
"engines": { "engines": {
"node": ">=6.9.0" "node": ">=6.9.0"
} }
}, },
"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": {
@@ -1592,9 +1592,9 @@
} }
}, },
"node_modules/@babel/runtime": { "node_modules/@babel/runtime": {
"version": "7.27.4", "version": "7.27.6",
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.27.4.tgz", "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.27.6.tgz",
"integrity": "sha512-t3yaEOuGu9NlIZ+hIeGbBjFtZT7j2cb2tg0fuaJKeGotchRjjLfrBA9Kwf8quhpP1EUuxModQg04q/mBwyg8uA==", "integrity": "sha512-vbavdySgbTTrmFE+EsiqUTzlOr5bzlnJtUv9PynGCAKvfQqjIXbvFdumPM/GxMDfyuGMJaJAU6TO4zc1Jf1i8Q==",
"license": "MIT", "license": "MIT",
"engines": { "engines": {
"node": ">=6.9.0" "node": ">=6.9.0"
@@ -1635,9 +1635,9 @@
} }
}, },
"node_modules/@babel/types": { "node_modules/@babel/types": {
"version": "7.27.3", "version": "7.27.6",
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.27.3.tgz", "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.27.6.tgz",
"integrity": "sha512-Y1GkI4ktrtvmawoSq+4FCVHNryea6uR+qUQy0AGxLSsjCX0nVmkYQMBLHDkXZuo5hGx7eYdnIaslsdBFm7zbUw==", "integrity": "sha512-ETyHEk2VHHvl9b9jZP5IHPavHYk57EhanlRRuae9XCpb/j5bDCbPPMOBfCWhnl/7EDJz0jEMCi/RhccCE8r1+Q==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
@@ -2552,9 +2552,9 @@
} }
}, },
"node_modules/@rollup/rollup-android-arm-eabi": { "node_modules/@rollup/rollup-android-arm-eabi": {
"version": "4.41.1", "version": "4.44.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.41.1.tgz", "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.44.0.tgz",
"integrity": "sha512-NELNvyEWZ6R9QMkiytB4/L4zSEaBC03KIXEghptLGLZWJ6VPrL63ooZQCOnlx36aQPGhzuOMwDerC1Eb2VmrLw==", "integrity": "sha512-xEiEE5oDW6tK4jXCAyliuntGR+amEMO7HLtdSshVuhFnKTYoeYMyXQK7pLouAJJj5KHdwdn87bfHAR2nSdNAUA==",
"cpu": [ "cpu": [
"arm" "arm"
], ],
@@ -2566,9 +2566,9 @@
] ]
}, },
"node_modules/@rollup/rollup-android-arm64": { "node_modules/@rollup/rollup-android-arm64": {
"version": "4.41.1", "version": "4.44.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.41.1.tgz", "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.44.0.tgz",
"integrity": "sha512-DXdQe1BJ6TK47ukAoZLehRHhfKnKg9BjnQYUu9gzhI8Mwa1d2fzxA1aw2JixHVl403bwp1+/o/NhhHtxWJBgEA==", "integrity": "sha512-uNSk/TgvMbskcHxXYHzqwiyBlJ/lGcv8DaUfcnNwict8ba9GTTNxfn3/FAoFZYgkaXXAdrAA+SLyKplyi349Jw==",
"cpu": [ "cpu": [
"arm64" "arm64"
], ],
@@ -2580,9 +2580,9 @@
] ]
}, },
"node_modules/@rollup/rollup-darwin-arm64": { "node_modules/@rollup/rollup-darwin-arm64": {
"version": "4.41.1", "version": "4.44.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.41.1.tgz", "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.44.0.tgz",
"integrity": "sha512-5afxvwszzdulsU2w8JKWwY8/sJOLPzf0e1bFuvcW5h9zsEg+RQAojdW0ux2zyYAz7R8HvvzKCjLNJhVq965U7w==", "integrity": "sha512-VGF3wy0Eq1gcEIkSCr8Ke03CWT+Pm2yveKLaDvq51pPpZza3JX/ClxXOCmTYYq3us5MvEuNRTaeyFThCKRQhOA==",
"cpu": [ "cpu": [
"arm64" "arm64"
], ],
@@ -2594,9 +2594,9 @@
] ]
}, },
"node_modules/@rollup/rollup-darwin-x64": { "node_modules/@rollup/rollup-darwin-x64": {
"version": "4.41.1", "version": "4.44.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.41.1.tgz", "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.44.0.tgz",
"integrity": "sha512-egpJACny8QOdHNNMZKf8xY0Is6gIMz+tuqXlusxquWu3F833DcMwmGM7WlvCO9sB3OsPjdC4U0wHw5FabzCGZg==", "integrity": "sha512-fBkyrDhwquRvrTxSGH/qqt3/T0w5Rg0L7ZIDypvBPc1/gzjJle6acCpZ36blwuwcKD/u6oCE/sRWlUAcxLWQbQ==",
"cpu": [ "cpu": [
"x64" "x64"
], ],
@@ -2608,9 +2608,9 @@
] ]
}, },
"node_modules/@rollup/rollup-freebsd-arm64": { "node_modules/@rollup/rollup-freebsd-arm64": {
"version": "4.41.1", "version": "4.44.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.41.1.tgz", "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.44.0.tgz",
"integrity": "sha512-DBVMZH5vbjgRk3r0OzgjS38z+atlupJ7xfKIDJdZZL6sM6wjfDNo64aowcLPKIx7LMQi8vybB56uh1Ftck/Atg==", "integrity": "sha512-u5AZzdQJYJXByB8giQ+r4VyfZP+walV+xHWdaFx/1VxsOn6eWJhK2Vl2eElvDJFKQBo/hcYIBg/jaKS8ZmKeNQ==",
"cpu": [ "cpu": [
"arm64" "arm64"
], ],
@@ -2622,9 +2622,9 @@
] ]
}, },
"node_modules/@rollup/rollup-freebsd-x64": { "node_modules/@rollup/rollup-freebsd-x64": {
"version": "4.41.1", "version": "4.44.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.41.1.tgz", "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.44.0.tgz",
"integrity": "sha512-3FkydeohozEskBxNWEIbPfOE0aqQgB6ttTkJ159uWOFn42VLyfAiyD9UK5mhu+ItWzft60DycIN1Xdgiy8o/SA==", "integrity": "sha512-qC0kS48c/s3EtdArkimctY7h3nHicQeEUdjJzYVJYR3ct3kWSafmn6jkNCA8InbUdge6PVx6keqjk5lVGJf99g==",
"cpu": [ "cpu": [
"x64" "x64"
], ],
@@ -2636,9 +2636,9 @@
] ]
}, },
"node_modules/@rollup/rollup-linux-arm-gnueabihf": { "node_modules/@rollup/rollup-linux-arm-gnueabihf": {
"version": "4.41.1", "version": "4.44.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.41.1.tgz", "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.44.0.tgz",
"integrity": "sha512-wC53ZNDgt0pqx5xCAgNunkTzFE8GTgdZ9EwYGVcg+jEjJdZGtq9xPjDnFgfFozQI/Xm1mh+D9YlYtl+ueswNEg==", "integrity": "sha512-x+e/Z9H0RAWckn4V2OZZl6EmV0L2diuX3QB0uM1r6BvhUIv6xBPL5mrAX2E3e8N8rEHVPwFfz/ETUbV4oW9+lQ==",
"cpu": [ "cpu": [
"arm" "arm"
], ],
@@ -2650,9 +2650,9 @@
] ]
}, },
"node_modules/@rollup/rollup-linux-arm-musleabihf": { "node_modules/@rollup/rollup-linux-arm-musleabihf": {
"version": "4.41.1", "version": "4.44.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.41.1.tgz", "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.44.0.tgz",
"integrity": "sha512-jwKCca1gbZkZLhLRtsrka5N8sFAaxrGz/7wRJ8Wwvq3jug7toO21vWlViihG85ei7uJTpzbXZRcORotE+xyrLA==", "integrity": "sha512-1exwiBFf4PU/8HvI8s80icyCcnAIB86MCBdst51fwFmH5dyeoWVPVgmQPcKrMtBQ0W5pAs7jBCWuRXgEpRzSCg==",
"cpu": [ "cpu": [
"arm" "arm"
], ],
@@ -2664,9 +2664,9 @@
] ]
}, },
"node_modules/@rollup/rollup-linux-arm64-gnu": { "node_modules/@rollup/rollup-linux-arm64-gnu": {
"version": "4.41.1", "version": "4.44.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.41.1.tgz", "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.44.0.tgz",
"integrity": "sha512-g0UBcNknsmmNQ8V2d/zD2P7WWfJKU0F1nu0k5pW4rvdb+BIqMm8ToluW/eeRmxCared5dD76lS04uL4UaNgpNA==", "integrity": "sha512-ZTR2mxBHb4tK4wGf9b8SYg0Y6KQPjGpR4UWwTFdnmjB4qRtoATZ5dWn3KsDwGa5Z2ZBOE7K52L36J9LueKBdOQ==",
"cpu": [ "cpu": [
"arm64" "arm64"
], ],
@@ -2678,9 +2678,9 @@
] ]
}, },
"node_modules/@rollup/rollup-linux-arm64-musl": { "node_modules/@rollup/rollup-linux-arm64-musl": {
"version": "4.41.1", "version": "4.44.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.41.1.tgz", "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.44.0.tgz",
"integrity": "sha512-XZpeGB5TKEZWzIrj7sXr+BEaSgo/ma/kCgrZgL0oo5qdB1JlTzIYQKel/RmhT6vMAvOdM2teYlAaOGJpJ9lahg==", "integrity": "sha512-GFWfAhVhWGd4r6UxmnKRTBwP1qmModHtd5gkraeW2G490BpFOZkFtem8yuX2NyafIP/mGpRJgTJ2PwohQkUY/Q==",
"cpu": [ "cpu": [
"arm64" "arm64"
], ],
@@ -2692,9 +2692,9 @@
] ]
}, },
"node_modules/@rollup/rollup-linux-loongarch64-gnu": { "node_modules/@rollup/rollup-linux-loongarch64-gnu": {
"version": "4.41.1", "version": "4.44.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.41.1.tgz", "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.44.0.tgz",
"integrity": "sha512-bkCfDJ4qzWfFRCNt5RVV4DOw6KEgFTUZi2r2RuYhGWC8WhCA8lCAJhDeAmrM/fdiAH54m0mA0Vk2FGRPyzI+tw==", "integrity": "sha512-xw+FTGcov/ejdusVOqKgMGW3c4+AgqrfvzWEVXcNP6zq2ue+lsYUgJ+5Rtn/OTJf7e2CbgTFvzLW2j0YAtj0Gg==",
"cpu": [ "cpu": [
"loong64" "loong64"
], ],
@@ -2706,9 +2706,9 @@
] ]
}, },
"node_modules/@rollup/rollup-linux-powerpc64le-gnu": { "node_modules/@rollup/rollup-linux-powerpc64le-gnu": {
"version": "4.41.1", "version": "4.44.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.41.1.tgz", "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.44.0.tgz",
"integrity": "sha512-3mr3Xm+gvMX+/8EKogIZSIEF0WUu0HL9di+YWlJpO8CQBnoLAEL/roTCxuLncEdgcfJcvA4UMOf+2dnjl4Ut1A==", "integrity": "sha512-bKGibTr9IdF0zr21kMvkZT4K6NV+jjRnBoVMt2uNMG0BYWm3qOVmYnXKzx7UhwrviKnmK46IKMByMgvpdQlyJQ==",
"cpu": [ "cpu": [
"ppc64" "ppc64"
], ],
@@ -2720,9 +2720,9 @@
] ]
}, },
"node_modules/@rollup/rollup-linux-riscv64-gnu": { "node_modules/@rollup/rollup-linux-riscv64-gnu": {
"version": "4.41.1", "version": "4.44.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.41.1.tgz", "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.44.0.tgz",
"integrity": "sha512-3rwCIh6MQ1LGrvKJitQjZFuQnT2wxfU+ivhNBzmxXTXPllewOF7JR1s2vMX/tWtUYFgphygxjqMl76q4aMotGw==", "integrity": "sha512-vV3cL48U5kDaKZtXrti12YRa7TyxgKAIDoYdqSIOMOFBXqFj2XbChHAtXquEn2+n78ciFgr4KIqEbydEGPxXgA==",
"cpu": [ "cpu": [
"riscv64" "riscv64"
], ],
@@ -2734,9 +2734,9 @@
] ]
}, },
"node_modules/@rollup/rollup-linux-riscv64-musl": { "node_modules/@rollup/rollup-linux-riscv64-musl": {
"version": "4.41.1", "version": "4.44.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.41.1.tgz", "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.44.0.tgz",
"integrity": "sha512-LdIUOb3gvfmpkgFZuccNa2uYiqtgZAz3PTzjuM5bH3nvuy9ty6RGc/Q0+HDFrHrizJGVpjnTZ1yS5TNNjFlklw==", "integrity": "sha512-TDKO8KlHJuvTEdfw5YYFBjhFts2TR0VpZsnLLSYmB7AaohJhM8ctDSdDnUGq77hUh4m/djRafw+9zQpkOanE2Q==",
"cpu": [ "cpu": [
"riscv64" "riscv64"
], ],
@@ -2748,9 +2748,9 @@
] ]
}, },
"node_modules/@rollup/rollup-linux-s390x-gnu": { "node_modules/@rollup/rollup-linux-s390x-gnu": {
"version": "4.41.1", "version": "4.44.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.41.1.tgz", "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.44.0.tgz",
"integrity": "sha512-oIE6M8WC9ma6xYqjvPhzZYk6NbobIURvP/lEbh7FWplcMO6gn7MM2yHKA1eC/GvYwzNKK/1LYgqzdkZ8YFxR8g==", "integrity": "sha512-8541GEyktXaw4lvnGp9m84KENcxInhAt6vPWJ9RodsB/iGjHoMB2Pp5MVBCiKIRxrxzJhGCxmNzdu+oDQ7kwRA==",
"cpu": [ "cpu": [
"s390x" "s390x"
], ],
@@ -2762,9 +2762,9 @@
] ]
}, },
"node_modules/@rollup/rollup-linux-x64-gnu": { "node_modules/@rollup/rollup-linux-x64-gnu": {
"version": "4.41.1", "version": "4.44.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.41.1.tgz", "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.44.0.tgz",
"integrity": "sha512-cWBOvayNvA+SyeQMp79BHPK8ws6sHSsYnK5zDcsC3Hsxr1dgTABKjMnMslPq1DvZIp6uO7kIWhiGwaTdR4Og9A==", "integrity": "sha512-iUVJc3c0o8l9Sa/qlDL2Z9UP92UZZW1+EmQ4xfjTc1akr0iUFZNfxrXJ/R1T90h/ILm9iXEY6+iPrmYB3pXKjw==",
"cpu": [ "cpu": [
"x64" "x64"
], ],
@@ -2776,9 +2776,9 @@
] ]
}, },
"node_modules/@rollup/rollup-linux-x64-musl": { "node_modules/@rollup/rollup-linux-x64-musl": {
"version": "4.41.1", "version": "4.44.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.41.1.tgz", "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.44.0.tgz",
"integrity": "sha512-y5CbN44M+pUCdGDlZFzGGBSKCA4A/J2ZH4edTYSSxFg7ce1Xt3GtydbVKWLlzL+INfFIZAEg1ZV6hh9+QQf9YQ==", "integrity": "sha512-PQUobbhLTQT5yz/SPg116VJBgz+XOtXt8D1ck+sfJJhuEsMj2jSej5yTdp8CvWBSceu+WW+ibVL6dm0ptG5fcA==",
"cpu": [ "cpu": [
"x64" "x64"
], ],
@@ -2790,9 +2790,9 @@
] ]
}, },
"node_modules/@rollup/rollup-win32-arm64-msvc": { "node_modules/@rollup/rollup-win32-arm64-msvc": {
"version": "4.41.1", "version": "4.44.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.41.1.tgz", "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.44.0.tgz",
"integrity": "sha512-lZkCxIrjlJlMt1dLO/FbpZbzt6J/A8p4DnqzSa4PWqPEUUUnzXLeki/iyPLfV0BmHItlYgHUqJe+3KiyydmiNQ==", "integrity": "sha512-M0CpcHf8TWn+4oTxJfh7LQuTuaYeXGbk0eageVjQCKzYLsajWS/lFC94qlRqOlyC2KvRT90ZrfXULYmukeIy7w==",
"cpu": [ "cpu": [
"arm64" "arm64"
], ],
@@ -2804,9 +2804,9 @@
] ]
}, },
"node_modules/@rollup/rollup-win32-ia32-msvc": { "node_modules/@rollup/rollup-win32-ia32-msvc": {
"version": "4.41.1", "version": "4.44.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.41.1.tgz", "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.44.0.tgz",
"integrity": "sha512-+psFT9+pIh2iuGsxFYYa/LhS5MFKmuivRsx9iPJWNSGbh2XVEjk90fmpUEjCnILPEPJnikAU6SFDiEUyOv90Pg==", "integrity": "sha512-3XJ0NQtMAXTWFW8FqZKcw3gOQwBtVWP/u8TpHP3CRPXD7Pd6s8lLdH3sHWh8vqKCyyiI8xW5ltJScQmBU9j7WA==",
"cpu": [ "cpu": [
"ia32" "ia32"
], ],
@@ -2818,9 +2818,9 @@
] ]
}, },
"node_modules/@rollup/rollup-win32-x64-msvc": { "node_modules/@rollup/rollup-win32-x64-msvc": {
"version": "4.41.1", "version": "4.44.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.41.1.tgz", "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.44.0.tgz",
"integrity": "sha512-Wq2zpapRYLfi4aKxf2Xff0tN+7slj2d4R87WEzqw7ZLsVvO5zwYCIuEGSZYiK41+GlwUo1HiR+GdkLEJnCKTCw==", "integrity": "sha512-Q2Mgwt+D8hd5FIPUuPDsvPR7Bguza6yTkJxspDGkZj7tBRn2y4KSWYuIXpftFSjBra76TbKerCV7rgFPQrn+wQ==",
"cpu": [ "cpu": [
"x64" "x64"
], ],
@@ -2887,9 +2887,9 @@
} }
}, },
"node_modules/@types/body-parser": { "node_modules/@types/body-parser": {
"version": "1.19.5", "version": "1.19.6",
"resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.5.tgz", "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.6.tgz",
"integrity": "sha512-fB3Zu92ucau0iQ0JMCFQE7b/dv8Ot07NI3KaZIkIUNXq82k4eBAqUaneXfleGY9JWskeS9y+u0nXMyspcuQrCg==", "integrity": "sha512-HLFeCYgz89uk22N5Qg3dvGvsv46B8GLvKKo1zKG4NybA8U2DiEO3w9lqGg29t/tfLRJpJ6iQxnVw4OnB7MoM9g==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
@@ -2962,16 +2962,16 @@
} }
}, },
"node_modules/@types/estree": { "node_modules/@types/estree": {
"version": "1.0.7", "version": "1.0.8",
"resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.7.tgz", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz",
"integrity": "sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ==", "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==",
"dev": true, "dev": true,
"license": "MIT" "license": "MIT"
}, },
"node_modules/@types/express": { "node_modules/@types/express": {
"version": "4.17.22", "version": "4.17.23",
"resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.22.tgz", "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.23.tgz",
"integrity": "sha512-eZUmSnhRX9YRSkplpz0N+k6NljUUn5l3EWZIKZvYzhvMphEuNiyyy1viH/ejgt66JWgALwC/gtSUAeQKtSwW/w==", "integrity": "sha512-Crp6WY9aTYP3qPi2wGDo9iUe/rceX01UMhnF1jmwDcKCFM6cx7YhGP/Mpr3y9AASpfHixIG0E6azCcL5OcDHsQ==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
@@ -3019,9 +3019,9 @@
} }
}, },
"node_modules/@types/http-errors": { "node_modules/@types/http-errors": {
"version": "2.0.4", "version": "2.0.5",
"resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.4.tgz", "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.5.tgz",
"integrity": "sha512-D0CFMMtydbJAegzOyHjtiKPLlvnm3iTZyZRSZoLq2mRhDdmLfIWOCYPfQJ4cu2erKghU++QvjcUjp/5h7hESpA==", "integrity": "sha512-r8Tayk8HJnX0FztbZN7oVqGccWgw98T/0neJphO91KkmOzug1KkofZURD4UaD5uH8AqcFLfdPErnBod0u71/qg==",
"dev": true, "dev": true,
"license": "MIT" "license": "MIT"
}, },
@@ -3108,13 +3108,13 @@
"license": "MIT" "license": "MIT"
}, },
"node_modules/@types/node": { "node_modules/@types/node": {
"version": "22.15.29", "version": "24.0.3",
"resolved": "https://registry.npmjs.org/@types/node/-/node-22.15.29.tgz", "resolved": "https://registry.npmjs.org/@types/node/-/node-24.0.3.tgz",
"integrity": "sha512-LNdjOkUDlU1RZb8e1kOIUpN1qQUlzGkEtbVNo53vbrwDg5om6oduhm4SiUaPW5ASTXhAiP0jInWG8Qx9fVlOeQ==", "integrity": "sha512-R4I/kzCYAdRLzfiCabn9hxWfbuHS573x+r0dJMkkzThEa7pbrcDWK+9zu3e7aBOouf+rQAciqPFMnxwr0aWgKg==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"undici-types": "~6.21.0" "undici-types": "~7.8.0"
} }
}, },
"node_modules/@types/node-forge": { "node_modules/@types/node-forge": {
@@ -3156,9 +3156,9 @@
"license": "MIT" "license": "MIT"
}, },
"node_modules/@types/send": { "node_modules/@types/send": {
"version": "0.17.4", "version": "0.17.5",
"resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.4.tgz", "resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.5.tgz",
"integrity": "sha512-x2EM6TJOybec7c52BX0ZspPodMsQUd5L6PRwOunVyVUhXiBSKf3AezDL8Dgvgt5o0UfKNfuA0eMLr2wLT4AiBA==", "integrity": "sha512-z6F2D3cOStZvuk2SaP6YrwkNO65iTZcwA2ZkSABegdkAh/lf+Aa/YQndZVfmEXT5vgAp6zv06VQ3ejSVjAny4w==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
@@ -3177,9 +3177,9 @@
} }
}, },
"node_modules/@types/serve-static": { "node_modules/@types/serve-static": {
"version": "1.15.7", "version": "1.15.8",
"resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.7.tgz", "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.8.tgz",
"integrity": "sha512-W8Ym+h8nhuRwaKPaDw34QUkwsGi6Rc4yYqvKFo5rm2FUEhCFbzVWrxXUxuKK8TASjWsysJY0nsmNCGhCOIsrOw==", "integrity": "sha512-roei0UY3LhpOJvjbIP6ZZFngyLKl5dskOtDhxY5THRSpO+ZI+nzJ+m5yUMzGrp89YRa7lvknKkMYjqQFGwA7Sg==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
@@ -3216,57 +3216,57 @@
} }
}, },
"node_modules/@vue/compiler-core": { "node_modules/@vue/compiler-core": {
"version": "3.5.16", "version": "3.5.17",
"resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.5.16.tgz", "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.5.17.tgz",
"integrity": "sha512-AOQS2eaQOaaZQoL1u+2rCJIKDruNXVBZSiUD3chnUrsoX5ZTQMaCvXlWNIfxBJuU15r1o7+mpo5223KVtIhAgQ==", "integrity": "sha512-Xe+AittLbAyV0pabcN7cP7/BenRBNcteM4aSDCtRvGw0d9OL+HG1u/XHLY/kt1q4fyMeZYXyIYrsHuPSiDPosA==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@babel/parser": "^7.27.2", "@babel/parser": "^7.27.5",
"@vue/shared": "3.5.16", "@vue/shared": "3.5.17",
"entities": "^4.5.0", "entities": "^4.5.0",
"estree-walker": "^2.0.2", "estree-walker": "^2.0.2",
"source-map-js": "^1.2.1" "source-map-js": "^1.2.1"
} }
}, },
"node_modules/@vue/compiler-dom": { "node_modules/@vue/compiler-dom": {
"version": "3.5.16", "version": "3.5.17",
"resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.5.16.tgz", "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.5.17.tgz",
"integrity": "sha512-SSJIhBr/teipXiXjmWOVWLnxjNGo65Oj/8wTEQz0nqwQeP75jWZ0n4sF24Zxoht1cuJoWopwj0J0exYwCJ0dCQ==", "integrity": "sha512-+2UgfLKoaNLhgfhV5Ihnk6wB4ljyW1/7wUIog2puUqajiC29Lp5R/IKDdkebh9jTbTogTbsgB+OY9cEWzG95JQ==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@vue/compiler-core": "3.5.16", "@vue/compiler-core": "3.5.17",
"@vue/shared": "3.5.16" "@vue/shared": "3.5.17"
} }
}, },
"node_modules/@vue/compiler-sfc": { "node_modules/@vue/compiler-sfc": {
"version": "3.5.16", "version": "3.5.17",
"resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.5.16.tgz", "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.5.17.tgz",
"integrity": "sha512-rQR6VSFNpiinDy/DVUE0vHoIDUF++6p910cgcZoaAUm3POxgNOOdS/xgoll3rNdKYTYPnnbARDCZOyZ+QSe6Pw==", "integrity": "sha512-rQQxbRJMgTqwRugtjw0cnyQv9cP4/4BxWfTdRBkqsTfLOHWykLzbOc3C4GGzAmdMDxhzU/1Ija5bTjMVrddqww==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@babel/parser": "^7.27.2", "@babel/parser": "^7.27.5",
"@vue/compiler-core": "3.5.16", "@vue/compiler-core": "3.5.17",
"@vue/compiler-dom": "3.5.16", "@vue/compiler-dom": "3.5.17",
"@vue/compiler-ssr": "3.5.16", "@vue/compiler-ssr": "3.5.17",
"@vue/shared": "3.5.16", "@vue/shared": "3.5.17",
"estree-walker": "^2.0.2", "estree-walker": "^2.0.2",
"magic-string": "^0.30.17", "magic-string": "^0.30.17",
"postcss": "^8.5.3", "postcss": "^8.5.6",
"source-map-js": "^1.2.1" "source-map-js": "^1.2.1"
} }
}, },
"node_modules/@vue/compiler-ssr": { "node_modules/@vue/compiler-ssr": {
"version": "3.5.16", "version": "3.5.17",
"resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.5.16.tgz", "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.5.17.tgz",
"integrity": "sha512-d2V7kfxbdsjrDSGlJE7my1ZzCXViEcqN6w14DOsDrUCHEA6vbnVCpRFfrc4ryCP/lCKzX2eS1YtnLE/BuC9f/A==", "integrity": "sha512-hkDbA0Q20ZzGgpj5uZjb9rBzQtIHLS78mMilwrlpWk2Ep37DYntUz0PonQ6kr113vfOEdM+zTBuJDaceNIW0tQ==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@vue/compiler-dom": "3.5.16", "@vue/compiler-dom": "3.5.17",
"@vue/shared": "3.5.16" "@vue/shared": "3.5.17"
} }
}, },
"node_modules/@vue/component-compiler-utils": { "node_modules/@vue/component-compiler-utils": {
@@ -3348,9 +3348,9 @@
"license": "MIT" "license": "MIT"
}, },
"node_modules/@vue/shared": { "node_modules/@vue/shared": {
"version": "3.5.16", "version": "3.5.17",
"resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.5.16.tgz", "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.5.17.tgz",
"integrity": "sha512-c/0fWy3Jw6Z8L9FmTyYfkpM5zklnqqa9+a6dz3DvONRKW2NEbh46BP0FHuLFSWi2TnQEtp91Z6zOWNrU6QiyPg==", "integrity": "sha512-CabR+UN630VnsJO/jHWYBC1YVXyMq94KKp6iF5MQgZJs5I8cmjw6oVMO1oDbtBkENSHSSn/UadWlW/OAgdmKrg==",
"dev": true, "dev": true,
"license": "MIT" "license": "MIT"
}, },
@@ -3600,9 +3600,9 @@
} }
}, },
"node_modules/acorn": { "node_modules/acorn": {
"version": "8.14.1", "version": "8.15.0",
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.1.tgz", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz",
"integrity": "sha512-OvQ/2pUDKmgfCg++xsTX1wGxfTaszcHVcTctW4UJB4hibJx2HXxxO5UmVgyjMa+ZDsiaf5wWLXYpRWMmBI0QHg==", "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"bin": { "bin": {
@@ -3869,9 +3869,9 @@
} }
}, },
"node_modules/axios": { "node_modules/axios": {
"version": "1.9.0", "version": "1.10.0",
"resolved": "https://registry.npmjs.org/axios/-/axios-1.9.0.tgz", "resolved": "https://registry.npmjs.org/axios/-/axios-1.10.0.tgz",
"integrity": "sha512-re4CqKTJaURpzbLHtIi6XpDv20/CnpXOtjRY5/CU32L8gU8ek9UIivcfvSWvmKEngmVbrUtPpdDwWDWL7DNHvg==", "integrity": "sha512-/1xYAC4MP/HEG+3duIhFr4ZQXR4sQXOIe+o6sdqzeykGLx6Upp/1p8MHqhINOvGeP7xyNHe7tsiJByc4SSVUxw==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
@@ -4101,9 +4101,9 @@
"license": "ISC" "license": "ISC"
}, },
"node_modules/bootstrap": { "node_modules/bootstrap": {
"version": "5.3.6", "version": "5.3.7",
"resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-5.3.6.tgz", "resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-5.3.7.tgz",
"integrity": "sha512-jX0GAcRzvdwISuvArXn3m7KZscWWFAf1MKBcnzaN02qWMb3jpMoUX4/qgeiGzqyIb4ojulRzs89UCUmGcFSzTA==", "integrity": "sha512-7KgiD8UHjfcPBHEpDNg+zGz8L3LqR3GVwqZiBRFX04a1BCArZOz1r2kjly2HQ0WokqTO0v1nF+QAt8dsW4lKlw==",
"funding": [ "funding": [
{ {
"type": "github", "type": "github",
@@ -4139,9 +4139,9 @@
"license": "MIT" "license": "MIT"
}, },
"node_modules/brace-expansion": { "node_modules/brace-expansion": {
"version": "1.1.11", "version": "1.1.12",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz",
"integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==",
"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.30001724",
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001720.tgz", "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001724.tgz",
"integrity": "sha512-Ec/2yV2nNPwb4DnTANEV99ZWwm3ZWfdlfkQbWSDDt+PsXEVYwlhPH8tdMaPunYTKKmz7AnHi2oNEi1GcmKCD8g==", "integrity": "sha512-WqJo7p0TbHDOythNTqYujmaJTvtYRZrjpP8TCvH6Vb9CYJerJNKamKzIWOM4BkQatWj9H2lYulpdAQNBe7QhNA==",
"dev": true, "dev": true,
"funding": [ "funding": [
{ {
@@ -4465,9 +4465,9 @@
} }
}, },
"node_modules/chart.js": { "node_modules/chart.js": {
"version": "4.4.9", "version": "4.5.0",
"resolved": "https://registry.npmjs.org/chart.js/-/chart.js-4.4.9.tgz", "resolved": "https://registry.npmjs.org/chart.js/-/chart.js-4.5.0.tgz",
"integrity": "sha512-EyZ9wWKgpAU0fLJ43YAEIF8sr5F2W3LqbS40ZJyHIner2lY14ufqv2VMp69MAiZ2rpwxEUxEhIH/0U3xyRynxg==", "integrity": "sha512-aYeC/jDgSEx8SHWZvANYMioYMZ2KX02W6f6uVfyteuCGcadDLcYVHdfdygsTQkQ4TKn5lghoojAsPj5pu0SnvQ==",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@kurkle/color": "^0.3.0" "@kurkle/color": "^0.3.0"
@@ -4861,13 +4861,13 @@
"license": "MIT" "license": "MIT"
}, },
"node_modules/core-js-compat": { "node_modules/core-js-compat": {
"version": "3.42.0", "version": "3.43.0",
"resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.42.0.tgz", "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.43.0.tgz",
"integrity": "sha512-bQasjMfyDGyaeWKBIu33lHh9qlSR0MFE/Nmc6nMjf/iU9b3rSMdAYz1Baxrv4lPdGUsTqZudHA4jIGSJy0SWZQ==", "integrity": "sha512-2GML2ZsCc5LR7hZYz4AXmjQw8zuy2T//2QntwdnpuYI7jteT6GVYJL7F6C2C57R7gSYrcqVW3lAALefdbhBLDA==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"browserslist": "^4.24.4" "browserslist": "^4.25.0"
}, },
"funding": { "funding": {
"type": "opencollective", "type": "opencollective",
@@ -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.171",
"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.171.tgz",
"integrity": "sha512-hwtetwfKNZo/UlwHIVBlKZVdy7o8bIZxxKs0Mv/ROPiQQQmDgdm5a+KvKtBsxM8ZjFzTaCeLoodZ8jiBE3o9rA==", "integrity": "sha512-scWpzXEJEMrGJa4Y6m/tVotb0WuvNmasv3wWVzUAeCgKU0ToFOhUW6Z+xWnRQANMYGxN4ngJXIThgBJOqzVPCQ==",
"dev": true, "dev": true,
"license": "ISC" "license": "ISC"
}, },
@@ -6322,15 +6322,16 @@
} }
}, },
"node_modules/form-data": { "node_modules/form-data": {
"version": "4.0.2", "version": "4.0.3",
"resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.2.tgz", "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.3.tgz",
"integrity": "sha512-hGfm/slu0ZabnNt4oaRZ6uREyfCj6P4fT/n6A1rGV+Z0VdGXjfOhVUpkn6qVQONHGIFwmveGXyDs75+nr6FM8w==", "integrity": "sha512-qsITQPfmvMOSAdeyZ+12I1c+CKSstAFAwu+97zrnWAbIr5u8wfsExUzCesVLC8NgHuRUqNN4Zy6UPWUTRGslcA==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"asynckit": "^0.4.0", "asynckit": "^0.4.0",
"combined-stream": "^1.0.8", "combined-stream": "^1.0.8",
"es-set-tostringtag": "^2.1.0", "es-set-tostringtag": "^2.1.0",
"hasown": "^2.0.2",
"mime-types": "^2.1.12" "mime-types": "^2.1.12"
}, },
"engines": { "engines": {
@@ -7154,9 +7155,9 @@
} }
}, },
"node_modules/immutable": { "node_modules/immutable": {
"version": "5.1.2", "version": "5.1.3",
"resolved": "https://registry.npmjs.org/immutable/-/immutable-5.1.2.tgz", "resolved": "https://registry.npmjs.org/immutable/-/immutable-5.1.3.tgz",
"integrity": "sha512-qHKXW1q6liAk1Oys6umoaZbDRqjcjgSrbnrifHsfsttza7zcvRAsL7mMV6xWcyhwQy7Xj5v4hhbr6b+iDYwlmQ==", "integrity": "sha512-+chQdDfvscSF1SJqv2gn4SRO2ZyS3xL3r7IW/wWEEzrzLisnOlKiQu5ytC/BVNcS15C39WT2Hg/bjKjDMcu+zg==",
"dev": true, "dev": true,
"license": "MIT" "license": "MIT"
}, },
@@ -7713,9 +7714,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": {
@@ -8856,9 +8857,9 @@
} }
}, },
"node_modules/postcss": { "node_modules/postcss": {
"version": "8.5.4", "version": "8.5.6",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.4.tgz", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz",
"integrity": "sha512-QSa9EBe+uwlGTFmHsPKokv3B/oEMQZxfqW0QqNCyhpa6mB1afzulwn8hihglqAb2pOw+BJgNlmXQ8la2VeHB7w==", "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==",
"dev": true, "dev": true,
"funding": [ "funding": [
{ {
@@ -9973,13 +9974,13 @@
} }
}, },
"node_modules/rollup": { "node_modules/rollup": {
"version": "4.41.1", "version": "4.44.0",
"resolved": "https://registry.npmjs.org/rollup/-/rollup-4.41.1.tgz", "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.44.0.tgz",
"integrity": "sha512-cPmwD3FnFv8rKMBc1MxWCwVQFxwf1JEmSX3iQXrRVVG15zerAIXRjMFVWnd5Q5QvgKF7Aj+5ykXFhUl+QGnyOw==", "integrity": "sha512-qHcdEzLCiktQIfwBq420pn2dP+30uzqYxv9ETm91wdt2R9AFcWfjNAmje4NWlnCIQ5RMTzVf0ZyisOKqHR6RwA==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@types/estree": "1.0.7" "@types/estree": "1.0.8"
}, },
"bin": { "bin": {
"rollup": "dist/bin/rollup" "rollup": "dist/bin/rollup"
@@ -9989,26 +9990,26 @@
"npm": ">=8.0.0" "npm": ">=8.0.0"
}, },
"optionalDependencies": { "optionalDependencies": {
"@rollup/rollup-android-arm-eabi": "4.41.1", "@rollup/rollup-android-arm-eabi": "4.44.0",
"@rollup/rollup-android-arm64": "4.41.1", "@rollup/rollup-android-arm64": "4.44.0",
"@rollup/rollup-darwin-arm64": "4.41.1", "@rollup/rollup-darwin-arm64": "4.44.0",
"@rollup/rollup-darwin-x64": "4.41.1", "@rollup/rollup-darwin-x64": "4.44.0",
"@rollup/rollup-freebsd-arm64": "4.41.1", "@rollup/rollup-freebsd-arm64": "4.44.0",
"@rollup/rollup-freebsd-x64": "4.41.1", "@rollup/rollup-freebsd-x64": "4.44.0",
"@rollup/rollup-linux-arm-gnueabihf": "4.41.1", "@rollup/rollup-linux-arm-gnueabihf": "4.44.0",
"@rollup/rollup-linux-arm-musleabihf": "4.41.1", "@rollup/rollup-linux-arm-musleabihf": "4.44.0",
"@rollup/rollup-linux-arm64-gnu": "4.41.1", "@rollup/rollup-linux-arm64-gnu": "4.44.0",
"@rollup/rollup-linux-arm64-musl": "4.41.1", "@rollup/rollup-linux-arm64-musl": "4.44.0",
"@rollup/rollup-linux-loongarch64-gnu": "4.41.1", "@rollup/rollup-linux-loongarch64-gnu": "4.44.0",
"@rollup/rollup-linux-powerpc64le-gnu": "4.41.1", "@rollup/rollup-linux-powerpc64le-gnu": "4.44.0",
"@rollup/rollup-linux-riscv64-gnu": "4.41.1", "@rollup/rollup-linux-riscv64-gnu": "4.44.0",
"@rollup/rollup-linux-riscv64-musl": "4.41.1", "@rollup/rollup-linux-riscv64-musl": "4.44.0",
"@rollup/rollup-linux-s390x-gnu": "4.41.1", "@rollup/rollup-linux-s390x-gnu": "4.44.0",
"@rollup/rollup-linux-x64-gnu": "4.41.1", "@rollup/rollup-linux-x64-gnu": "4.44.0",
"@rollup/rollup-linux-x64-musl": "4.41.1", "@rollup/rollup-linux-x64-musl": "4.44.0",
"@rollup/rollup-win32-arm64-msvc": "4.41.1", "@rollup/rollup-win32-arm64-msvc": "4.44.0",
"@rollup/rollup-win32-ia32-msvc": "4.41.1", "@rollup/rollup-win32-ia32-msvc": "4.44.0",
"@rollup/rollup-win32-x64-msvc": "4.41.1", "@rollup/rollup-win32-x64-msvc": "4.44.0",
"fsevents": "~2.3.2" "fsevents": "~2.3.2"
} }
}, },
@@ -10064,9 +10065,9 @@
"license": "MIT" "license": "MIT"
}, },
"node_modules/sass": { "node_modules/sass": {
"version": "1.89.1", "version": "1.89.2",
"resolved": "https://registry.npmjs.org/sass/-/sass-1.89.1.tgz", "resolved": "https://registry.npmjs.org/sass/-/sass-1.89.2.tgz",
"integrity": "sha512-eMLLkl+qz7tx/0cJ9wI+w09GQ2zodTkcE/aVfywwdlRcI3EO19xGnbmJwg/JMIm+5MxVJ6outddLZ4Von4E++Q==", "integrity": "sha512-xCmtksBKd/jdJ9Bt9p7nPKiuqrlBMBuuGkQlkhZjjQk3Ty48lv93k5Dq6OPkKt4XwxDJ7tvlfrTa1MPA9bf+QA==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
@@ -10414,9 +10415,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": {
@@ -10858,9 +10859,9 @@
} }
}, },
"node_modules/terser": { "node_modules/terser": {
"version": "5.40.0", "version": "5.43.1",
"resolved": "https://registry.npmjs.org/terser/-/terser-5.40.0.tgz", "resolved": "https://registry.npmjs.org/terser/-/terser-5.43.1.tgz",
"integrity": "sha512-cfeKl/jjwSR5ar7d0FGmave9hFGJT8obyo0z+CrQOylLDbk7X81nPU6vq9VORa5jU30SkDnT2FXjLbR8HLP+xA==", "integrity": "sha512-+6erLbBm0+LROX2sPXlUYx/ux5PyE9K/a92Wrt6oA+WDAoFTdpHE5tCYCI5PNzq2y8df4rA+QgHLJuR4jNymsg==",
"dev": true, "dev": true,
"license": "BSD-2-Clause", "license": "BSD-2-Clause",
"dependencies": { "dependencies": {
@@ -11013,9 +11014,9 @@
} }
}, },
"node_modules/tinyglobby/node_modules/fdir": { "node_modules/tinyglobby/node_modules/fdir": {
"version": "6.4.5", "version": "6.4.6",
"resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.5.tgz", "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.6.tgz",
"integrity": "sha512-4BG7puHpVsIYxZUbiUE3RqGloLaSSwzYie5jvasC4LWuBWzZawynvYouhjbQKw2JuIGYdm0DzIxl8iVidKlUEw==", "integrity": "sha512-hiFoqpyZcfNm1yc4u8oWCf9A2c4D3QjCrks3zmoVKVxpQRzmPNar1hUJcBG2RQHvEVGDN+Jm81ZheVLAQMK6+w==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"peerDependencies": { "peerDependencies": {
@@ -11133,9 +11134,9 @@
} }
}, },
"node_modules/undici-types": { "node_modules/undici-types": {
"version": "6.21.0", "version": "7.8.0",
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.8.0.tgz",
"integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", "integrity": "sha512-9UJ2xGDvQ43tYyVMpuHlsgApydB8ZKfVYTsLDhXkFL/6gfkp+U8xTGdh8pMJv1SpZna0zxG1DwsKZsreLbXBxw==",
"dev": true, "dev": true,
"license": "MIT" "license": "MIT"
}, },
@@ -11423,9 +11424,9 @@
"license": "MIT" "license": "MIT"
}, },
"node_modules/vite/node_modules/fdir": { "node_modules/vite/node_modules/fdir": {
"version": "6.4.5", "version": "6.4.6",
"resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.5.tgz", "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.6.tgz",
"integrity": "sha512-4BG7puHpVsIYxZUbiUE3RqGloLaSSwzYie5jvasC4LWuBWzZawynvYouhjbQKw2JuIGYdm0DzIxl8iVidKlUEw==", "integrity": "sha512-hiFoqpyZcfNm1yc4u8oWCf9A2c4D3QjCrks3zmoVKVxpQRzmPNar1hUJcBG2RQHvEVGDN+Jm81ZheVLAQMK6+w==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"peerDependencies": { "peerDependencies": {
@@ -12081,9 +12082,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

@@ -112,7 +112,7 @@
"webhook_trigger_DESTROY_TRANSACTION": "After transaction delete", "webhook_trigger_DESTROY_TRANSACTION": "After transaction delete",
"webhook_response_TRANSACTIONS": "Transaction details", "webhook_response_TRANSACTIONS": "Transaction details",
"webhook_response_ACCOUNTS": "Account details", "webhook_response_ACCOUNTS": "Account details",
"webhook_response_none_NONE": "No details", "webhook_response_none_NONE": "Tidak ada detil",
"webhook_delivery_JSON": "JSON", "webhook_delivery_JSON": "JSON",
"actions": "Tindakan", "actions": "Tindakan",
"meta_data": "Data meta", "meta_data": "Data meta",
@@ -129,7 +129,7 @@
"edit_webhook_js": "Edit webhook \"{title}\"", "edit_webhook_js": "Edit webhook \"{title}\"",
"webhook_was_triggered": "The webhook was triggered on the indicated transaction. Please wait for results to appear.", "webhook_was_triggered": "The webhook was triggered on the indicated transaction. Please wait for results to appear.",
"view_message": "View message", "view_message": "View message",
"view_attempts": "View failed attempts", "view_attempts": "Lihat upaya yang gagal",
"message_content_title": "Webhook message content", "message_content_title": "Webhook message content",
"message_content_help": "This is the content of the message that was sent (or tried) using this webhook.", "message_content_help": "This is the content of the message that was sent (or tried) using this webhook.",
"attempt_content_title": "Webhook attempts", "attempt_content_title": "Webhook attempts",
@@ -137,7 +137,7 @@
"no_attempts": "There are no unsuccessful attempts. That's a good thing!", "no_attempts": "There are no unsuccessful attempts. That's a good thing!",
"webhook_attempt_at": "Attempt at {moment}", "webhook_attempt_at": "Attempt at {moment}",
"logs": "Log", "logs": "Log",
"response": "Response", "response": "Tanggapan",
"visit_webhook_url": "Visit webhook URL", "visit_webhook_url": "Visit webhook URL",
"reset_webhook_secret": "Reset webhook secret", "reset_webhook_secret": "Reset webhook secret",
"header_exchange_rates": "Exchange rates", "header_exchange_rates": "Exchange rates",

View File

@@ -31,7 +31,7 @@
"apply_rules_checkbox": "Apply rules", "apply_rules_checkbox": "Apply rules",
"fire_webhooks_checkbox": "Fire webhooks", "fire_webhooks_checkbox": "Fire webhooks",
"no_budget_pointer": "Hen\u00fcz b\u00fct\u00e7eniz yok gibi g\u00f6r\u00fcn\u00fcyor. <a href=\"budgets\">b\u00fct\u00e7eler<\/a> sayfas\u0131nda biraz olu\u015fturmal\u0131s\u0131n\u0131z. B\u00fct\u00e7eler, giderleri takip etmenize yard\u0131mc\u0131 olabilir.", "no_budget_pointer": "Hen\u00fcz b\u00fct\u00e7eniz yok gibi g\u00f6r\u00fcn\u00fcyor. <a href=\"budgets\">b\u00fct\u00e7eler<\/a> sayfas\u0131nda biraz olu\u015fturmal\u0131s\u0131n\u0131z. B\u00fct\u00e7eler, giderleri takip etmenize yard\u0131mc\u0131 olabilir.",
"no_bill_pointer": "You seem to have no subscription yet. You should create some on the <a href=\"subscriptions\">subscription<\/a>-page. Subscriptions can help you keep track of expenses.", "no_bill_pointer": "Hen\u00fcz aboneli\u011finiz yok gibi g\u00f6r\u00fcn\u00fcyor. <a href=\"subscriptions\">Abonelik<\/a> sayfas\u0131nda bir abonelik olu\u015fturmal\u0131s\u0131n\u0131z. Abonelikler, harcamalar\u0131n\u0131z\u0131 takip etmenize yard\u0131mc\u0131 olabilir.",
"source_account": "Kaynak hesap", "source_account": "Kaynak hesap",
"hidden_fields_preferences": "You can enable more transaction options in your <a href=\"preferences\">preferences<\/a>.", "hidden_fields_preferences": "You can enable more transaction options in your <a href=\"preferences\">preferences<\/a>.",
"destination_account": "Hedef hesap", "destination_account": "Hedef hesap",

Some files were not shown because too many files have changed in this diff Show More