Compare commits

..

31 Commits

Author SHA1 Message Date
github-actions[bot]
dc7d4fb258 Merge pull request #10698 from firefly-iii/release-1754331101
🤖 Automatically merge the PR into the develop branch.
2025-08-04 20:11:50 +02:00
JC5
a807ca5002 🤖 Auto commit for release 'develop' on 2025-08-04 2025-08-04 20:11:41 +02:00
github-actions[bot]
d59d326841 Merge pull request #10693 from firefly-iii/release-1754278961
🤖 Automatically merge the PR into the develop branch.
2025-08-04 05:42:50 +02:00
JC5
b915548e82 🤖 Auto commit for release 'develop' on 2025-08-04 2025-08-04 05:42:41 +02:00
James Cole
8200a81840 Add enrichment. 2025-08-03 20:19:07 +02:00
James Cole
6a49918707 Add budget transformer and enrichment. 2025-08-03 20:17:50 +02:00
James Cole
e55fc483bd Merge branch 'develop' of github.com:firefly-iii/firefly-iii into develop 2025-08-03 17:42:13 +02:00
James Cole
4ff5f5883d Improve budget limit. 2025-08-03 17:42:07 +02:00
github-actions[bot]
eda2eae04a Merge pull request #10689 from firefly-iii/release-1754232349
🤖 Automatically merge the PR into the develop branch.
2025-08-03 16:45:56 +02:00
JC5
c07c30ea17 🤖 Auto commit for release 'develop' on 2025-08-03 2025-08-03 16:45:49 +02:00
James Cole
56f1eb03e0 Add missing field. 2025-08-03 10:24:49 +02:00
James Cole
d4e14dd262 Move account balance logic to enrichment. 2025-08-03 10:22:12 +02:00
James Cole
0c7f04fb17 Expand bill transformer. 2025-08-03 08:12:19 +02:00
James Cole
061c01da53 Remove unnecessary setters. 2025-08-03 08:02:13 +02:00
James Cole
716d72d8af Optimize available budgets. 2025-08-03 07:53:36 +02:00
James Cole
3233ca4a4c Fix badly named field. 2025-08-03 07:13:57 +02:00
James Cole
1041030b1e First start on optimized available balance enrichment. 2025-08-03 07:12:06 +02:00
James Cole
bb3b06cf08 Fix #10687 2025-08-02 19:17:37 +02:00
James Cole
f35e361915 Match fields for 6.3.0 2025-08-02 14:21:22 +02:00
James Cole
47d697c7dc Remove references to "balances". 2025-08-02 11:07:35 +02:00
James Cole
3745d79f1f Clean up account transformer. 2025-08-02 07:16:30 +02:00
github-actions[bot]
04cbff4b9a Merge pull request #10684 from firefly-iii/release-1754070963
🤖 Automatically merge the PR into the develop branch.
2025-08-01 19:56:12 +02:00
JC5
0c2ca4b97c 🤖 Auto commit for release 'develop' on 2025-08-01 2025-08-01 19:56:03 +02:00
James Cole
3918665cd1 Move all endpoints to v1. 2025-08-01 19:41:36 +02:00
James Cole
9eb8869649 Fix files. 2025-08-01 19:33:30 +02:00
github-actions[bot]
62221af591 Merge pull request #10683 from firefly-iii/release-1754069211
🤖 Automatically merge the PR into the develop branch.
2025-08-01 19:26:58 +02:00
JC5
4d013a44ce 🤖 Auto commit for release 'develop' on 2025-08-01 2025-08-01 19:26:51 +02:00
Sander Dorigo
c55cfd1acf Rename file 2025-08-01 15:30:45 +02:00
github-actions[bot]
b2652b83ce Merge pull request #10682 from firefly-iii/release-1754049870
🤖 Automatically merge the PR into the develop branch.
2025-08-01 14:04:37 +02:00
JC5
3d28932216 🤖 Auto commit for release 'develop' on 2025-08-01 2025-08-01 14:04:30 +02:00
Sander Dorigo
87567d5a31 Rename default to primary 2025-08-01 13:48:32 +02:00
103 changed files with 1291 additions and 693 deletions

View File

@@ -26,7 +26,7 @@ namespace FireflyIII\Api\V1\Controllers\Chart;
use Carbon\Carbon;
use FireflyIII\Api\V1\Controllers\Controller;
use FireflyIII\Api\V1\Requests\Generic\DateRequest;
use FireflyIII\Api\V1\Requests\Data\DateRequest;
use FireflyIII\Enums\UserRoleEnum;
use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Models\Budget;
@@ -291,7 +291,7 @@ class BudgetController extends Controller
}
if ($current->transaction_currency_id !== $this->primaryCurrency->id) {
// convert and then add it.
$converted = $converter->convert($current->transactionCurrency, $this->primaryCurrency, $limit->start_date, $limit->amount);
$converted = $converter->convert($current->transactionCurrency, $this->primaryCurrency, $current->start_date, $current->amount);
$amount = bcadd($amount, $converted);
Log::debug(sprintf('Budgeted in limit #%d: %s %s, converted to %s %s', $current->id, $current->transactionCurrency->code, $current->amount, $this->primaryCurrency->code, $converted));
Log::debug(sprintf('Set amount in limit to %s', $amount));

View File

@@ -26,7 +26,7 @@ namespace FireflyIII\Api\V1\Controllers\Chart;
use Carbon\Carbon;
use FireflyIII\Api\V1\Controllers\Controller;
use FireflyIII\Api\V1\Request\Generic\DateRequest;
use FireflyIII\Api\V1\Requests\Data\DateRequest;
use FireflyIII\Enums\AccountTypeEnum;
use FireflyIII\Enums\TransactionTypeEnum;
use FireflyIII\Enums\UserRoleEnum;

View File

@@ -96,8 +96,8 @@ class ShowController extends Controller
/** @var User $admin */
$admin = auth()->user();
$enrichment = new AccountEnrichment();
$enrichment->setDate($this->parameters->get('date'));
$enrichment->setUser($admin);
$enrichment->setPrimaryCurrency($this->primaryCurrency);
$accounts = $enrichment->enrich($accounts);
// make paginator:
@@ -131,8 +131,8 @@ class ShowController extends Controller
/** @var User $admin */
$admin = auth()->user();
$enrichment = new AccountEnrichment();
$enrichment->setDate($this->parameters->get('date'));
$enrichment->setUser($admin);
$enrichment->setPrimaryCurrency($this->primaryCurrency);
$account = $enrichment->enrichSingle($account);

View File

@@ -75,8 +75,8 @@ class StoreController extends Controller
/** @var User $admin */
$admin = auth()->user();
$enrichment = new AccountEnrichment();
$enrichment->setDate(null);
$enrichment->setUser($admin);
$enrichment->setPrimaryCurrency($this->primaryCurrency);
$account = $enrichment->enrichSingle($account);
/** @var AccountTransformer $transformer */

View File

@@ -80,8 +80,8 @@ class UpdateController extends Controller
/** @var User $admin */
$admin = auth()->user();
$enrichment = new AccountEnrichment();
$enrichment->setDate(null);
$enrichment->setUser($admin);
$enrichment->setPrimaryCurrency($this->primaryCurrency);
$account = $enrichment->enrichSingle($account);
/** @var AccountTransformer $transformer */

View File

@@ -28,6 +28,7 @@ use FireflyIII\Api\V1\Controllers\Controller;
use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Models\AvailableBudget;
use FireflyIII\Repositories\Budget\AvailableBudgetRepositoryInterface;
use FireflyIII\Support\JsonApi\Enrichments\AvailableBudgetEnrichment;
use FireflyIII\Transformers\AvailableBudgetTransformer;
use FireflyIII\User;
use Illuminate\Http\JsonResponse;
@@ -75,7 +76,6 @@ class ShowController extends Controller
// types to get, page size:
$pageSize = $this->parameters->get('limit');
$start = $this->parameters->get('start');
$end = $this->parameters->get('end');
@@ -84,6 +84,13 @@ class ShowController extends Controller
$count = $collection->count();
$availableBudgets = $collection->slice(($this->parameters->get('page') - 1) * $pageSize, $pageSize);
// enrich
/** @var User $admin */
$admin = auth()->user();
$enrichment = new AvailableBudgetEnrichment();
$enrichment->setUser($admin);
$availableBudgets = $enrichment->enrich($availableBudgets);
// make paginator:
$paginator = new LengthAwarePaginator($availableBudgets, $count, $pageSize, $this->parameters->get('page'));
$paginator->setPath(route('api.v1.available-budgets.index').$this->buildParams());
@@ -106,13 +113,25 @@ class ShowController extends Controller
*/
public function show(AvailableBudget $availableBudget): JsonResponse
{
$manager = $this->getManager();
$manager = $this->getManager();
$start = $this->parameters->get('start');
$end = $this->parameters->get('end');
/** @var AvailableBudgetTransformer $transformer */
$transformer = app(AvailableBudgetTransformer::class);
$transformer = app(AvailableBudgetTransformer::class);
$transformer->setParameters($this->parameters);
$resource = new Item($availableBudget, $transformer, 'available_budgets');
// enrich
/** @var User $admin */
$admin = auth()->user();
$enrichment = new AvailableBudgetEnrichment();
$enrichment->setUser($admin);
$enrichment->setStart($start);
$enrichment->setEnd($end);
$availableBudget = $enrichment->enrichSingle($availableBudget);
$resource = new Item($availableBudget, $transformer, 'available_budgets');
return response()->json($manager->createData($resource)->toArray())->header('Content-Type', self::CONTENT_TYPE);
}

View File

@@ -83,8 +83,6 @@ class ShowController extends Controller
$admin = auth()->user();
$enrichment = new SubscriptionEnrichment();
$enrichment->setUser($admin);
$enrichment->setConvertToPrimary($this->convertToPrimary);
$enrichment->setPrimaryCurrency($this->primaryCurrency);
$enrichment->setStart($this->parameters->get('start'));
$enrichment->setEnd($this->parameters->get('end'));
$bills = $enrichment->enrich($bills);
@@ -114,8 +112,6 @@ class ShowController extends Controller
$admin = auth()->user();
$enrichment = new SubscriptionEnrichment();
$enrichment->setUser($admin);
$enrichment->setConvertToPrimary($this->convertToPrimary);
$enrichment->setPrimaryCurrency($this->primaryCurrency);
$enrichment->setStart($this->parameters->get('start'));
$enrichment->setEnd($this->parameters->get('end'));
$bill = $enrichment->enrichSingle($bill);

View File

@@ -79,8 +79,6 @@ class StoreController extends Controller
$admin = auth()->user();
$enrichment = new SubscriptionEnrichment();
$enrichment->setUser($admin);
$enrichment->setConvertToPrimary($this->convertToPrimary);
$enrichment->setPrimaryCurrency($this->primaryCurrency);
$enrichment->setStart($this->parameters->get('start'));
$enrichment->setEnd($this->parameters->get('end'));
$bill = $enrichment->enrichSingle($bill);

View File

@@ -74,8 +74,6 @@ class UpdateController extends Controller
$admin = auth()->user();
$enrichment = new SubscriptionEnrichment();
$enrichment->setUser($admin);
$enrichment->setConvertToPrimary($this->convertToPrimary);
$enrichment->setPrimaryCurrency($this->primaryCurrency);
$enrichment->setStart($this->parameters->get('start'));
$enrichment->setEnd($this->parameters->get('end'));
$bill = $enrichment->enrichSingle($bill);

View File

@@ -32,6 +32,7 @@ use FireflyIII\Models\Budget;
use FireflyIII\Repositories\Budget\BudgetLimitRepositoryInterface;
use FireflyIII\Repositories\Budget\BudgetRepositoryInterface;
use FireflyIII\Support\Http\Api\TransactionFilter;
use FireflyIII\Support\JsonApi\Enrichments\BudgetLimitEnrichment;
use FireflyIII\Support\JsonApi\Enrichments\TransactionGroupEnrichment;
use FireflyIII\Transformers\AttachmentTransformer;
use FireflyIII\Transformers\BudgetLimitTransformer;
@@ -117,6 +118,14 @@ class ListController extends Controller
$paginator = new LengthAwarePaginator($budgetLimits, $count, $pageSize, $this->parameters->get('page'));
$paginator->setPath(route('api.v1.budgets.budget-limits', [$budget->id]).$this->buildParams());
// enrich
/** @var User $admin */
$admin = auth()->user();
$enrichment = new BudgetLimitEnrichment();
$enrichment->setUser($admin);
$budgetLimits = $enrichment->enrich($budgetLimits);
/** @var BudgetLimitTransformer $transformer */
$transformer = app(BudgetLimitTransformer::class);
$transformer->setParameters($this->parameters);

View File

@@ -29,7 +29,9 @@ use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Models\Budget;
use FireflyIII\Repositories\Budget\BudgetLimitRepositoryInterface;
use FireflyIII\Repositories\Budget\BudgetRepositoryInterface;
use FireflyIII\Support\JsonApi\Enrichments\BudgetEnrichment;
use FireflyIII\Transformers\BudgetTransformer;
use FireflyIII\User;
use Illuminate\Http\JsonResponse;
use Illuminate\Pagination\LengthAwarePaginator;
use League\Fractal\Pagination\IlluminatePaginatorAdapter;
@@ -82,6 +84,15 @@ class ShowController extends Controller
$count = $collection->count();
$budgets = $collection->slice(($this->parameters->get('page') - 1) * $pageSize, $pageSize);
// enrich
/** @var User $admin */
$admin = auth()->user();
$enrichment = new BudgetEnrichment();
$enrichment->setUser($admin);
$enrichment->setStart($this->parameters->get('start'));
$enrichment->setEnd($this->parameters->get('end'));
$budgets = $enrichment->enrich($budgets);
// make paginator:
$paginator = new LengthAwarePaginator($budgets, $count, $pageSize, $this->parameters->get('page'));
$paginator->setPath(route('api.v1.budgets.index').$this->buildParams());
@@ -103,6 +114,15 @@ class ShowController extends Controller
{
$manager = $this->getManager();
// enrich
/** @var User $admin */
$admin = auth()->user();
$enrichment = new BudgetEnrichment();
$enrichment->setUser($admin);
$enrichment->setStart($this->parameters->get('start'));
$enrichment->setEnd($this->parameters->get('end'));
$budget = $enrichment->enrichSingle($budget);
/** @var BudgetTransformer $transformer */
$transformer = app(BudgetTransformer::class);
$transformer->setParameters($this->parameters);

View File

@@ -28,7 +28,9 @@ use FireflyIII\Api\V1\Controllers\Controller;
use FireflyIII\Api\V1\Requests\Models\Budget\StoreRequest;
use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Repositories\Budget\BudgetRepositoryInterface;
use FireflyIII\Support\JsonApi\Enrichments\BudgetEnrichment;
use FireflyIII\Transformers\BudgetTransformer;
use FireflyIII\User;
use Illuminate\Http\JsonResponse;
use League\Fractal\Resource\Item;
@@ -69,6 +71,13 @@ class StoreController extends Controller
$budget->refresh();
$manager = $this->getManager();
// enrich
/** @var User $admin */
$admin = auth()->user();
$enrichment = new BudgetEnrichment();
$enrichment->setUser($admin);
$budget = $enrichment->enrichSingle($budget);
/** @var BudgetTransformer $transformer */
$transformer = app(BudgetTransformer::class);
$transformer->setParameters($this->parameters);

View File

@@ -28,7 +28,9 @@ use FireflyIII\Api\V1\Controllers\Controller;
use FireflyIII\Api\V1\Requests\Models\Budget\UpdateRequest;
use FireflyIII\Models\Budget;
use FireflyIII\Repositories\Budget\BudgetRepositoryInterface;
use FireflyIII\Support\JsonApi\Enrichments\BudgetEnrichment;
use FireflyIII\Transformers\BudgetTransformer;
use FireflyIII\User;
use Illuminate\Http\JsonResponse;
use League\Fractal\Resource\Item;
@@ -67,6 +69,13 @@ class UpdateController extends Controller
$budget = $this->repository->update($budget, $data);
$manager = $this->getManager();
// enrich
/** @var User $admin */
$admin = auth()->user();
$enrichment = new BudgetEnrichment();
$enrichment->setUser($admin);
$budget = $enrichment->enrichSingle($budget);
/** @var BudgetTransformer $transformer */
$transformer = app(BudgetTransformer::class);
$transformer->setParameters($this->parameters);

View File

@@ -31,6 +31,7 @@ use FireflyIII\Models\Budget;
use FireflyIII\Models\BudgetLimit;
use FireflyIII\Repositories\Budget\BudgetLimitRepositoryInterface;
use FireflyIII\Repositories\Budget\BudgetRepositoryInterface;
use FireflyIII\Support\JsonApi\Enrichments\BudgetLimitEnrichment;
use FireflyIII\Transformers\BudgetLimitTransformer;
use FireflyIII\User;
use Illuminate\Http\JsonResponse;
@@ -84,6 +85,14 @@ class ShowController extends Controller
$paginator = new LengthAwarePaginator($budgetLimits, $count, $pageSize, $this->parameters->get('page'));
$paginator->setPath(route('api.v1.budgets.limits.index', [$budget->id]).$this->buildParams());
// enrich
/** @var User $admin */
$admin = auth()->user();
$enrichment = new BudgetLimitEnrichment();
$enrichment->setUser($admin);
$budgetLimits = $enrichment->enrich($budgetLimits);
/** @var BudgetLimitTransformer $transformer */
$transformer = app(BudgetLimitTransformer::class);
$transformer->setParameters($this->parameters);
@@ -113,6 +122,13 @@ class ShowController extends Controller
$paginator = new LengthAwarePaginator($budgetLimits, $count, $pageSize, $this->parameters->get('page'));
$paginator->setPath(route('api.v1.budget-limits.index').$this->buildParams());
// enrich
/** @var User $admin */
$admin = auth()->user();
$enrichment = new BudgetLimitEnrichment();
$enrichment->setUser($admin);
$budgetLimits = $enrichment->enrich($budgetLimits);
/** @var BudgetLimitTransformer $transformer */
$transformer = app(BudgetLimitTransformer::class);
$transformer->setParameters($this->parameters);
@@ -137,6 +153,13 @@ class ShowController extends Controller
// continue!
$manager = $this->getManager();
// enrich
/** @var User $admin */
$admin = auth()->user();
$enrichment = new BudgetLimitEnrichment();
$enrichment->setUser($admin);
$budgetLimit = $enrichment->enrichSingle($budgetLimit);
/** @var BudgetLimitTransformer $transformer */
$transformer = app(BudgetLimitTransformer::class);
$transformer->setParameters($this->parameters);

View File

@@ -28,6 +28,7 @@ use FireflyIII\Api\V1\Controllers\Controller;
use FireflyIII\Api\V1\Requests\Models\BudgetLimit\StoreRequest;
use FireflyIII\Models\Budget;
use FireflyIII\Repositories\Budget\BudgetLimitRepositoryInterface;
use FireflyIII\Support\JsonApi\Enrichments\BudgetLimitEnrichment;
use FireflyIII\Transformers\BudgetLimitTransformer;
use FireflyIII\User;
use Illuminate\Http\JsonResponse;
@@ -74,6 +75,13 @@ class StoreController extends Controller
$budgetLimit = $this->blRepository->store($data);
$manager = $this->getManager();
// enrich
/** @var User $admin */
$admin = auth()->user();
$enrichment = new BudgetLimitEnrichment();
$enrichment->setUser($admin);
$budgetLimit = $enrichment->enrichSingle($budgetLimit);
/** @var BudgetLimitTransformer $transformer */
$transformer = app(BudgetLimitTransformer::class);
$transformer->setParameters($this->parameters);

View File

@@ -30,6 +30,7 @@ use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Models\Budget;
use FireflyIII\Models\BudgetLimit;
use FireflyIII\Repositories\Budget\BudgetLimitRepositoryInterface;
use FireflyIII\Support\JsonApi\Enrichments\BudgetLimitEnrichment;
use FireflyIII\Transformers\BudgetLimitTransformer;
use FireflyIII\User;
use Illuminate\Http\JsonResponse;
@@ -80,6 +81,13 @@ class UpdateController extends Controller
$budgetLimit = $this->blRepository->update($budgetLimit, $data);
$manager = $this->getManager();
// enrich
/** @var User $admin */
$admin = auth()->user();
$enrichment = new BudgetLimitEnrichment();
$enrichment->setUser($admin);
$budgetLimit = $enrichment->enrich($budgetLimit);
/** @var BudgetLimitTransformer $transformer */
$transformer = app(BudgetLimitTransformer::class);
$transformer->setParameters($this->parameters);

View File

@@ -85,8 +85,6 @@ class ListController extends Controller
$admin = auth()->user();
$enrichment = new SubscriptionEnrichment();
$enrichment->setUser($admin);
$enrichment->setConvertToPrimary($this->convertToPrimary);
$enrichment->setPrimaryCurrency($this->primaryCurrency);
$enrichment->setStart($this->parameters->get('start'));
$enrichment->setEnd($this->parameters->get('end'));
$bills = $enrichment->enrich($bills);

View File

@@ -83,8 +83,8 @@ class ListController extends Controller
/** @var User $admin */
$admin = auth()->user();
$enrichment = new AccountEnrichment();
$enrichment->setDate($this->parameters->get('date'));
$enrichment->setUser($admin);
$enrichment->setPrimaryCurrency($this->primaryCurrency);
$accounts = $enrichment->enrich($accounts);
// make paginator:

View File

@@ -43,6 +43,7 @@ use FireflyIII\Repositories\Rule\RuleRepositoryInterface;
use FireflyIII\Support\Http\Api\AccountFilter;
use FireflyIII\Support\Http\Api\TransactionFilter;
use FireflyIII\Support\JsonApi\Enrichments\AccountEnrichment;
use FireflyIII\Support\JsonApi\Enrichments\BudgetLimitEnrichment;
use FireflyIII\Support\JsonApi\Enrichments\SubscriptionEnrichment;
use FireflyIII\Support\JsonApi\Enrichments\TransactionGroupEnrichment;
use FireflyIII\Transformers\AccountTransformer;
@@ -107,8 +108,8 @@ class ListController extends Controller
/** @var User $admin */
$admin = auth()->user();
$enrichment = new AccountEnrichment();
$enrichment->setDate($this->parameters->get('date'));
$enrichment->setUser($admin);
$enrichment->setPrimaryCurrency($this->primaryCurrency);
$accounts = $enrichment->enrich($accounts);
// make paginator:
@@ -188,8 +189,6 @@ class ListController extends Controller
$admin = auth()->user();
$enrichment = new SubscriptionEnrichment();
$enrichment->setUser($admin);
$enrichment->setConvertToPrimary($this->convertToPrimary);
$enrichment->setPrimaryCurrency($this->primaryCurrency);
$enrichment->setStart($this->parameters->get('start'));
$enrichment->setEnd($this->parameters->get('end'));
$bills = $enrichment->enrichSingle($bills);
@@ -229,6 +228,13 @@ class ListController extends Controller
$paginator = new LengthAwarePaginator($budgetLimits, $count, $pageSize, $this->parameters->get('page'));
$paginator->setPath(route('api.v1.currencies.budget-limits', [$currency->code]).$this->buildParams());
// enrich
/** @var User $admin */
$admin = auth()->user();
$enrichment = new BudgetLimitEnrichment();
$enrichment->setUser($admin);
$budgetLimits = $enrichment->enrich($budgetLimits);
/** @var BudgetLimitTransformer $transformer */
$transformer = app(BudgetLimitTransformer::class);
$transformer->setParameters($this->parameters);

View File

@@ -88,8 +88,8 @@ class AccountController extends Controller
/** @var User $admin */
$admin = auth()->user();
$enrichment = new AccountEnrichment();
$enrichment->setDate($this->parameters->get('date'));
$enrichment->setUser($admin);
$enrichment->setPrimaryCurrency($this->primaryCurrency);
$accounts = $enrichment->enrich($accounts);
/** @var AccountTransformer $transformer */

View File

@@ -1,62 +0,0 @@
<?php
/*
* DateRequest.php
* Copyright (c) 2021 james@firefly-iii.org
*
* This file is part of Firefly III (https://github.com/firefly-iii).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace FireflyIII\Api\V1\Requests\Generic;
use FireflyIII\Support\Request\ChecksLogin;
use FireflyIII\Support\Request\ConvertsDataTypes;
use Illuminate\Foundation\Http\FormRequest;
/**
* Request class for end points that require date parameters.
*
* Class DateRequest
*/
class DateRequest extends FormRequest
{
use ChecksLogin;
use ConvertsDataTypes;
/**
* Get all data from the request.
*/
public function getAll(): array
{
return [
'start' => $this->getCarbonDate('start')->startOfDay(),
'end' => $this->getCarbonDate('end')->endOfDay(),
];
}
/**
* The rules that the incoming request must be matched against.
*/
public function rules(): array
{
return [
'start' => 'required|date|after:1970-01-02|before:2038-01-17',
'end' => 'required|date|after_or_equal:start|before:2038-01-17|after:1970-01-02',
];
}
}

View File

@@ -25,7 +25,6 @@ declare(strict_types=1);
namespace FireflyIII\Api\V1\Requests\Models\PiggyBank;
use Illuminate\Contracts\Validation\Validator;
use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Models\TransactionCurrency;
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
use FireflyIII\Rules\IsValidZeroOrMoreAmount;
@@ -96,7 +95,10 @@ class StoreRequest extends FormRequest
function (Validator $validator): void {
// validate start before end only if both are there.
$data = $validator->getData();
$currency = $this->getCurrencyFromData($data);
$currency = $this->getCurrencyFromData($validator, $data);
if (null === $currency) {
return;
}
$targetAmount = (string) ($data['target_amount'] ?? '0');
$currentAmount = '0';
if (array_key_exists('accounts', $data) && is_array($data['accounts'])) {
@@ -130,7 +132,7 @@ class StoreRequest extends FormRequest
}
}
private function getCurrencyFromData(array $data): TransactionCurrency
private function getCurrencyFromData(Validator $validator, array $data): ?TransactionCurrency
{
if (array_key_exists('transaction_currency_code', $data) && '' !== (string) $data['transaction_currency_code']) {
$currency = TransactionCurrency::whereCode($data['transaction_currency_code'])->first();
@@ -144,7 +146,8 @@ class StoreRequest extends FormRequest
return $currency;
}
}
$validator->errors()->add('transaction_currency_id', trans('validation.require_currency_id_code'));
throw new FireflyException('Unexpected empty currency.');
return null;
}
}

View File

@@ -63,10 +63,10 @@ class CorrectsCurrencies extends Command
$repos = app(CurrencyRepositoryInterface::class);
// first check if the user has any default currency (not necessarily the case, so can be forced).
$defaultCurrency = app('amount')->getPrimaryCurrencyByUserGroup($userGroup);
$primaryCurrency = app('amount')->getPrimaryCurrencyByUserGroup($userGroup);
Log::debug(sprintf('Now correcting currencies for user group #%d', $userGroup->id));
$found = [$defaultCurrency->id];
$found = [$primaryCurrency->id];
// get all meta entries
$meta = AccountMeta::leftJoin('accounts', 'accounts.id', '=', 'account_meta.account_id')

View File

@@ -108,11 +108,11 @@ class UpgradesAccountCurrencies extends Command
$accounts = $this->accountRepos->getAccountsByType([AccountTypeEnum::DEFAULT->value, AccountTypeEnum::ASSET->value]);
// get user's currency preference:
$defaultCurrency = app('amount')->getPrimaryCurrencyByUserGroup($user->userGroup);
$primaryCurrency = app('amount')->getPrimaryCurrencyByUserGroup($user->userGroup);
/** @var Account $account */
foreach ($accounts as $account) {
$this->updateAccount($account, $defaultCurrency);
$this->updateAccount($account, $primaryCurrency);
}
}

View File

@@ -117,13 +117,13 @@ class UpgradesCurrencyPreferences extends Command
// set the default currency for the user and for the group:
$preference = $this->getPreference($user);
$defaultCurrency = TransactionCurrency::where('code', $preference)->first();
if (null === $defaultCurrency) {
$primaryCurrency = TransactionCurrency::where('code', $preference)->first();
if (null === $primaryCurrency) {
// get EUR
$defaultCurrency = TransactionCurrency::where('code', 'EUR')->first();
$primaryCurrency = TransactionCurrency::where('code', 'EUR')->first();
}
$user->currencies()->updateExistingPivot($defaultCurrency->id, ['user_default' => true]);
$user->userGroup->currencies()->updateExistingPivot($defaultCurrency->id, ['group_default' => true]);
$user->currencies()->updateExistingPivot($primaryCurrency->id, ['user_default' => true]);
$user->userGroup->currencies()->updateExistingPivot($primaryCurrency->id, ['group_default' => true]);
}
private function getPreference(User $user): string

View File

@@ -1,7 +1,7 @@
<?php
/*
* UserGroupChangedDefaultCurrency.php
* UserGroupChangedPrimaryCurrency.php
* Copyright (c) 2024 james@firefly-iii.org.
*
* This file is part of Firefly III (https://github.com/firefly-iii).
@@ -29,7 +29,7 @@ use FireflyIII\Models\UserGroup;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\Log;
class UserGroupChangedDefaultCurrency extends Event
class UserGroupChangedPrimaryCurrency extends Event
{
use SerializesModels;

View File

@@ -126,7 +126,7 @@ class PiggyBankFactory
private function getCurrency(array $data): TransactionCurrency
{
// currency:
$defaultCurrency = app('amount')->getPrimaryCurrency();
$primaryCurrency = app('amount')->getPrimaryCurrency();
$currency = null;
if (array_key_exists('transaction_currency_code', $data)) {
$currency = $this->currencyRepository->findByCode((string)($data['transaction_currency_code'] ?? ''));
@@ -134,7 +134,7 @@ class PiggyBankFactory
if (array_key_exists('transaction_currency_id', $data)) {
$currency = $this->currencyRepository->find((int)($data['transaction_currency_id'] ?? 0));
}
$currency ??= $defaultCurrency;
$currency ??= $primaryCurrency;
return $currency;
}

View File

@@ -141,8 +141,8 @@ class MonthReportGenerator implements ReportGeneratorInterface
Log::debug(sprintf('getAuditReport: Call finalAccountBalance with date/time "%s"', $date->toIso8601String()));
$dayBeforeBalance = Steam::finalAccountBalance($account, $date);
$startBalance = $dayBeforeBalance['balance'];
$defaultCurrency = app('amount')->getPrimaryCurrencyByUserGroup($account->user->userGroup);
$currency = $accountRepository->getAccountCurrency($account) ?? $defaultCurrency;
$primaryCurrency = app('amount')->getPrimaryCurrencyByUserGroup($account->user->userGroup);
$currency = $accountRepository->getAccountCurrency($account) ?? $primaryCurrency;
foreach ($journals as $index => $journal) {
$journals[$index]['balance_before'] = $startBalance;

View File

@@ -32,7 +32,6 @@ use FireflyIII\Models\TransactionGroup;
use FireflyIII\Models\TransactionJournal;
use FireflyIII\Models\Webhook;
use FireflyIII\Models\WebhookMessage;
use FireflyIII\Support\Facades\Amount;
use FireflyIII\Support\JsonApi\Enrichments\AccountEnrichment;
use FireflyIII\Transformers\AccountTransformer;
use FireflyIII\Transformers\TransactionGroupTransformer;
@@ -176,8 +175,8 @@ class StandardMessageGenerator implements MessageGeneratorInterface
/** @var TransactionGroup $model */
$accounts = $this->collectAccounts($model);
$enrichment = new AccountEnrichment();
$enrichment->setDate(null);
$enrichment->setUser($model->user);
$enrichment->setPrimaryCurrency(Amount::getPrimaryCurrencyByUserGroup($model->userGroup));
$accounts = $enrichment->enrich($accounts);
foreach ($accounts as $account) {
$transformer = new AccountTransformer();

View File

@@ -24,7 +24,7 @@ declare(strict_types=1);
namespace FireflyIII\Handlers\Events;
use FireflyIII\Events\Preferences\UserGroupChangedDefaultCurrency;
use FireflyIII\Events\Preferences\UserGroupChangedPrimaryCurrency;
use FireflyIII\Models\Budget;
use FireflyIII\Models\PiggyBank;
use FireflyIII\Models\UserGroup;
@@ -38,7 +38,7 @@ use Illuminate\Support\Facades\Log;
class PreferencesEventHandler
{
public function resetPrimaryCurrencyAmounts(UserGroupChangedDefaultCurrency $event): void
public function resetPrimaryCurrencyAmounts(UserGroupChangedPrimaryCurrency $event): void
{
// Reset the primary currency amounts for all objects that have it.
Log::debug('Resetting primary currency amounts for all objects.');

View File

@@ -77,7 +77,7 @@ class CreateController extends Controller
$periods[$current] = (string) trans('firefly.repeat_freq_'.$current);
}
$subTitle = (string) trans('firefly.create_new_bill');
$defaultCurrency = $this->primaryCurrency;
$primaryCurrency = $this->primaryCurrency;
// put previous url in session if not redirect from store (not "create another").
if (true !== session('bills.create.fromStore')) {
@@ -85,7 +85,7 @@ class CreateController extends Controller
}
$request->session()->forget('bills.create.fromStore');
return view('bills.create', compact('periods', 'subTitle', 'defaultCurrency'));
return view('bills.create', compact('periods', 'subTitle', 'primaryCurrency'));
}
/**

View File

@@ -88,7 +88,7 @@ class EditController extends Controller
$bill->amount_min = app('steam')->bcround($bill->amount_min, $bill->transactionCurrency->decimal_places);
$bill->amount_max = app('steam')->bcround($bill->amount_max, $bill->transactionCurrency->decimal_places);
$rules = $this->repository->getRulesForBill($bill);
$defaultCurrency = $this->primaryCurrency;
$primaryCurrency = $this->primaryCurrency;
// code to handle active-checkboxes
$hasOldInput = null !== $request->old('_token');
@@ -105,7 +105,7 @@ class EditController extends Controller
$request->session()->flash('preFilled', $preFilled);
$request->session()->forget('bills.edit.fromUpdate');
return view('bills.edit', compact('subTitle', 'periods', 'rules', 'bill', 'defaultCurrency', 'preFilled'));
return view('bills.edit', compact('subTitle', 'periods', 'rules', 'bill', 'primaryCurrency', 'preFilled'));
}
/**

View File

@@ -91,8 +91,6 @@ class IndexController extends Controller
$admin = auth()->user();
$enrichment = new SubscriptionEnrichment();
$enrichment->setUser($admin);
$enrichment->setConvertToPrimary($this->convertToPrimary);
$enrichment->setPrimaryCurrency($this->primaryCurrency);
$enrichment->setStart($tempStart);
$enrichment->setEnd($end);
$collection = $enrichment->enrich($collection);
@@ -101,7 +99,7 @@ class IndexController extends Controller
$parameters->set('start', $tempStart);
$parameters->set('end', $end);
$parameters->set('convertToPrimary', $this->convertToPrimary);
$parameters->set('defaultCurrency', $this->primaryCurrency);
$parameters->set('primaryCurrency', $this->primaryCurrency);
/** @var BillTransformer $transformer */
$transformer = app(BillTransformer::class);

View File

@@ -149,10 +149,10 @@ class ShowController extends Controller
$admin = auth()->user();
$enrichment = new SubscriptionEnrichment();
$enrichment->setUser($admin);
$enrichment->setConvertToPrimary($this->convertToPrimary);
$enrichment->setPrimaryCurrency($this->primaryCurrency);
$enrichment->setStart($start);
$enrichment->setEnd($end);
/** @var Bill $bill */
$bill = $enrichment->enrichSingle($bill);
/** @var BillTransformer $transformer */

View File

@@ -95,6 +95,7 @@ class EditController extends Controller
$preFilled = [
'active' => $hasOldInput ? (bool) $request->old('active') : $budget->active,
'auto_budget_currency_id' => $hasOldInput ? (int) $request->old('auto_budget_currency_id') : $this->primaryCurrency->id,
'notes' => $this->repository->getNoteText($budget),
];
if ($autoBudget instanceof AutoBudget) {
$amount = $hasOldInput ? $request->old('auto_budget_amount') : $autoBudget->amount;

View File

@@ -136,7 +136,7 @@ class IndexController extends Controller
// get all inactive budgets, and simply list them:
$inactive = $this->repository->getInactiveBudgets();
$defaultCurrency = $this->primaryCurrency;
$primaryCurrency = $this->primaryCurrency;
return view(
'budgets.index',
@@ -149,7 +149,7 @@ class IndexController extends Controller
'budgets',
'currencies',
'periodTitle',
'defaultCurrency',
'primaryCurrency',
'activeDaysPassed',
'activeDaysLeft',
'inactive',
@@ -194,7 +194,7 @@ class IndexController extends Controller
return $availableBudgets;
}
private function getAllBudgets(Carbon $start, Carbon $end, Collection $currencies, TransactionCurrency $defaultCurrency): array
private function getAllBudgets(Carbon $start, Carbon $end, Collection $currencies, TransactionCurrency $primaryCurrency): array
{
// get all budgets, and paginate them into $budgets.
$collection = $this->repository->getActiveBudgets();
@@ -216,7 +216,7 @@ class IndexController extends Controller
/** @var BudgetLimit $limit */
foreach ($budgetLimits as $limit) {
Log::debug(sprintf('Working on budget limit #%d', $limit->id));
$currency = $limit->transactionCurrency ?? $defaultCurrency;
$currency = $limit->transactionCurrency ?? $primaryCurrency;
$amount = app('steam')->bcround($limit->amount, $currency->decimal_places);
$array['budgeted'][] = [
'id' => $limit->id,

View File

@@ -148,7 +148,7 @@ class IndexController extends Controller
// enrich each account.
$enrichment = new AccountEnrichment();
$enrichment->setUser(auth()->user());
$enrichment->setPrimaryCurrency($this->primaryCurrency);
$enrichment->setDate($end);
$return = [];
/** @var PiggyBank $piggy */

View File

@@ -26,7 +26,7 @@ namespace FireflyIII\Http\Controllers;
use JsonException;
use Carbon\Carbon;
use FireflyIII\Enums\AccountTypeEnum;
use FireflyIII\Events\Preferences\UserGroupChangedDefaultCurrency;
use FireflyIII\Events\Preferences\UserGroupChangedPrimaryCurrency;
use FireflyIII\Events\Test\UserTestNotificationChannel;
use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Http\Requests\PreferencesRequest;
@@ -271,7 +271,7 @@ class PreferencesController extends Controller
// set to true!
Log::debug('User sets convertToPrimary to true.');
Preferences::set('convert_to_primary', $convertToPrimary);
event(new UserGroupChangedDefaultCurrency(auth()->user()->userGroup));
event(new UserGroupChangedPrimaryCurrency(auth()->user()->userGroup));
}
Preferences::set('convert_to_primary', $convertToPrimary);

View File

@@ -84,7 +84,7 @@ class CreateController extends Controller
{
$budgets = app('expandedform')->makeSelectListWithEmpty($this->budgetRepos->getActiveBudgets());
$bills = app('expandedform')->makeSelectListWithEmpty($this->billRepository->getActiveBills());
$defaultCurrency = $this->primaryCurrency;
$primaryCurrency = $this->primaryCurrency;
$tomorrow = today(config('app.timezone'));
$oldRepetitionType = $request->old('repetition_type');
$tomorrow->addDay();
@@ -116,7 +116,7 @@ class CreateController extends Controller
return view(
'recurring.create',
compact('tomorrow', 'oldRepetitionType', 'bills', 'weekendResponses', 'preFilled', 'repetitionEnds', 'defaultCurrency', 'budgets')
compact('tomorrow', 'oldRepetitionType', 'bills', 'weekendResponses', 'preFilled', 'repetitionEnds', 'primaryCurrency', 'budgets')
);
}
@@ -129,7 +129,7 @@ class CreateController extends Controller
{
$budgets = app('expandedform')->makeSelectListWithEmpty($this->budgetRepos->getActiveBudgets());
$bills = app('expandedform')->makeSelectListWithEmpty($this->billRepository->getActiveBills());
$defaultCurrency = $this->primaryCurrency;
$primaryCurrency = $this->primaryCurrency;
$tomorrow = today(config('app.timezone'));
$oldRepetitionType = $request->old('repetition_type');
$tomorrow->addDay();
@@ -210,7 +210,7 @@ class CreateController extends Controller
return view(
'recurring.create',
compact('tomorrow', 'oldRepetitionType', 'bills', 'weekendResponses', 'preFilled', 'repetitionEnds', 'defaultCurrency', 'budgets')
compact('tomorrow', 'oldRepetitionType', 'bills', 'weekendResponses', 'preFilled', 'repetitionEnds', 'primaryCurrency', 'budgets')
);
}

View File

@@ -117,7 +117,7 @@ class CreateController extends Controller
$optionalFields = app('preferences')->get('transaction_journal_optional_fields', [])->data;
$allowedOpposingTypes = config('firefly.allowed_opposing_types');
$accountToTypes = config('firefly.account_to_transaction');
$defaultCurrency = $this->primaryCurrency;
$primaryCurrency = $this->primaryCurrency;
$previousUrl = $this->rememberPreviousUrl('transactions.create.url');
$parts = parse_url((string) $previousUrl);
$search = sprintf('?%s', $parts['query'] ?? '');
@@ -156,7 +156,7 @@ class CreateController extends Controller
'objectType',
'optionalDateFields',
'subTitle',
'defaultCurrency',
'primaryCurrency',
'previousUrl',
'optionalFields',
'preFilled',

View File

@@ -84,7 +84,7 @@ class EditController extends Controller
$title = $transactionGroup->transactionJournals()->count() > 1 ? $transactionGroup->title : $transactionGroup->transactionJournals()->first()->description;
$subTitle = (string) trans('firefly.edit_transaction_title', ['description' => $title]);
$subTitleIcon = 'fa-plus';
$defaultCurrency = $this->primaryCurrency;
$primaryCurrency = $this->primaryCurrency;
$cash = $repository->getCashAccount();
$previousUrl = $this->rememberPreviousUrl('transactions.edit.url');
$parts = parse_url((string) $previousUrl);
@@ -130,7 +130,7 @@ class EditController extends Controller
'transactionGroup',
'allowedOpposingTypes',
'accountToTypes',
'defaultCurrency',
'primaryCurrency',
'previousUrl'
)
);

View File

@@ -120,7 +120,7 @@ class Range
// save some formats:
$monthAndDayFormat = (string) trans('config.month_and_day_js', [], $locale);
$dateTimeFormat = (string) trans('config.date_time_js', [], $locale);
$defaultCurrency = Amount::getPrimaryCurrency();
$primaryCurrency = Amount::getPrimaryCurrency();
// also format for moment JS:
$madMomentJS = (string) trans('config.month_and_day_moment_js', [], $locale);
@@ -128,7 +128,7 @@ class Range
app('view')->share('madMomentJS', $madMomentJS);
app('view')->share('monthAndDayFormat', $monthAndDayFormat);
app('view')->share('dateTimeFormat', $dateTimeFormat);
app('view')->share('defaultCurrency', $defaultCurrency);
app('view')->share('primaryCurrency', $primaryCurrency);
}
/**

View File

@@ -47,12 +47,13 @@ class BudgetFormUpdateRequest extends FormRequest
public function getBudgetData(): array
{
return [
'name' => $this->convertString('name'),
'active' => $this->boolean('active'),
'auto_budget_type' => $this->convertInteger('auto_budget_type'),
'currency_id' => $this->convertInteger('auto_budget_currency_id'),
'auto_budget_amount' => $this->convertString('auto_budget_amount'),
'auto_budget_period' => $this->convertString('auto_budget_period'),
'name' => $this->convertString('name'),
'active' => $this->boolean('active'),
'auto_budget_type' => $this->convertInteger('auto_budget_type'),
'currency_id' => $this->convertInteger('auto_budget_currency_id'),
'auto_budget_amount' => $this->convertString('auto_budget_amount'),
'auto_budget_period' => $this->convertString('auto_budget_period'),
'notes' => $this->stringWithNewlines('notes'),
];
}

View File

@@ -35,7 +35,7 @@ use FireflyIII\Events\Model\PiggyBank\ChangedName;
use FireflyIII\Events\Model\Rule\RuleActionFailedOnArray;
use FireflyIII\Events\Model\Rule\RuleActionFailedOnObject;
use FireflyIII\Events\NewVersionAvailable;
use FireflyIII\Events\Preferences\UserGroupChangedDefaultCurrency;
use FireflyIII\Events\Preferences\UserGroupChangedPrimaryCurrency;
use FireflyIII\Events\RegisteredUser;
use FireflyIII\Events\RequestedNewPassword;
use FireflyIII\Events\RequestedReportOnJournals;
@@ -257,7 +257,7 @@ class EventServiceProvider extends ServiceProvider
'FireflyIII\Handlers\Events\Security\MFAHandler@sendMFAFailedAttemptsMail',
],
// preferences
UserGroupChangedDefaultCurrency::class => [
UserGroupChangedPrimaryCurrency::class => [
'FireflyIII\Handlers\Events\PreferencesEventHandler@resetPrimaryCurrencyAmounts',
],
];

View File

@@ -32,6 +32,7 @@ use FireflyIII\Support\Report\Summarizer\TransactionSummarizer;
use FireflyIII\Support\Repositories\UserGroup\UserGroupInterface;
use FireflyIII\Support\Repositories\UserGroup\UserGroupTrait;
use Illuminate\Support\Collection;
use Override;
/**
* Class NoBudgetRepository
@@ -98,4 +99,23 @@ class NoBudgetRepository implements NoBudgetRepositoryInterface, UserGroupInterf
return $summarizer->groupByCurrencyId($journals);
}
#[Override]
public function collectExpenses(Carbon $start, Carbon $end, ?Collection $accounts = null, ?TransactionCurrency $currency = null): array
{
/** @var GroupCollectorInterface $collector */
$collector = app(GroupCollectorInterface::class);
$collector->setUser($this->user)->setRange($start, $end)->setTypes([TransactionTypeEnum::WITHDRAWAL->value]);
if ($accounts instanceof Collection && $accounts->count() > 0) {
$collector->setAccounts($accounts);
}
if ($currency instanceof TransactionCurrency) {
$collector->setCurrency($currency);
}
$collector->withoutBudget();
$collector->withBudgetInformation();
return $collector->getExtractedJournals();
}
}

View File

@@ -49,4 +49,6 @@ interface NoBudgetRepositoryInterface
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 collectExpenses(Carbon $start, Carbon $end, ?Collection $accounts = null, ?TransactionCurrency $currency = null): array;
}

View File

@@ -39,6 +39,7 @@ use FireflyIII\Support\Repositories\UserGroup\UserGroupInterface;
use FireflyIII\Support\Repositories\UserGroup\UserGroupTrait;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Log;
use Override;
/**
* Class OperationsRepository
@@ -57,17 +58,17 @@ class OperationsRepository implements OperationsRepositoryInterface, UserGroupIn
$total = '0';
$count = 0;
foreach ($budget->budgetlimits as $limit) {
$diff = (int)$limit->start_date->diffInDays($limit->end_date, true);
$diff = (int) $limit->start_date->diffInDays($limit->end_date, true);
$diff = 0 === $diff ? 1 : $diff;
$amount = $limit->amount;
$perDay = bcdiv((string)$amount, (string)$diff);
$perDay = bcdiv((string) $amount, (string) $diff);
$total = bcadd($total, $perDay);
++$count;
app('log')->debug(sprintf('Found %d budget limits. Per day is %s, total is %s', $count, $perDay, $total));
}
$avg = $total;
if ($count > 0) {
$avg = bcdiv($total, (string)$count);
$avg = bcdiv($total, (string) $count);
}
app('log')->debug(sprintf('%s / %d = %s = average.', $total, $count, $avg));
@@ -95,9 +96,9 @@ class OperationsRepository implements OperationsRepositoryInterface, UserGroupIn
/** @var array $journal */
foreach ($journals as $journal) {
// prep data array for currency:
$budgetId = (int)$journal['budget_id'];
$budgetId = (int) $journal['budget_id'];
$budgetName = $journal['budget_name'];
$currencyId = (int)$journal['currency_id'];
$currencyId = (int) $journal['currency_id'];
$key = sprintf('%d-%d', $budgetId, $currencyId);
$data[$key] ??= [
@@ -112,7 +113,7 @@ class OperationsRepository implements OperationsRepositoryInterface, UserGroupIn
'entries' => [],
];
$date = $journal['date']->format($carbonFormat);
$data[$key]['entries'][$date] = bcadd($data[$key]['entries'][$date] ?? '0', (string)$journal['amount']);
$data[$key]['entries'][$date] = bcadd($data[$key]['entries'][$date] ?? '0', (string) $journal['amount']);
}
return $data;
@@ -156,7 +157,7 @@ class OperationsRepository implements OperationsRepositoryInterface, UserGroupIn
foreach ($journals as $journal) {
$amount = app('steam')->negative($journal['amount']);
$journalCurrencyId = (int)$journal['currency_id'];
$journalCurrencyId = (int) $journal['currency_id'];
if (false === $convertToPrimary) {
$currencyId = $journalCurrencyId;
$currencyName = $journal['currency_name'];
@@ -169,8 +170,8 @@ class OperationsRepository implements OperationsRepositoryInterface, UserGroupIn
$amount = $converter->convert($currencies[$journalCurrencyId], $primaryCurrency, $journal['date'], $amount);
}
$budgetId = (int)$journal['budget_id'];
$budgetName = (string)$journal['budget_name'];
$budgetId = (int) $journal['budget_id'];
$budgetName = (string) $journal['budget_name'];
// catch "no budget" entries.
if (0 === $budgetId) {
@@ -196,7 +197,7 @@ class OperationsRepository implements OperationsRepositoryInterface, UserGroupIn
// add journal to array:
// only a subset of the fields.
$journalId = (int)$journal['transaction_journal_id'];
$journalId = (int) $journal['transaction_journal_id'];
$array[$currencyId]['budgets'][$budgetId]['transaction_journals'][$journalId] = [
'amount' => $amount,
'destination_account_id' => $journal['destination_account_id'],
@@ -282,4 +283,77 @@ class OperationsRepository implements OperationsRepositoryInterface, UserGroupIn
return $summarizer->groupByCurrencyId($journals, 'negative', false);
}
public function sumCollectedExpenses(array $expenses, Carbon $start, Carbon $end, TransactionCurrency $transactionCurrency, bool $convertToPrimary = false): array
{
Log::debug(sprintf('Start of %s.', __METHOD__));
$summarizer = new TransactionSummarizer($this->user);
$summarizer->setConvertToPrimary($convertToPrimary);
// filter $journals by range AND currency if it is present.
$expenses = array_filter($expenses, static function (array $expense) use ($start, $end, $transactionCurrency): bool {
return $expense['date']->between($start, $end) && $expense['currency_id'] === $transactionCurrency->id;
});
return $summarizer->groupByCurrencyId($expenses, 'negative', false);
}
public function sumCollectedExpensesByBudget(array $expenses, Budget $budget, bool $convertToPrimary = false): array
{
Log::debug(sprintf('Start of %s.', __METHOD__));
$summarizer = new TransactionSummarizer($this->user);
$summarizer->setConvertToPrimary($convertToPrimary);
// filter $journals by range AND currency if it is present.
$expenses = array_filter($expenses, static function (array $expense) use ($budget): bool {
return $expense['budget_id'] === $budget->id;
});
return $summarizer->groupByCurrencyId($expenses, 'negative', false);
}
#[Override]
public function collectExpenses(Carbon $start, Carbon $end, ?Collection $accounts = null, ?Collection $budgets = null, ?TransactionCurrency $currency = null): array
{
Log::debug(sprintf('Start of %s(date, date, array, array, "%s").', __METHOD__, $currency?->code));
// 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.
// 2024-12-24 disable the exclusion for now.
$repository = app(AccountRepositoryInterface::class);
$repository->setUser($this->user);
$subset = $repository->getAccountsByType(config('firefly.valid_liabilities'));
$selection = new Collection();
/** @var Account $account */
foreach ($subset as $account) {
if ('credit' === $repository->getMetaValue($account, 'liability_direction')) {
$selection->push($account);
}
}
/** @var GroupCollectorInterface $collector */
$collector = app(GroupCollectorInterface::class);
$collector->setUser($this->user)
->setRange($start, $end)
// ->excludeDestinationAccounts($selection)
->setTypes([TransactionTypeEnum::WITHDRAWAL->value])
;
if ($accounts instanceof Collection) {
$collector->setAccounts($accounts);
}
if (!$budgets instanceof Collection) {
$budgets = $this->getBudgets();
}
if ($currency instanceof TransactionCurrency) {
Log::debug(sprintf('Limit to normal currency %s', $currency->code));
$collector->setNormalCurrency($currency);
}
if ($budgets->count() > 0) {
$collector->setBudgets($budgets);
}
return $collector->getExtractedJournals();
}
}

View File

@@ -24,8 +24,8 @@ declare(strict_types=1);
namespace FireflyIII\Repositories\Budget;
use Deprecated;
use Carbon\Carbon;
use Deprecated;
use FireflyIII\Enums\UserRoleEnum;
use FireflyIII\Models\Budget;
use FireflyIII\Models\TransactionCurrency;
@@ -73,4 +73,10 @@ interface OperationsRepositoryInterface
?TransactionCurrency $currency = null,
bool $convertToPrimary = false
): array;
public function sumCollectedExpenses(array $expenses, Carbon $start, Carbon $end, TransactionCurrency $transactionCurrency, bool $convertToPrimary = false): array;
public function sumCollectedExpensesByBudget(array $expenses, Budget $budget, bool $convertToPrimary = false): array;
public function collectExpenses(Carbon $start, Carbon $end, ?Collection $accounts = null, ?Collection $budgets = null, ?TransactionCurrency $currency = null): array;
}

View File

@@ -24,7 +24,7 @@ declare(strict_types=1);
namespace FireflyIII\Repositories\Currency;
use Carbon\Carbon;
use FireflyIII\Events\Preferences\UserGroupChangedDefaultCurrency;
use FireflyIII\Events\Preferences\UserGroupChangedPrimaryCurrency;
use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Factory\TransactionCurrencyFactory;
use FireflyIII\Models\AccountMeta;
@@ -438,7 +438,7 @@ class CurrencyRepository implements CurrencyRepositoryInterface, UserGroupInterf
if ($current->id !== $currency->id) {
Log::debug('Trigger on a different default currency.');
// clear all primary currency amounts through an event.
event(new UserGroupChangedDefaultCurrency($this->userGroup));
event(new UserGroupChangedPrimaryCurrency($this->userGroup));
}
}
}

View File

@@ -131,7 +131,7 @@ class PiggyBankRepository implements PiggyBankRepositoryInterface, UserGroupInte
/**
* Get current amount saved in piggy bank.
*/
public function getCurrentPrimaryAmount(PiggyBank $piggyBank, ?Account $account = null): string
public function getCurrentPrimaryCurrencyAmount(PiggyBank $piggyBank, ?Account $account = null): string
{
$sum = '0';
foreach ($piggyBank->accounts as $current) {

View File

@@ -80,7 +80,7 @@ interface PiggyBankRepositoryInterface
*/
public function getCurrentAmount(PiggyBank $piggyBank, ?Account $account = null): string;
public function getCurrentPrimaryAmount(PiggyBank $piggyBank, ?Account $account = null): string;
public function getCurrentPrimaryCurrencyAmount(PiggyBank $piggyBank, ?Account $account = null): string;
/**
* Get all events.

View File

@@ -24,7 +24,7 @@ declare(strict_types=1);
namespace FireflyIII\Repositories\UserGroups\Currency;
use FireflyIII\Events\Preferences\UserGroupChangedDefaultCurrency;
use FireflyIII\Events\Preferences\UserGroupChangedPrimaryCurrency;
use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Factory\TransactionCurrencyFactory;
use FireflyIII\Models\AccountMeta;
@@ -386,7 +386,7 @@ class CurrencyRepository implements CurrencyRepositoryInterface
if ($current->id !== $currency->id) {
Log::debug('Trigger on a different default currency.');
// clear all primary currency amounts through an event.
event(new UserGroupChangedDefaultCurrency($this->userGroup));
event(new UserGroupChangedPrimaryCurrency($this->userGroup));
}
}
}

View File

@@ -44,7 +44,7 @@ class FrontpageChartGenerator
use AugumentData;
public bool $convertToPrimary = false;
public TransactionCurrency $defaultCurrency;
public TransactionCurrency $primaryCurrency;
private AccountRepositoryInterface $accountRepos;
private array $currencies;
private NoCategoryRepositoryInterface $noCatRepos;

View File

@@ -61,7 +61,7 @@ class CurrencyForm
$classes = $this->getHolderClasses($name);
$value = $this->fillFieldValue($name, $value);
$options['step'] = 'any';
$defaultCurrency = $options['currency'] ?? app('amount')->getPrimaryCurrency();
$primaryCurrency = $options['currency'] ?? app('amount')->getPrimaryCurrency();
/** @var Collection $currencies */
$currencies = app('amount')->getCurrencies();
@@ -72,15 +72,15 @@ class CurrencyForm
$preFilled = [];
}
$key = 'amount_currency_id_'.$name;
$sentCurrencyId = array_key_exists($key, $preFilled) ? (int) $preFilled[$key] : $defaultCurrency->id;
$sentCurrencyId = array_key_exists($key, $preFilled) ? (int) $preFilled[$key] : $primaryCurrency->id;
app('log')->debug(sprintf('Sent currency ID is %d', $sentCurrencyId));
// find this currency in set of currencies:
foreach ($currencies as $currency) {
if ($currency->id === $sentCurrencyId) {
$defaultCurrency = $currency;
app('log')->debug(sprintf('default currency is now %s', $defaultCurrency->code));
$primaryCurrency = $currency;
app('log')->debug(sprintf('default currency is now %s', $primaryCurrency->code));
break;
}
@@ -88,11 +88,11 @@ class CurrencyForm
// make sure value is formatted nicely:
if (null !== $value && '' !== $value) {
$value = app('steam')->bcround($value, $defaultCurrency->decimal_places);
$value = app('steam')->bcround($value, $primaryCurrency->decimal_places);
}
try {
$html = view('form.'.$view, compact('defaultCurrency', 'currencies', 'classes', 'name', 'label', 'value', 'options'))->render();
$html = view('form.'.$view, compact('primaryCurrency', 'currencies', 'classes', 'name', 'label', 'value', 'options'))->render();
} catch (Throwable $e) {
app('log')->debug(sprintf('Could not render currencyField(): %s', $e->getMessage()));
$html = 'Could not render currencyField.';
@@ -129,7 +129,7 @@ class CurrencyForm
$classes = $this->getHolderClasses($name);
$value = $this->fillFieldValue($name, $value);
$options['step'] = 'any';
$defaultCurrency = $options['currency'] ?? app('amount')->getPrimaryCurrency();
$primaryCurrency = $options['currency'] ?? app('amount')->getPrimaryCurrency();
/** @var Collection $currencies */
$currencies = app('amount')->getAllCurrencies();
@@ -141,15 +141,15 @@ class CurrencyForm
$preFilled = [];
}
$key = 'amount_currency_id_'.$name;
$sentCurrencyId = array_key_exists($key, $preFilled) ? (int) $preFilled[$key] : $defaultCurrency->id;
$sentCurrencyId = array_key_exists($key, $preFilled) ? (int) $preFilled[$key] : $primaryCurrency->id;
app('log')->debug(sprintf('Sent currency ID is %d', $sentCurrencyId));
// find this currency in set of currencies:
foreach ($currencies as $currency) {
if ($currency->id === $sentCurrencyId) {
$defaultCurrency = $currency;
app('log')->debug(sprintf('default currency is now %s', $defaultCurrency->code));
$primaryCurrency = $currency;
app('log')->debug(sprintf('default currency is now %s', $primaryCurrency->code));
break;
}
@@ -157,11 +157,11 @@ class CurrencyForm
// make sure value is formatted nicely:
if (null !== $value && '' !== $value) {
$value = app('steam')->bcround($value, $defaultCurrency->decimal_places);
$value = app('steam')->bcround($value, $primaryCurrency->decimal_places);
}
try {
$html = view('form.'.$view, compact('defaultCurrency', 'currencies', 'classes', 'name', 'label', 'value', 'options'))->render();
$html = view('form.'.$view, compact('primaryCurrency', 'currencies', 'classes', 'name', 'label', 'value', 'options'))->render();
} catch (Throwable $e) {
app('log')->debug(sprintf('Could not render currencyField(): %s', $e->getMessage()));
$html = 'Could not render currencyField.';

View File

@@ -202,7 +202,7 @@ trait AugumentData
$currency = $entry->transactionCurrency;
if ($this->convertToPrimary) {
// the sumExpenses method already handles this.
$currency = $this->defaultCurrency;
$currency = $this->primaryCurrency;
}
// clone because these objects change each other.

View File

@@ -24,6 +24,7 @@ declare(strict_types=1);
namespace FireflyIII\Support\JsonApi\Enrichments;
use Carbon\Carbon;
use FireflyIII\Enums\TransactionTypeEnum;
use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Helpers\Collector\GroupCollectorInterface;
@@ -34,7 +35,9 @@ use FireflyIII\Models\Location;
use FireflyIII\Models\Note;
use FireflyIII\Models\TransactionCurrency;
use FireflyIII\Models\UserGroup;
use FireflyIII\Support\Facades\Amount;
use FireflyIII\Support\Facades\Steam;
use FireflyIII\Support\Http\Api\ExchangeRateConverter;
use FireflyIII\User;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Collection;
@@ -61,22 +64,25 @@ class AccountEnrichment implements EnrichmentInterface
private User $user;
private UserGroup $userGroup;
private array $lastActivities;
private ?Carbon $date = null;
private bool $convertToPrimary = false;
/**
* TODO The account enricher must do conversion from and to the primary currency.
*/
public function __construct()
{
$this->accountIds = [];
$this->openingBalances = [];
$this->currencies = [];
$this->accountTypeIds = [];
$this->accountTypes = [];
$this->meta = [];
$this->notes = [];
$this->lastActivities = [];
$this->locations = [];
// $this->repository = app(AccountRepositoryInterface::class);
// $this->currencyRepository = app(CurrencyRepositoryInterface::class);
// $this->start = null;
// $this->end = null;
$this->accountIds = [];
$this->openingBalances = [];
$this->currencies = [];
$this->accountTypeIds = [];
$this->accountTypes = [];
$this->meta = [];
$this->notes = [];
$this->lastActivities = [];
$this->locations = [];
$this->primaryCurrency = Amount::getPrimaryCurrency();
$this->convertToPrimary = Amount::convertToPrimary();
}
#[Override]
@@ -106,6 +112,7 @@ class AccountEnrichment implements EnrichmentInterface
$this->collectLastActivities();
$this->collectLocations();
$this->collectOpeningBalances();
$this->collectBalances();
$this->appendCollectedData();
return $this->collection;
@@ -227,32 +234,30 @@ class AccountEnrichment implements EnrichmentInterface
private function appendCollectedData(): void
{
$accountTypes = $this->accountTypes;
$meta = $this->meta;
$currencies = $this->currencies;
$notes = $this->notes;
$openingBalances = $this->openingBalances;
$locations = $this->locations;
$lastActivities = $this->lastActivities;
$this->collection = $this->collection->map(function (Account $item) use ($accountTypes, $meta, $currencies, $notes, $openingBalances, $locations, $lastActivities) {
$item->full_account_type = $accountTypes[(int) $item->account_type_id] ?? null;
$this->collection = $this->collection->map(function (Account $item) use ($notes, $openingBalances, $locations, $lastActivities) {
$item->full_account_type = $this->accountTypes[(int) $item->account_type_id] ?? null;
$accountMeta = [
'currency' => null,
'location' => [
'currency' => null,
'location' => [
'latitude' => null,
'longitude' => null,
'zoom_level' => null,
],
'opening_balance_date' => null,
];
if (array_key_exists((int) $item->id, $meta)) {
foreach ($meta[(int) $item->id] as $name => $value) {
if (array_key_exists((int) $item->id, $this->meta)) {
foreach ($this->meta[(int) $item->id] as $name => $value) {
$accountMeta[$name] = $value;
}
}
// also add currency, if present.
if (array_key_exists('currency_id', $accountMeta)) {
$currencyId = (int) $accountMeta['currency_id'];
$accountMeta['currency'] = $currencies[$currencyId];
$accountMeta['currency'] = $this->currencies[$currencyId];
}
// if notes, add notes.
@@ -265,6 +270,64 @@ class AccountEnrichment implements EnrichmentInterface
$accountMeta['opening_balance_amount'] = $openingBalances[$item->id]['amount'];
}
// add balances
// get currencies:
$currency = $this->primaryCurrency; // assume primary currency
if (null !== $accountMeta['currency']) {
$currency = $accountMeta['currency'];
}
// get the current balance:
$date = $this->getDate();
$finalBalance = Steam::finalAccountBalance($item, $date, $this->primaryCurrency, $this->convertToPrimary);
Log::debug(sprintf('Call finalAccountBalance(%s) with date/time "%s"', var_export($this->convertToPrimary, true), $date->toIso8601String()), $finalBalance);
// collect current balances:
$currentBalance = Steam::bcround($finalBalance[$currency->code] ?? '0', $currency->decimal_places);
$openingBalance = Steam::bcround($accountMeta['opening_balance_amount'] ?? '0', $currency->decimal_places);
$virtualBalance = Steam::bcround($account->virtual_balance ?? '0', $currency->decimal_places);
$debtAmount = $accountMeta['current_debt'] ?? null;
// set some pc_ default values to NULL:
$pcCurrentBalance = null;
$pcOpeningBalance = null;
$pcVirtualBalance = null;
$pcDebtAmount = null;
// convert to primary currency if needed:
if ($this->convertToPrimary && $currency->id !== $this->primaryCurrency->id) {
Log::debug(sprintf('Convert to primary, from %s to %s', $currency->code, $this->primaryCurrency->code));
$converter = new ExchangeRateConverter();
$pcCurrentBalance = $converter->convert($currency, $this->primaryCurrency, $date, $currentBalance);
$pcOpeningBalance = $converter->convert($currency, $this->primaryCurrency, $date, $openingBalance);
$pcVirtualBalance = $converter->convert($currency, $this->primaryCurrency, $date, $virtualBalance);
$pcDebtAmount = null === $debtAmount ? null : $converter->convert($currency, $this->primaryCurrency, $date, $debtAmount);
}
if ($this->convertToPrimary && $currency->id === $this->primaryCurrency->id) {
$pcCurrentBalance = $currentBalance;
$pcOpeningBalance = $openingBalance;
$pcVirtualBalance = $virtualBalance;
$pcDebtAmount = $debtAmount;
}
// set opening balance(s) to NULL if the date is null
if (null === $accountMeta['opening_balance_date']) {
$openingBalance = null;
$pcOpeningBalance = null;
}
$accountMeta['balances'] = [
'current_balance' => $currentBalance,
'pc_current_balance' => $pcCurrentBalance,
'opening_balance' => $openingBalance,
'pc_opening_balance' => $pcOpeningBalance,
'virtual_balance' => $virtualBalance,
'pc_virtual_balance' => $pcVirtualBalance,
'debt_amount' => $debtAmount,
'pc_debt_amount' => $pcDebtAmount,
];
// end add balances
// if location, add location:
if (array_key_exists($item->id, $locations)) {
$accountMeta['location'] = $locations[$item->id];
@@ -278,13 +341,24 @@ class AccountEnrichment implements EnrichmentInterface
});
}
public function setPrimaryCurrency(TransactionCurrency $primary): void
{
$this->primaryCurrency = $primary;
}
private function collectLastActivities(): void
{
$this->lastActivities = Steam::getLastActivities($this->accountIds);
}
private function collectBalances(): void {}
public function setDate(?Carbon $date): void
{
$this->date = $date;
}
public function getDate(): Carbon
{
if (null === $this->date) {
return today();
}
return $this->date;
}
}

View File

@@ -0,0 +1,166 @@
<?php
/*
* AvailableBudgetEnrichment.php
* Copyright (c) 2025 james@firefly-iii.org.
*
* This file is part of Firefly III (https://github.com/firefly-iii).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see https://www.gnu.org/licenses/.
*/
declare(strict_types=1);
namespace FireflyIII\Support\JsonApi\Enrichments;
use Carbon\Carbon;
use FireflyIII\Models\AvailableBudget;
use FireflyIII\Models\TransactionCurrency;
use FireflyIII\Models\UserGroup;
use FireflyIII\Repositories\Budget\BudgetRepositoryInterface;
use FireflyIII\Repositories\Budget\NoBudgetRepositoryInterface;
use FireflyIII\Repositories\Budget\OperationsRepositoryInterface;
use FireflyIII\Support\Facades\Amount;
use FireflyIII\User;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Log;
use Override;
class AvailableBudgetEnrichment implements EnrichmentInterface
{
private User $user;
private UserGroup $userGroup;
private TransactionCurrency $primaryCurrency;
private bool $convertToPrimary = false;
private array $ids = [];
private Collection $collection;
private array $spentInBudgets = [];
private array $spentOutsideBudgets = [];
private array $pcSpentInBudgets = [];
private array $pcSpentOutsideBudgets = [];
private readonly NoBudgetRepositoryInterface $noBudgetRepository;
private readonly OperationsRepositoryInterface $opsRepository;
private readonly BudgetRepositoryInterface $repository;
private ?Carbon $start = null;
private ?Carbon $end = null;
public function __construct()
{
$this->primaryCurrency = Amount::getPrimaryCurrency();
$this->convertToPrimary = Amount::convertToPrimary();
$this->noBudgetRepository = app(NoBudgetRepositoryInterface::class);
$this->opsRepository = app(OperationsRepositoryInterface::class);
$this->repository = app(BudgetRepositoryInterface::class);
}
#[Override]
public function enrich(Collection $collection): Collection
{
$this->collection = $collection;
$this->collectIds();
$this->collectSpentInfo();
$this->appendCollectedData();
return $this->collection;
}
#[Override]
public function enrichSingle(array|Model $model): array|Model
{
Log::debug(__METHOD__);
$collection = new Collection([$model]);
$collection = $this->enrich($collection);
return $collection->first();
}
#[Override]
public function setUser(User $user): void
{
$this->user = $user;
$this->setUserGroup($user->userGroup);
}
#[Override]
public function setUserGroup(UserGroup $userGroup): void
{
$this->userGroup = $userGroup;
$this->noBudgetRepository->setUserGroup($userGroup);
$this->opsRepository->setUserGroup($userGroup);
$this->repository->setUserGroup($userGroup);
}
private function collectIds(): void
{
/** @var AvailableBudget $availableBudget */
foreach ($this->collection as $availableBudget) {
$this->ids[] = (int) $availableBudget->id;
}
$this->ids = array_unique($this->ids);
}
private function collectSpentInfo(): void
{
$start = $this->collection->min('start_date');
$end = $this->collection->max('end_date');
$allActive = $this->repository->getActiveBudgets();
$spentInBudgets = $this->opsRepository->collectExpenses($start, $end, null, $allActive, null);
$spentOutsideBudgets = $this->noBudgetRepository->collectExpenses($start, $end, null, null, null);
foreach ($this->collection as $availableBudget) {
$id = (int) $availableBudget->id;
$filteredSpentInBudgets = $this->opsRepository->sumCollectedExpenses($spentInBudgets, $availableBudget->start_date, $availableBudget->end_date, $availableBudget->transactionCurrency, false);
$filteredSpentOutsideBudgets = $this->opsRepository->sumCollectedExpenses($spentOutsideBudgets, $availableBudget->start_date, $availableBudget->end_date, $availableBudget->transactionCurrency, false);
$this->spentInBudgets[$id] = array_values($filteredSpentInBudgets);
$this->spentOutsideBudgets[$id] = array_values($filteredSpentOutsideBudgets);
if (true === $this->convertToPrimary) {
$pcFilteredSpentInBudgets = $this->opsRepository->sumCollectedExpenses($spentInBudgets, $availableBudget->start_date, $availableBudget->end_date, $availableBudget->transactionCurrency, true);
$pcFilteredSpentOutsideBudgets = $this->opsRepository->sumCollectedExpenses($spentOutsideBudgets, $availableBudget->start_date, $availableBudget->end_date, $availableBudget->transactionCurrency, true);
$this->pcSpentInBudgets[$id] = array_values($pcFilteredSpentInBudgets);
$this->pcSpentOutsideBudgets[$id] = array_values($pcFilteredSpentOutsideBudgets);
}
// filter arrays on date.
// send them to sumCollection thing.
// save.
}
// first collect, then filter and append.
}
private function appendCollectedData(): void
{
$spentInsideBudgets = $this->spentInBudgets;
$spentOutsideBudgets = $this->spentOutsideBudgets;
$pcSpentInBudgets = $this->pcSpentInBudgets;
$pcSpentOutsideBudgets = $this->pcSpentOutsideBudgets;
$this->collection = $this->collection->map(function (AvailableBudget $item) use ($spentInsideBudgets, $spentOutsideBudgets, $pcSpentInBudgets, $pcSpentOutsideBudgets) {
$id = (int) $item->id;
$meta = [
'spent_in_budgets' => $spentInsideBudgets[$id] ?? [],
'pc_spent_in_budgets' => $pcSpentInBudgets[$id] ?? [],
'spent_outside_budgets' => $spentOutsideBudgets[$id] ?? [],
'pc_spent_outside_budgets' => $pcSpentOutsideBudgets[$id] ?? [],
];
$item->meta = $meta;
return $item;
});
}
}

View File

@@ -0,0 +1,156 @@
<?php
declare(strict_types=1);
namespace FireflyIII\Support\JsonApi\Enrichments;
use Carbon\Carbon;
use FireflyIII\Models\AutoBudget;
use FireflyIII\Models\Budget;
use FireflyIII\Models\Note;
use FireflyIII\Models\TransactionCurrency;
use FireflyIII\Models\UserGroup;
use FireflyIII\Repositories\Budget\OperationsRepositoryInterface;
use FireflyIII\Support\Facades\Amount;
use FireflyIII\User;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Log;
class BudgetEnrichment implements EnrichmentInterface
{
private Collection $collection;
private bool $convertToPrimary = true;
private TransactionCurrency $primaryCurrency;
private User $user;
private UserGroup $userGroup;
private array $ids = [];
private array $notes = [];
private array $autoBudgets = [];
private array $currencies = [];
private ?Carbon $start = null;
private ?Carbon $end = null;
private array $spent = [];
private array $pcSpent = [];
public function __construct()
{
$this->convertToPrimary = Amount::convertToPrimary();
$this->primaryCurrency = Amount::getPrimaryCurrency();
}
public function enrich(Collection $collection): Collection
{
$this->collection = $collection;
$this->collectIds();
$this->collectNotes();
$this->collectAutoBudgets();
$this->collectExpenses();
$this->appendCollectedData();
return $this->collection;
}
public function enrichSingle(array|Model $model): array|Model
{
Log::debug(__METHOD__);
$collection = new Collection([$model]);
$collection = $this->enrich($collection);
return $collection->first();
}
public function setUser(User $user): void
{
$this->user = $user;
$this->setUserGroup($user->userGroup);
}
public function setUserGroup(UserGroup $userGroup): void
{
$this->userGroup = $userGroup;
}
private function collectIds(): void
{
/** @var Budget $budget */
foreach ($this->collection as $budget) {
$this->ids[] = (int)$budget->id;
}
}
private function collectNotes(): void
{
$notes = Note::query()->whereIn('noteable_id', $this->ids)
->whereNotNull('notes.text')
->where('notes.text', '!=', '')
->where('noteable_type', Budget::class)->get(['notes.noteable_id', 'notes.text'])->toArray()
;
foreach ($notes as $note) {
$this->notes[(int)$note['noteable_id']] = (string)$note['text'];
}
Log::debug(sprintf('Enrich with %d note(s)', count($this->notes)));
}
private function appendCollectedData(): void
{
$this->collection = $this->collection->map(function (Budget $item) {
$id = (int)$item->id;
$meta = [
'notes' => $this->notes[$id] ?? null,
'currency' => $this->currencies[$id] ?? null,
'auto_budget' => $this->autoBudgets[$id] ?? null,
'spent' => $this->spent[$id] ?? null,
'pc_spent' => $this->pcSpent[$id] ?? null,
];
$item->meta = $meta;
return $item;
});
}
private function collectAutoBudgets(): void
{
$set = AutoBudget::whereIn('budget_id', $this->ids)->with(['transactionCurrency'])->get();
/** @var AutoBudget $autoBudget */
foreach ($set as $autoBudget) {
$budgetId = (int)$autoBudget->budget_id;
$this->currencies[$budgetId] = $autoBudget->transactionCurrency;
$this->autoBudgets[$budgetId] = [
'type' => (int)$autoBudget->auto_budget_type,
'period' => $autoBudget->period,
'amount' => $autoBudget->amount,
'pc_amount' => $autoBudget->native_amount,
];
}
}
private function collectExpenses(): void
{
if (null !== $this->start && null !== $this->end) {
/** @var OperationsRepositoryInterface $opsRepository */
$opsRepository = app(OperationsRepositoryInterface::class);
$opsRepository->setUser($this->user);
$opsRepository->setUserGroup($this->userGroup);
// $spent = $this->beautify();
// $set = $this->opsRepository->sumExpenses($start, $end, null, new Collection([$budget]))
$expenses = $opsRepository->collectExpenses($this->start, $this->end, null, $this->collection, null);
foreach ($this->collection as $item) {
$id = (int)$item->id;
$this->spent[$id] = array_values($opsRepository->sumCollectedExpensesByBudget($expenses, $item, false));
$this->pcSpent[$id] = array_values($opsRepository->sumCollectedExpensesByBudget($expenses, $item, true));
}
}
}
public function setEnd(?Carbon $end): void
{
$this->end = $end;
}
public function setStart(?Carbon $start): void
{
$this->start = $start;
}
}

View File

@@ -0,0 +1,136 @@
<?php
declare(strict_types=1);
namespace FireflyIII\Support\JsonApi\Enrichments;
use Carbon\Carbon;
use FireflyIII\Models\Budget;
use FireflyIII\Models\BudgetLimit;
use FireflyIII\Models\Note;
use FireflyIII\Models\TransactionCurrency;
use FireflyIII\Models\UserGroup;
use FireflyIII\Repositories\Budget\OperationsRepository;
use FireflyIII\Support\Facades\Amount;
use FireflyIII\User;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Log;
class BudgetLimitEnrichment implements EnrichmentInterface
{
private User $user;
private UserGroup $userGroup;
private Collection $collection;
private array $ids = [];
private array $notes = [];
private Carbon $start;
private Carbon $end;
private Collection $budgets;
private array $expenses = [];
private array $pcExpenses = [];
private bool $convertToPrimary = true;
private TransactionCurrency $primaryCurrency;
public function __construct()
{
$this->convertToPrimary = Amount::convertToPrimary();
$this->primaryCurrency = Amount::getPrimaryCurrency();
}
public function enrich(Collection $collection): Collection
{
$this->collection = $collection;
$this->collectIds();
$this->collectNotes();
$this->collectBudgets();
$this->appendCollectedData();
return $this->collection;
}
public function enrichSingle(array|Model $model): array|Model
{
Log::debug(__METHOD__);
$collection = new Collection()->push($model);
$collection = $this->enrich($collection);
return $collection->first();
}
public function setUser(User $user): void
{
$this->user = $user;
$this->userGroup = $user->userGroup;
}
public function setUserGroup(UserGroup $userGroup): void
{
$this->userGroup = $userGroup;
}
private function collectIds(): void
{
$this->start = $this->collection->min('start_date');
$this->end = $this->collection->max('end_date');
/** @var BudgetLimit $limit */
foreach ($this->collection as $limit) {
$this->ids[] = (int)$limit->id;
}
$this->ids = array_unique($this->ids);
}
private function collectNotes(): void
{
$notes = Note::query()->whereIn('noteable_id', $this->ids)
->whereNotNull('notes.text')
->where('notes.text', '!=', '')
->where('noteable_type', BudgetLimit::class)->get(['notes.noteable_id', 'notes.text'])->toArray()
;
foreach ($notes as $note) {
$this->notes[(int)$note['noteable_id']] = (string)$note['text'];
}
Log::debug(sprintf('Enrich with %d note(s)', count($this->notes)));
}
private function appendCollectedData(): void
{
$this->collection = $this->collection->map(function (BudgetLimit $item) {
$id = (int)$item->id;
$meta = [
'notes' => $this->notes[$id] ?? null,
'spent' => $this->expenses[$id] ?? [],
'pc_spent' => $this->pcExpenses[$id] ?? [],
];
$item->meta = $meta;
return $item;
});
}
private function collectBudgets(): void
{
$budgetIds = $this->collection->pluck('budget_id')->unique()->toArray();
$this->budgets = Budget::whereIn('id', $budgetIds)->get();
$repository = app(OperationsRepository::class);
$repository->setUser($this->user);
$expenses = $repository->collectExpenses($this->start, $this->end, null, $this->budgets, null);
/** @var BudgetLimit $budgetLimit */
foreach ($this->collection as $budgetLimit) {
$id = (int)$budgetLimit->id;
$filteredExpenses = $repository->sumCollectedExpenses($expenses, $budgetLimit->start_date, $budgetLimit->end_date, $budgetLimit->transactionCurrency, false);
$this->expenses[$id] = array_values($filteredExpenses);
if (true === $this->convertToPrimary && $budgetLimit->transactionCurrency->id !== $this->primaryCurrency->id) {
$pcFilteredExpenses = $repository->sumCollectedExpenses($expenses, $budgetLimit->start_date, $budgetLimit->end_date, $budgetLimit->transactionCurrency, true);
$this->pcExpenses[$id] = array_values($pcFilteredExpenses);
}
if (true === $this->convertToPrimary && $budgetLimit->transactionCurrency->id === $this->primaryCurrency->id) {
$this->pcExpenses[$id] = $this->expenses[$id] ?? [];
}
}
}
}

View File

@@ -12,6 +12,7 @@ use FireflyIII\Models\ObjectGroup;
use FireflyIII\Models\TransactionCurrency;
use FireflyIII\Models\TransactionJournal;
use FireflyIII\Models\UserGroup;
use FireflyIII\Support\Facades\Amount;
use FireflyIII\Support\Facades\Steam;
use FireflyIII\Support\Http\Api\ExchangeRateConverter;
use FireflyIII\Support\Models\BillDateCalculator;
@@ -38,6 +39,12 @@ class SubscriptionEnrichment implements EnrichmentInterface
private TransactionCurrency $primaryCurrency;
private BillDateCalculator $calculator;
public function __construct()
{
$this->convertToPrimary = Amount::convertToPrimary();
$this->primaryCurrency = Amount::getPrimaryCurrency();
}
public function enrich(Collection $collection): Collection
{
Log::debug(sprintf('%s(%s item(s))', __METHOD__, $collection->count()));
@@ -54,7 +61,7 @@ class SubscriptionEnrichment implements EnrichmentInterface
$paidDates = $this->paidDates;
$payDates = $this->payDates;
$this->collection = $this->collection->map(function (Bill $item) use ($notes, $objectGroups, $paidDates, $payDates) {
$id = (int)$item->id;
$id = (int) $item->id;
$currency = $item->transactionCurrency;
$nem = $this->getNextExpectedMatch($payDates[$id] ?? []);
@@ -70,10 +77,23 @@ class SubscriptionEnrichment implements EnrichmentInterface
'nem_diff' => $this->getNextExpectedMatchDiff($nem, $payDates[$id] ?? []),
];
$amounts = [
'amount_min' => Steam::bcround($item->amount_min, $currency->decimal_places),
'amount_max' => Steam::bcround($item->amount_max, $currency->decimal_places),
'average' => Steam::bcround(bcdiv(bcadd($item->amount_min, $item->amount_max), '2'), $currency->decimal_places),
'amount_min' => Steam::bcround($item->amount_min, $currency->decimal_places),
'amount_max' => Steam::bcround($item->amount_max, $currency->decimal_places),
'average' => Steam::bcround(bcdiv(bcadd($item->amount_min, $item->amount_max), '2'), $currency->decimal_places),
'pc_amount_min' => null,
'pc_amount_max' => null,
'pc_average' => null,
];
if ($this->convertToPrimary && $currency->id === $this->primaryCurrency->id) {
$amounts['pc_amount_min'] = $amounts['amount_min'];
$amounts['pc_amount_max'] = $amounts['amount_max'];
$amounts['pc_average'] = $amounts['average'];
}
if ($this->convertToPrimary && $currency->id !== $this->primaryCurrency->id) {
$amounts['pc_amount_min'] = Steam::bcround($item->native_amount_min, $this->primaryCurrency->decimal_places);
$amounts['pc_amount_max'] = Steam::bcround($item->native_amount_max, $this->primaryCurrency->decimal_places);
$amounts['pc_average'] = Steam::bcround(bcdiv(bcadd($item->native_amount_min, $item->native_amount_max), '2'), $this->primaryCurrency->decimal_places);
}
// add object group if available
if (array_key_exists($id, $this->mappedObjects)) {
@@ -88,16 +108,6 @@ class SubscriptionEnrichment implements EnrichmentInterface
$meta['notes'] = $notes[$item->id];
}
// Convert amounts to primary currency if needed
if ($this->convertToPrimary && $item->currency_id !== $this->primaryCurrency->id) {
Log::debug('Convert to primary currency');
$converter = new ExchangeRateConverter();
$amounts = [
'amount_min' => Steam::bcround($converter->convert($item->transactionCurrency, $this->primaryCurrency, today(), $item->amount_min), $this->primaryCurrency->decimal_places),
'amount_max' => Steam::bcround($converter->convert($item->transactionCurrency, $this->primaryCurrency, today(), $item->amount_max), $this->primaryCurrency->decimal_places),
];
$amounts['average'] = Steam::bcround(bcdiv(bcadd($amounts['amount_min'], $amounts['amount_max']), '2'), $this->primaryCurrency->decimal_places);
}
$item->amounts = $amounts;
$item->meta = $meta;
@@ -124,7 +134,7 @@ class SubscriptionEnrichment implements EnrichmentInterface
->where('noteable_type', Bill::class)->get(['notes.noteable_id', 'notes.text'])->toArray()
;
foreach ($notes as $note) {
$this->notes[(int)$note['noteable_id']] = (string)$note['text'];
$this->notes[(int) $note['noteable_id']] = (string) $note['text'];
}
Log::debug(sprintf('Enrich with %d note(s)', count($this->notes)));
}
@@ -140,21 +150,11 @@ class SubscriptionEnrichment implements EnrichmentInterface
$this->userGroup = $userGroup;
}
public function setConvertToPrimary(bool $convertToPrimary): void
{
$this->convertToPrimary = $convertToPrimary;
}
public function setPrimaryCurrency(TransactionCurrency $primaryCurrency): void
{
$this->primaryCurrency = $primaryCurrency;
}
private function collectSubscriptionIds(): void
{
/** @var Bill $bill */
foreach ($this->collection as $bill) {
$this->subscriptionIds[] = (int)$bill->id;
$this->subscriptionIds[] = (int) $bill->id;
}
$this->subscriptionIds = array_unique($this->subscriptionIds);
}
@@ -170,14 +170,14 @@ class SubscriptionEnrichment implements EnrichmentInterface
$ids = array_unique($set->pluck('object_group_id')->toArray());
foreach ($set as $entry) {
$this->mappedObjects[(int)$entry->object_groupable_id] = (int)$entry->object_group_id;
$this->mappedObjects[(int) $entry->object_groupable_id] = (int) $entry->object_group_id;
}
$groups = ObjectGroup::whereIn('id', $ids)->get(['id', 'title', 'order'])->toArray();
foreach ($groups as $group) {
$group['id'] = (int)$group['id'];
$group['order'] = (int)$group['order'];
$this->objectGroups[(int)$group['id']] = $group;
$group['id'] = (int) $group['id'];
$group['order'] = (int) $group['order'];
$this->objectGroups[(int) $group['id']] = $group;
}
}
@@ -224,9 +224,11 @@ class SubscriptionEnrichment implements EnrichmentInterface
'transaction_journals.transaction_group_id',
'transactions.transaction_currency_id',
'currency.code AS transaction_currency_code',
'currency.symbol AS transaction_currency_symbol',
'currency.decimal_places AS transaction_currency_decimal_places',
'transactions.foreign_currency_id',
'foreign_currency.code AS foreign_currency_code',
'foreign_currency.symbol AS foreign_currency_symbol',
'foreign_currency.decimal_places AS foreign_currency_decimal_places',
'transactions.amount',
'transactions.foreign_amount',
@@ -252,30 +254,50 @@ class SubscriptionEnrichment implements EnrichmentInterface
});
foreach ($filtered as $entry) {
$array = [
'transaction_group_id' => (string)$entry->transaction_group_id,
'transaction_journal_id' => (string)$entry->id,
'date' => $entry->date->toAtomString(),
'date_object' => $entry->date,
'bill_id' => $entry->bill_id,
'currency_id' => $entry->transaction_currency_id,
'currency_code' => $entry->transaction_currency_code,
'currency_decimal_places' => $entry->transaction_currency_decimal_places,
'amount' => Steam::bcround($entry->amount, $entry->transaction_currency_decimal_places),
'transaction_group_id' => (string) $entry->transaction_group_id,
'transaction_journal_id' => (string) $entry->id,
'date' => $entry->date->toAtomString(),
'date_object' => $entry->date,
'subscription_id' => (string) $entry->bill_id,
'currency_id' => (string) $entry->transaction_currency_id,
'currency_code' => $entry->transaction_currency_code,
'currency_symbol' => $entry->transaction_currency_symbol,
'currency_decimal_places' => $entry->transaction_currency_decimal_places,
'primary_currency_id' => (string) $this->primaryCurrency->id,
'primary_currency_code' => $this->primaryCurrency->code,
'primary_currency_symbol' => $this->primaryCurrency->symbol,
'primary_currency_decimal_places' => $this->primaryCurrency->decimal_places,
'amount' => Steam::bcround($entry->amount, $entry->transaction_currency_decimal_places),
'pc_amount' => null,
'foreign_amount' => null,
'pc_foreign_amount' => null,
];
if (null !== $entry->foreign_amount && null !== $entry->foreign_currency_code) {
$array['foreign_currency_id'] = $entry->foreign_currency_id;
$array['foreign_currency_id'] = (string) $entry->foreign_currency_id;
$array['foreign_currency_code'] = $entry->foreign_currency_code;
$array['foreign_currency_symbol'] = $entry->foreign_currency_symbol;
$array['foreign_currency_decimal_places'] = $entry->foreign_currency_decimal_places;
$array['foreign_amount'] = Steam::bcround($entry->foreign_amount, $entry->foreign_currency_decimal_places);
}
if ($this->convertToPrimary) {
$array['amount'] = $converter->convert($entry->transactionCurrency, $this->primaryCurrency, $entry->date, $entry->amount);
$array['currency_id'] = $this->primaryCurrency->id;
$array['currency_code'] = $this->primaryCurrency->code;
$array['currency_decimal_places'] = $this->primaryCurrency->decimal_places;
// convert to primary, but is already primary.
if ($this->convertToPrimary && (int) $entry->transaction_currency_id === $this->primaryCurrency->id) {
$array['pc_amount'] = $array['amount'];
}
// convert to primary, but is NOT already primary.
if ($this->convertToPrimary && (int) $entry->transaction_currency_id !== $this->primaryCurrency->id) {
$array['pc_amount'] = $converter->convert($entry->transactionCurrency, $this->primaryCurrency, $entry->date, $entry->amount);
}
// convert to primary, but foreign is already primary.
if ($this->convertToPrimary && (int) $entry->foreign_currency_id === $this->primaryCurrency->id) {
$array['pc_foreign_amount'] = $array['foreign_amount'];
}
// convert to primary, but foreign is NOT already primary.
if ($this->convertToPrimary && null !== $entry->foreign_currency_id && (int) $entry->foreign_currency_id !== $this->primaryCurrency->id) {
// TODO this is very database intensive.
$foreignCurrency = TransactionCurrency::find($entry->foreign_currency_id);
$array['pc_foreign_amount'] = $converter->convert($foreignCurrency, $this->primaryCurrency, $entry->date, $entry->amount);
}
$result[] = $array;
}
$this->paidDates[(int) $subscription->id] = $result;
@@ -352,7 +374,7 @@ class SubscriptionEnrichment implements EnrichmentInterface
/** @var Bill $subscription */
foreach ($this->collection as $subscription) {
$id = (int)$subscription->id;
$id = (int) $subscription->id;
$lastPaidDate = $this->getLastPaidDate($paidDates[$id] ?? []);
$payDates = $this->calculator->getPayDates($this->start, $this->end, $subscription->date, $subscription->repeat_freq, $subscription->skip, $lastPaidDate);
$payDatesFormatted = [];

View File

@@ -113,7 +113,7 @@ class Steam
if (!$convertToPrimary) {
if (!$currency instanceof TransactionCurrency) {
Log::debug(sprintf('Unset pc_balance and make defaultCurrency balance the balance for account #%d', $account->id));
Log::debug(sprintf('Unset pc_balance and make primaryCurrency balance the balance for account #%d', $account->id));
$set['balance'] = $set[$primaryCurrency->code] ?? '0';
unset($set[$primaryCurrency->code]);
}
@@ -381,7 +381,7 @@ class Steam
}
// if there is a request to convert, convert to "pc_balance" and use "balance" for whichever amount is in the primary currency.
if ($convertToPrimary) {
$return['primary_balance'] = $this->convertAllBalances($others, $primary, $date); // todo sum all and convert.
$return['pc_balance'] = $this->convertAllBalances($others, $primary, $date); // todo sum all and convert.
// Log::debug(sprintf('Set pc_balance to %s', $return['pc_balance']));
}

View File

@@ -30,9 +30,6 @@ use FireflyIII\Models\Account;
use FireflyIII\Models\TransactionCurrency;
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
use FireflyIII\Support\Facades\Amount;
use FireflyIII\Support\Facades\Steam;
use FireflyIII\Support\Http\Api\ExchangeRateConverter;
use Illuminate\Support\Facades\Log;
use Symfony\Component\HttpFoundation\ParameterBag;
/**
@@ -63,103 +60,46 @@ class AccountTransformer extends AbstractTransformer
public function transform(Account $account): array
{
if (null === $account->meta) {
$account->meta = [];
$account->meta = [
'currency' => null,
];
}
// get account type:
$accountType = (string)config(sprintf('firefly.shortNamesByFullName.%s', $account->full_account_type));
$liabilityType = (string)config(sprintf('firefly.shortLiabilityNameByFullName.%s', $account->full_account_type));
$liabilityType = '' === $liabilityType ? null : strtolower($liabilityType);
$liabilityDirection = $account->meta['liability_direction'] ?? null;
// get account role (will only work if the type is asset).
$accountRole = $this->getAccountRole($account, $accountType);
$accountType = (string) config(sprintf('firefly.shortNamesByFullName.%s', $account->full_account_type));
$liabilityType = (string) config(sprintf('firefly.shortLiabilityNameByFullName.%s', $account->full_account_type));
$liabilityType = '' === $liabilityType ? null : strtolower($liabilityType);
$liabilityDirection = $account->meta['liability_direction'] ?? null;
$accountRole = $this->getAccountRole($account, $accountType);
$hasCurrencySettings = null !== $account->meta['currency'];
$includeNetWorth = 1 === (int) ($account->meta['include_net_worth'] ?? 0);
$longitude = $account->meta['location']['longitude'] ?? null;
$latitude = $account->meta['location']['latitude'] ?? null;
$zoomLevel = $account->meta['location']['zoom_level'] ?? null;
$order = $account->order;
// date (for balance etc.)
$date = $this->getDate();
$date = $this->getDate();
$date->endOfDay();
[$creditCardType, $monthlyPaymentDate] = $this->getCCInfo($account, $accountRole, $accountType);
[$openingBalance, $pcOpeningBalance, $openingBalanceDate] = $this->getOpeningBalance($account, $accountType);
[$interest, $interestPeriod] = $this->getInterest($account, $accountType);
$primary = $this->primary;
if (!$this->convertToPrimary) {
// reset primary currency to NULL, not interesting.
$primary = null;
// get primary currency as fallback:
$currency = $this->primary; // assume primary currency
if ($hasCurrencySettings) {
$currency = $account->meta['currency'];
}
$decimalPlaces = (int)$account->meta['currency']?->decimal_places;
$decimalPlaces = 0 === $decimalPlaces ? 2 : $decimalPlaces;
$openingBalanceRounded = Steam::bcround($openingBalance, $decimalPlaces);
$includeNetWorth = 1 === (int)($account->meta['include_net_worth'] ?? 0);
$longitude = $account->meta['location']['longitude'] ?? null;
$latitude = $account->meta['location']['latitude'] ?? null;
$zoomLevel = $account->meta['location']['zoom_level'] ?? null;
// no order for some accounts:
$order = $account->order;
if (!in_array(strtolower($accountType), ['liability', 'liabilities', 'asset'], true)) {
$order = null;
}
Log::debug(sprintf('transform: Call finalAccountBalance with date/time "%s"', $date->toIso8601String()));
$finalBalance = Steam::finalAccountBalance($account, $date, $this->primary, $this->convertToPrimary);
if ($this->convertToPrimary) {
$finalBalance['balance'] = $finalBalance[$account->meta['currency']?->code] ?? '0';
}
$currentBalance = Steam::bcround($finalBalance['balance'] ?? '0', $decimalPlaces);
$pcCurrentBalance = $this->convertToPrimary ? Steam::bcround($finalBalance['pc_balance'] ?? '0', $primary->decimal_places) : null;
// set up balances array:
$balances = [];
$balances[]
= [
'type' => 'current',
'amount' => $currentBalance,
'currency_id' => $account->meta['currency_id'] ?? null,
'currency_code' => $account->meta['currency']?->code,
'currency_symbol' => $account->meta['currency']?->symbol,
'currency_decimal_places' => $account->meta['currency']?->decimal_places,
'date' => $date->toAtomString(),
];
if (null !== $pcCurrentBalance) {
$balances[] = [
'type' => 'pc_current',
'amount' => $pcCurrentBalance,
'currency_id' => $primary instanceof TransactionCurrency ? (string)$primary->id : null,
'currency_code' => $primary?->code,
'currency_symbol' => $primary?->symbol,
'ccurrency_decimal_places' => $primary?->decimal_places,
'date' => $date->toAtomString(),
];
}
if (null !== $openingBalance) {
$balances[] = [
'type' => 'opening',
'amount' => $openingBalanceRounded,
'currency_id' => $account->meta['currency_id'] ?? null,
'currency_code' => $account->meta['currency']?->code,
'currency_symbol' => $account->meta['currency']?->symbol,
'currency_decimal_places' => $account->meta['currency']?->decimal_places,
'date' => $openingBalanceDate,
];
}
if (null !== $account->virtual_balance) {
$balances[] = [
'type' => 'virtual',
'amount' => Steam::bcround($account->virtual_balance, $decimalPlaces),
'currency_id' => $account->meta['currency_id'] ?? null,
'currency_code' => $account->meta['currency']?->code,
'currency_symbol' => $account->meta['currency']?->symbol,
'currency_decimal_places' => $account->meta['currency']?->decimal_places,
'date' => $date->toAtomString(),
];
}
// get some listed information from the account meta-data:
[$creditCardType, $monthlyPaymentDate] = $this->getCCInfo($account, $accountRole, $accountType);
$openingBalanceDate = $this->getOpeningBalance($account, $accountType);
[$interest, $interestPeriod] = $this->getInterest($account, $accountType);
return [
'id' => (string)$account->id,
'id' => (string) $account->id,
'created_at' => $account->created_at->toAtomString(),
'updated_at' => $account->updated_at->toAtomString(),
'active' => $account->active,
@@ -167,16 +107,36 @@ class AccountTransformer extends AbstractTransformer
'name' => $account->name,
'type' => strtolower($accountType),
'account_role' => $accountRole,
'currency_id' => $account->meta['currency_id'] ?? null,
'currency_code' => $account->meta['currency']?->code,
'currency_symbol' => $account->meta['currency']?->symbol,
'currency_decimal_places' => $account->meta['currency']?->decimal_places,
'primary_currency_id' => $primary instanceof TransactionCurrency ? (string)$primary->id : null,
'primary_currency_code' => $primary?->code,
'primary_currency_symbol' => $primary?->symbol,
'primary_currency_decimal_places' => $primary?->decimal_places,
'current_balance' => $currentBalance,
'pc_current_balance' => $pcCurrentBalance,
// currency information, structured for 6.3.0.
'object_has_currency_setting' => $hasCurrencySettings,
// currency is object specific or primary, already determined above.
'currency_id' => (string) $currency['id'],
'currency_name' => $currency['name'],
'currency_code' => $currency['code'],
'currency_symbol' => $currency['symbol'],
'currency_decimal_places' => $currency['decimal_places'],
'primary_currency_id' => (string) $this->primary->id,
'primary_currency_name' => $this->primary->name,
'primary_currency_code' => $this->primary->code,
'primary_currency_symbol' => $this->primary->symbol,
'primary_currency_decimal_places' => $this->primary->decimal_places,
// balances, structured for 6.3.0.
'current_balance' => $account->meta['balances']['current_balance'],
'pc_current_balance' => $account->meta['balances']['pc_current_balance'],
'opening_balance' => $account->meta['balances']['opening_balance'],
'pc_opening_balance' => $account->meta['balances']['pc_opening_balance'],
'virtual_balance' => $account->meta['balances']['virtual_balance'],
'pc_virtual_balance' => $account->meta['balances']['pc_virtual_balance'],
'debt_amount' => $account->meta['balances']['debt_amount'],
'pc_debt_amount' => $account->meta['balances']['pc_debt_amount'],
'current_balance_date' => $date->toAtomString(),
'notes' => $account->meta['notes'] ?? null,
'monthly_payment_date' => $monthlyPaymentDate,
@@ -184,22 +144,16 @@ class AccountTransformer extends AbstractTransformer
'account_number' => $account->meta['account_number'] ?? null,
'iban' => '' === $account->iban ? null : $account->iban,
'bic' => $account->meta['BIC'] ?? null,
'virtual_balance' => Steam::bcround($account->virtual_balance, $decimalPlaces),
'pc_virtual_balance' => $this->convertToPrimary ? Steam::bcround($account->native_virtual_balance, $primary->decimal_places) : null,
'opening_balance' => $openingBalanceRounded,
'pc_opening_balance' => $pcOpeningBalance,
'opening_balance_date' => $openingBalanceDate,
'liability_type' => $liabilityType,
'liability_direction' => $liabilityDirection,
'interest' => $interest,
'interest_period' => $interestPeriod,
'current_debt' => $account->meta['current_debt'] ?? null,
'include_net_worth' => $includeNetWorth,
'longitude' => $longitude,
'latitude' => $latitude,
'zoom_level' => $zoomLevel,
'last_activity' => array_key_exists('last_activity', $account->meta) ? $account->meta['last_activity']->toAtomString() : null,
'balances' => $balances,
'links' => [
[
'rel' => 'self',
@@ -212,7 +166,7 @@ class AccountTransformer extends AbstractTransformer
private function getAccountRole(Account $account, string $accountType): ?string
{
$accountRole = $account->meta['account_role'] ?? null;
if ('asset' !== $accountType || '' === (string)$accountRole) {
if ('asset' !== $accountType || '' === (string) $accountRole) {
return null;
}
@@ -248,7 +202,7 @@ class AccountTransformer extends AbstractTransformer
}
$monthlyPaymentDate = $object->toAtomString();
}
if (10 !== strlen((string)$monthlyPaymentDate)) {
if (10 !== strlen((string) $monthlyPaymentDate)) {
$monthlyPaymentDate = Carbon::parse($monthlyPaymentDate, config('app.timezone'))->toAtomString();
}
}
@@ -256,15 +210,10 @@ class AccountTransformer extends AbstractTransformer
return [$creditCardType, $monthlyPaymentDate];
}
private function getOpeningBalance(Account $account, string $accountType): array
private function getOpeningBalance(Account $account, string $accountType): ?string
{
$openingBalance = null;
$openingBalanceDate = null;
$pcOpeningBalance = null;
if (in_array($accountType, ['asset', 'liabilities'], true)) {
// grab from meta.
$openingBalance = $account->meta['opening_balance_amount'] ?? null;
$pcOpeningBalance = null;
$openingBalanceDate = $account->meta['opening_balance_date'] ?? null;
}
if (null !== $openingBalanceDate) {
@@ -274,15 +223,9 @@ class AccountTransformer extends AbstractTransformer
}
$openingBalanceDate = $object->toAtomString();
// NOW do conversion.
if ($this->convertToPrimary && null !== $account->meta['currency']) {
$converter = new ExchangeRateConverter();
$pcOpeningBalance = $converter->convert($account->meta['currency'], $this->primary, $object, $openingBalance);
}
}
return [$openingBalance, $pcOpeningBalance, $openingBalanceDate];
return $openingBalanceDate;
}
private function getInterest(Account $account, string $accountType): array

View File

@@ -26,9 +26,6 @@ namespace FireflyIII\Transformers;
use FireflyIII\Models\AvailableBudget;
use FireflyIII\Models\TransactionCurrency;
use FireflyIII\Repositories\Budget\BudgetRepositoryInterface;
use FireflyIII\Repositories\Budget\NoBudgetRepositoryInterface;
use FireflyIII\Repositories\Budget\OperationsRepositoryInterface;
use FireflyIII\Support\Facades\Amount;
/**
@@ -36,22 +33,16 @@ use FireflyIII\Support\Facades\Amount;
*/
class AvailableBudgetTransformer extends AbstractTransformer
{
private readonly bool $convertToPrimary;
private readonly TransactionCurrency $primary;
private readonly NoBudgetRepositoryInterface $noBudgetRepository;
private readonly OperationsRepositoryInterface $opsRepository;
private readonly BudgetRepositoryInterface $repository;
private readonly bool $convertToPrimary;
private readonly TransactionCurrency $primary;
/**
* CurrencyTransformer constructor.
*/
public function __construct()
{
$this->repository = app(BudgetRepositoryInterface::class);
$this->opsRepository = app(OperationsRepositoryInterface::class);
$this->noBudgetRepository = app(NoBudgetRepositoryInterface::class);
$this->primary = Amount::getPrimaryCurrency();
$this->convertToPrimary = Amount::convertToPrimary();
$this->primary = Amount::getPrimaryCurrency();
$this->convertToPrimary = Amount::convertToPrimary();
}
/**
@@ -59,31 +50,41 @@ class AvailableBudgetTransformer extends AbstractTransformer
*/
public function transform(AvailableBudget $availableBudget): array
{
$this->repository->setUser($availableBudget->user);
$currency = $availableBudget->transactionCurrency;
$primary = $this->primary;
if (!$this->convertToPrimary) {
$primary = null;
$amount = Steam::bcround($availableBudget->amount, $currency->decimal_places);
$pcAmount = null;
if ($this->convertToPrimary) {
$pcAmount = Steam::bcround($availableBudget->native_amount, $this->primary->decimal_places);
}
$data = [
'id' => (string)$availableBudget->id,
return [
'id' => (string) $availableBudget->id,
'created_at' => $availableBudget->created_at->toAtomString(),
'updated_at' => $availableBudget->updated_at->toAtomString(),
'currency_id' => (string)$currency->id,
// currencies according to 6.3.0
'object_has_currency_setting' => true,
'currency_id' => (string) $currency->id,
'currency_name' => $currency->name,
'currency_code' => $currency->code,
'currency_symbol' => $currency->symbol,
'currency_decimal_places' => $currency->decimal_places,
'primary_currency_id' => $primary instanceof TransactionCurrency ? (string)$primary->id : null,
'primary_currency_code' => $primary?->code,
'primary_currency_symbol' => $primary?->symbol,
'primary_currency_decimal_places' => $primary?->decimal_places,
'amount' => app('steam')->bcround($availableBudget->amount, $currency->decimal_places),
'pc_amount' => $this->convertToPrimary ? app('steam')->bcround($availableBudget->native_amount, $currency->decimal_places) : null,
'primary_currency_id' => (string) $this->primary->id,
'primary_currency_name' => $this->primary->name,
'primary_currency_code' => $this->primary->code,
'primary_currency_symbol' => $this->primary->symbol,
'primary_currency_decimal_places' => $this->primary->decimal_places,
'amount' => $amount,
'pc_amount' => $pcAmount,
'start' => $availableBudget->start_date->toAtomString(),
'end' => $availableBudget->end_date->endOfDay()->toAtomString(),
'spent_in_budgets' => [],
'spent_no_budget' => [],
'spent_in_budgets' => $availableBudget->meta['spent_in_budgets'],
'pc_spent_in_budgets' => $availableBudget->meta['pc_spent_in_budgets'],
'spent_outside_budgets' => $availableBudget->meta['spent_outside_budgets'],
'pc_spent_outside_budgets' => $availableBudget->meta['pc_spent_outside_budgets'],
'links' => [
[
'rel' => 'self',
@@ -91,28 +92,5 @@ class AvailableBudgetTransformer extends AbstractTransformer
],
],
];
$start = $this->parameters->get('start');
$end = $this->parameters->get('end');
if (null !== $start && null !== $end) {
$data['spent_in_budgets'] = $this->getSpentInBudgets();
$data['spent_no_budget'] = $this->spentOutsideBudgets();
}
return $data;
}
private function getSpentInBudgets(): array
{
$allActive = $this->repository->getActiveBudgets();
$sums = $this->opsRepository->sumExpenses($this->parameters->get('start'), $this->parameters->get('end'), null, $allActive);
return array_values($sums);
}
private function spentOutsideBudgets(): array
{
$sums = $this->noBudgetRepository->sumExpenses($this->parameters->get('start'), $this->parameters->get('end'));
return array_values($sums);
}
}

View File

@@ -55,20 +55,32 @@ class BillTransformer extends AbstractTransformer
'id' => $bill->id,
'created_at' => $bill->created_at->toAtomString(),
'updated_at' => $bill->updated_at->toAtomString(),
'currency_id' => (string)$bill->transaction_currency_id,
'name' => $bill->name,
// currencies according to 6.3.0
'object_has_currency_setting' => true,
'currency_id' => (string) $bill->transaction_currency_id,
'currency_name' => $currency->name,
'currency_code' => $currency->code,
'currency_symbol' => $currency->symbol,
'currency_decimal_places' => $currency->decimal_places,
'primary_currency_id' => (string)$this->primary->id,
'primary_currency_id' => (string) $this->primary->id,
'primary_currency_name' => $this->primary->name,
'primary_currency_code' => $this->primary->code,
'primary_currency_symbol' => $this->primary->symbol,
'primary_currency_decimal_places' => $this->primary->decimal_places,
'name' => $bill->name,
// amounts according to 6.3.0
'amount_min' => $bill->amounts['amount_min'],
'pc_amount_min' => $bill->amounts['pc_amount_min'],
'amount_max' => $bill->amounts['amount_max'],
'pc_amount_max' => $bill->amounts['pc_amount_max'],
'amount_avg' => $bill->amounts['average'],
'pc_amount_avg' => $bill->amounts['pc_average'],
'date' => $bill->date->toAtomString(),
'end_date' => $bill->end_date?->toAtomString(),
'extension_date' => $bill->extension_date?->toAtomString(),
@@ -87,10 +99,6 @@ class BillTransformer extends AbstractTransformer
'next_expected_match' => $bill->meta['nem']?->toAtomString(),
'next_expected_match_diff' => $bill->meta['nem_diff'],
// these fields need work:
// 'next_expected_match' => $nem,
// 'next_expected_match_diff' => $nemDiff,
// 'pay_dates' => $payDatesFormatted,
'links' => [
[
'rel' => 'self',

View File

@@ -26,10 +26,8 @@ namespace FireflyIII\Transformers;
use FireflyIII\Models\BudgetLimit;
use FireflyIII\Models\TransactionCurrency;
use FireflyIII\Repositories\Budget\BudgetLimitRepositoryInterface;
use FireflyIII\Repositories\Budget\OperationsRepository;
use FireflyIII\Support\Facades\Amount;
use Illuminate\Support\Collection;
use FireflyIII\Support\Facades\Steam;
use League\Fractal\Resource\Item;
/**
@@ -42,11 +40,11 @@ class BudgetLimitTransformer extends AbstractTransformer
'budget',
];
protected bool $convertToPrimary;
protected TransactionCurrency $primary;
protected TransactionCurrency $primaryCurrency;
public function __construct()
{
$this->primary = Amount::getPrimaryCurrency();
$this->primaryCurrency = Amount::getPrimaryCurrency();
$this->convertToPrimary = Amount::convertToPrimary();
}
@@ -65,39 +63,19 @@ class BudgetLimitTransformer extends AbstractTransformer
*/
public function transform(BudgetLimit $budgetLimit): array
{
$repository = app(OperationsRepository::class);
$limitRepos = app(BudgetLimitRepositoryInterface::class);
$repository->setUser($budgetLimit->budget->user);
$limitRepos->setUser($budgetLimit->budget->user);
$expenses = $repository->sumExpenses(
$budgetLimit->start_date,
$budgetLimit->end_date,
null,
new Collection([$budgetLimit->budget]),
$budgetLimit->transactionCurrency
);
$currency = $budgetLimit->transactionCurrency;
$amount = $budgetLimit->amount;
$notes = $limitRepos->getNoteText($budgetLimit);
$currencyDecimalPlaces = 2;
$currencyId = null;
$currencyName = null;
$currencyCode = null;
$currencySymbol = null;
if (null !== $currency) {
$amount = $budgetLimit->amount;
$currencyId = $currency->id;
$currencyName = $currency->name;
$currencyCode = $currency->code;
$currencySymbol = $currency->symbol;
$currencyDecimalPlaces = $currency->decimal_places;
}
$amount = app('steam')->bcround($amount, $currencyDecimalPlaces);
$primary = $this->primary;
if (!$this->convertToPrimary) {
$primary = null;
}
$currency = $budgetLimit->transactionCurrency;
if (null === $currency) {
$currency = $this->primaryCurrency;
}
$amount = Steam::bcround($budgetLimit->amount, $currency->decimal_places);
$pcAmount = null;
if ($this->convertToPrimary && $currency->id === $this->primaryCurrency->id) {
$pcAmount = $amount;
}
if ($this->convertToPrimary && $currency->id !== $this->primaryCurrency->id) {
$pcAmount = Steam::bcround($budgetLimit->native_amount, $this->primaryCurrency->decimal_places);
}
return [
'id' => (string)$budgetLimit->id,
@@ -106,20 +84,28 @@ class BudgetLimitTransformer extends AbstractTransformer
'start' => $budgetLimit->start_date->toAtomString(),
'end' => $budgetLimit->end_date->endOfDay()->toAtomString(),
'budget_id' => (string)$budgetLimit->budget_id,
'currency_id' => (string)$currencyId,
'currency_code' => $currencyCode,
'currency_name' => $currencyName,
'currency_decimal_places' => $currencyDecimalPlaces,
'currency_symbol' => $currencySymbol,
'primary_currency_id' => $primary instanceof TransactionCurrency ? (string)$primary->id : null,
'primary_currency_code' => $primary?->code,
'primary_currency_symbol' => $primary?->symbol,
'primary_currency_decimal_places' => $primary?->decimal_places,
// currency settings according to 6.3.0
'object_has_currency_setting' => true,
'currency_id' => (string)$currency->id,
'currency_name' => $currency->name,
'currency_code' => $currency->code,
'currency_symbol' => $currency->symbol,
'currency_decimal_places' => $currency->decimal_places,
'primary_currency_id' => (int)$this->primaryCurrency->id,
'primary_currency_name' => $this->primaryCurrency->name,
'primary_currency_code' => $this->primaryCurrency->code,
'primary_currency_symbol' => $this->primaryCurrency->symbol,
'primary_currency_decimal_places' => $this->primaryCurrency->decimal_places,
'amount' => $amount,
'pc_amount' => $this->convertToPrimary ? app('steam')->bcround($budgetLimit->native_amount, $primary->decimal_places) : null,
'pc_amount' => $pcAmount,
'period' => $budgetLimit->period,
'spent' => $expenses[$currencyId]['sum'] ?? '0', // will be in primary currency if convertToPrimary.
'notes' => '' === $notes ? null : $notes,
'spent' => $budgetLimit->meta['spent'],
'pc_spent' => $budgetLimit->meta['pc_spent'],
'notes' => $budgetLimit->meta['notes'],
'links' => [
[
'rel' => 'self',

View File

@@ -27,10 +27,8 @@ namespace FireflyIII\Transformers;
use FireflyIII\Enums\AutoBudgetType;
use FireflyIII\Models\Budget;
use FireflyIII\Models\TransactionCurrency;
use FireflyIII\Repositories\Budget\BudgetRepositoryInterface;
use FireflyIII\Repositories\Budget\OperationsRepositoryInterface;
use FireflyIII\Support\Facades\Amount;
use Illuminate\Support\Collection;
use FireflyIII\Support\Facades\Steam;
use Symfony\Component\HttpFoundation\ParameterBag;
/**
@@ -38,21 +36,23 @@ use Symfony\Component\HttpFoundation\ParameterBag;
*/
class BudgetTransformer extends AbstractTransformer
{
private readonly bool $convertToPrimary;
private readonly TransactionCurrency $primary;
private readonly OperationsRepositoryInterface $opsRepository;
private readonly BudgetRepositoryInterface $repository;
private readonly bool $convertToPrimary;
private readonly TransactionCurrency $primaryCurrency;
private array $types;
/**
* BudgetTransformer constructor.
*/
public function __construct()
{
$this->opsRepository = app(OperationsRepositoryInterface::class);
$this->repository = app(BudgetRepositoryInterface::class);
$this->parameters = new ParameterBag();
$this->primary = Amount::getPrimaryCurrency();
$this->primaryCurrency = Amount::getPrimaryCurrency();
$this->convertToPrimary = Amount::convertToPrimary();
$this->types = [
AutoBudgetType::AUTO_BUDGET_RESET->value => 'reset',
AutoBudgetType::AUTO_BUDGET_ROLLOVER->value => 'rollover',
AutoBudgetType::AUTO_BUDGET_ADJUSTED->value => 'adjusted',
];
}
/**
@@ -60,69 +60,51 @@ class BudgetTransformer extends AbstractTransformer
*/
public function transform(Budget $budget): array
{
$this->opsRepository->setUser($budget->user);
$start = $this->parameters->get('start');
$end = $this->parameters->get('end');
$autoBudget = $this->repository->getAutoBudget($budget);
$spent = [];
if (null !== $start && null !== $end) {
$spent = $this->beautify($this->opsRepository->sumExpenses($start, $end, null, new Collection([$budget])));
}
// info for auto budget.
$abType = null;
$abAmount = null;
$abPrimary = null;
$abPeriod = null;
$notes = $this->repository->getNoteText($budget);
$abType = null;
$abAmount = null;
$abPrimary = null;
$abPeriod = null;
$types = [
AutoBudgetType::AUTO_BUDGET_RESET->value => 'reset',
AutoBudgetType::AUTO_BUDGET_ROLLOVER->value => 'rollover',
AutoBudgetType::AUTO_BUDGET_ADJUSTED->value => 'adjusted',
];
$currency = $autoBudget?->transactionCurrency;
$primary = $this->primary;
if (!$this->convertToPrimary) {
$primary = null;
}
if (null === $autoBudget) {
$currency = $primary;
}
if (null !== $autoBudget) {
$abType = $types[$autoBudget->auto_budget_type];
$abAmount = app('steam')->bcround($autoBudget->amount, $currency->decimal_places);
$abPrimary = $this->convertToPrimary ? app('steam')->bcround($autoBudget->native_amount, $primary->decimal_places) : null;
$abPeriod = $autoBudget->period;
$currency = $budget->meta['currency'] ?? null;
if (null !== $budget->meta['auto_budget']) {
$abType = $this->types[$budget->meta['auto_budget']['type']];
$abAmount = Steam::bcround($budget->meta['auto_budget']['amount'], $currency->decimal_places);
$abPrimary = $this->convertToPrimary ? Steam::bcround($budget->meta['auto_budget']['pc_amount'], $this->primaryCurrency->decimal_places) : null;
$abPeriod = $budget->meta['auto_budget']['period'];
}
return [
'id' => (string) $budget->id,
'id' => (string)$budget->id,
'created_at' => $budget->created_at->toAtomString(),
'updated_at' => $budget->updated_at->toAtomString(),
'active' => $budget->active,
'name' => $budget->name,
'order' => $budget->order,
'notes' => $notes,
'notes' => $budget->meta['notes'],
'auto_budget_type' => $abType,
'auto_budget_period' => $abPeriod,
'currency_id' => null === $autoBudget ? null : (string) $autoBudget->transactionCurrency->id,
'currency_code' => $autoBudget?->transactionCurrency->code,
'currency_name' => $autoBudget?->transactionCurrency->name,
'currency_decimal_places' => $autoBudget?->transactionCurrency->decimal_places,
'currency_symbol' => $autoBudget?->transactionCurrency->symbol,
// new currency settings.
'object_has_currency_setting' => null !== $budget->meta['currency'],
'currency_id' => null === $currency ? null : (string)$currency->id,
'currency_code' => $currency?->code,
'currency_name' => $currency?->name,
'currency_symbol' => $currency?->symbol,
'currency_decimal_places' => $currency?->decimal_places,
'primary_currency_id' => $primary instanceof TransactionCurrency ? (string) $primary->id : null,
'primary_currency_code' => $primary?->code,
'primary_currency_symbol' => $primary?->symbol,
'primary_currency_decimal_places' => $primary?->decimal_places,
// amount and primary currency amount if present.
'primary_currency_id' => (string)$this->primaryCurrency->id,
'primary_currency_name' => $this->primaryCurrency->name,
'primary_currency_code' => $this->primaryCurrency->code,
'primary_currency_symbol' => $this->primaryCurrency->symbol,
'primary_currency_decimal_places' => $this->primaryCurrency->decimal_places,
'auto_budget_amount' => $abAmount,
'pc_auto_budget_amount' => $abPrimary,
'spent' => $spent, // always in primary currency.
'spent' => $this->beautify($budget->meta['spent']), // always in primary currency.
'pc_spent' => $this->beautify($budget->meta['pc_spent']), // always in primary currency.
'links' => [
[
'rel' => 'self',
@@ -136,7 +118,7 @@ class BudgetTransformer extends AbstractTransformer
{
$return = [];
foreach ($array as $data) {
$data['sum'] = app('steam')->bcround($data['sum'], (int) $data['currency_decimal_places']);
$data['sum'] = Steam::bcround($data['sum'], (int)$data['currency_decimal_places']);
$return[] = $data;
}

View File

@@ -99,7 +99,7 @@ class CategoryTransformer extends AbstractTransformer
{
$return = [];
foreach ($array as $data) {
$data['sum'] = app('steam')->bcround($data['sum'], (int)$data['currency_decimal_places']);
$data['sum'] = Steam::bcround($data['sum'], (int)$data['currency_decimal_places']);
$return[] = $data;
}

View File

@@ -42,6 +42,7 @@ class CurrencyTransformer extends AbstractTransformer
'updated_at' => $currency->updated_at->toAtomString(),
'native' => $currency->userGroupNative,
'default' => $currency->userGroupNative,
'primary' => $currency->userGroupNative,
'enabled' => $currency->userGroupEnabled,
'name' => $currency->name,
'code' => $currency->code,

View File

@@ -75,7 +75,7 @@ class PiggyBankEventTransformer extends AbstractTransformer
'id' => (string) $event->id,
'created_at' => $event->created_at?->toAtomString(),
'updated_at' => $event->updated_at?->toAtomString(),
'amount' => app('steam')->bcround($event->amount, $currency->decimal_places),
'amount' => Steam::bcround($event->amount, $currency->decimal_places),
'currency_id' => (string) $currency->id,
'currency_code' => $currency->code,
'currency_symbol' => $currency->symbol,

View File

@@ -88,9 +88,9 @@ class PiggyBankTransformer extends AbstractTransformer
if (0 !== bccomp($targetAmount, '0')) { // target amount is not 0.00
$leftToSave = bcsub($piggyBank->target_amount, $currentAmount);
$percentage = (int) bcmul(bcdiv($currentAmount, $targetAmount), '100');
$targetAmount = app('steam')->bcround($targetAmount, $currency->decimal_places);
$leftToSave = app('steam')->bcround($leftToSave, $currency->decimal_places);
$savePerMonth = app('steam')->bcround($this->piggyRepos->getSuggestedMonthlyAmount($piggyBank), $currency->decimal_places);
$targetAmount = Steam::bcround($targetAmount, $currency->decimal_places);
$leftToSave = Steam::bcround($leftToSave, $currency->decimal_places);
$savePerMonth = Steam::bcround($this->piggyRepos->getSuggestedMonthlyAmount($piggyBank), $currency->decimal_places);
}
$startDate = $piggyBank->start_date?->format('Y-m-d');
$targetDate = $piggyBank->target_date?->format('Y-m-d');

View File

@@ -193,10 +193,10 @@ class RecurrenceTransformer extends AbstractTransformer
$destinationType = $destinationAccount->accountType->type;
$destinationIban = $destinationAccount->iban;
}
$amount = app('steam')->bcround($transaction->amount, $transaction->transactionCurrency->decimal_places);
$amount = Steam::bcround($transaction->amount, $transaction->transactionCurrency->decimal_places);
$foreignAmount = null;
if (null !== $transaction->foreign_currency_id && null !== $transaction->foreign_amount) {
$foreignAmount = app('steam')->bcround($transaction->foreign_amount, $foreignCurrencyDp);
$foreignAmount = Steam::bcround($transaction->foreign_amount, $foreignCurrencyDp);
}
$transactionArray = [
'id' => (string) $transaction->id,

View File

@@ -35,6 +35,7 @@ use FireflyIII\Models\TransactionCurrency;
use FireflyIII\Models\TransactionGroup;
use FireflyIII\Models\TransactionJournal;
use FireflyIII\Repositories\TransactionGroup\TransactionGroupRepositoryInterface;
use FireflyIII\Support\Facades\Steam;
use FireflyIII\Support\NullArrayObject;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Log;
@@ -116,10 +117,10 @@ class TransactionGroupTransformer extends AbstractTransformer
private function transformTransaction(array $transaction): array
{
// amount:
$amount = app('steam')->positive((string) ($transaction['amount'] ?? '0'));
$amount = Steam::positive((string) ($transaction['amount'] ?? '0'));
$foreignAmount = null;
if (null !== $transaction['foreign_amount'] && '' !== $transaction['foreign_amount'] && 0 !== bccomp('0', (string) $transaction['foreign_amount'])) {
$foreignAmount = app('steam')->positive($transaction['foreign_amount']);
$foreignAmount = Steam::positive($transaction['foreign_amount']);
}
// set primary amount to the normal amount if the currency matches.
@@ -128,7 +129,7 @@ class TransactionGroupTransformer extends AbstractTransformer
}
if (array_key_exists('pc_amount', $transaction) && null !== $transaction['pc_amount']) {
$transaction['pc_amount'] = app('steam')->positive($transaction['pc_amount']);
$transaction['pc_amount'] = Steam::positive($transaction['pc_amount']);
}
$type = $this->stringFromArray($transaction, 'transaction_type_type', TransactionTypeEnum::WITHDRAWAL->value);
@@ -139,107 +140,107 @@ class TransactionGroupTransformer extends AbstractTransformer
$recurrenceCount = null !== $recurrenceCount ? (int) $recurrenceCount : null;
return [
'user' => (string) $transaction['user_id'],
'transaction_journal_id' => (string) $transaction['transaction_journal_id'],
'type' => strtolower((string) $type),
'date' => $transaction['date']->toAtomString(),
'order' => $transaction['order'],
'user' => (string) $transaction['user_id'],
'transaction_journal_id' => (string) $transaction['transaction_journal_id'],
'type' => strtolower((string) $type),
'date' => $transaction['date']->toAtomString(),
'order' => $transaction['order'],
'currency_id' => (string) $transaction['currency_id'],
'currency_code' => $transaction['currency_code'],
'currency_name' => $transaction['currency_name'],
'currency_symbol' => $transaction['currency_symbol'],
'currency_decimal_places' => (int) $transaction['currency_decimal_places'],
// currency information, structured for 6.3.0.
'object_has_currency_setting' => true,
'foreign_currency_id' => $this->stringFromArray($transaction, 'foreign_currency_id', null),
'foreign_currency_code' => $transaction['foreign_currency_code'],
'foreign_currency_name' => $transaction['foreign_currency_name'],
'foreign_currency_symbol' => $transaction['foreign_currency_symbol'],
'foreign_currency_decimal_places' => $transaction['foreign_currency_decimal_places'],
'currency_id' => (string) $transaction['currency_id'],
'currency_code' => $transaction['currency_code'],
'currency_name' => $transaction['currency_name'],
'currency_symbol' => $transaction['currency_symbol'],
'currency_decimal_places' => (int) $transaction['currency_decimal_places'],
'amount' => $amount,
'foreign_amount' => $foreignAmount,
'foreign_currency_id' => $this->stringFromArray($transaction, 'foreign_currency_id', null),
'foreign_currency_code' => $transaction['foreign_currency_code'],
'foreign_currency_name' => $transaction['foreign_currency_name'],
'foreign_currency_symbol' => $transaction['foreign_currency_symbol'],
'foreign_currency_decimal_places' => $transaction['foreign_currency_decimal_places'],
// primary currency amount, defaults to NULL when convertToPrimary is false.
'pc_amount' => $transaction['pc_amount'] ?? null,
'primary_currency_id' => $transaction['primary_currency']['id'] ?? null,
'primary_currency_code' => $transaction['primary_currency']['code'] ?? null,
'primary_currency_name' => $transaction['primary_currency']['name'] ?? null,
'primary_currency_symbol' => $transaction['primary_currency']['symbol'] ?? null,
'primary_currency_decimal_places' => $transaction['primary_currency']['decimal_places'] ?? null,
// primary currency, always present.
'primary_currency_id' => $transaction['primary_currency']['id'] ?? null,
'primary_currency_code' => $transaction['primary_currency']['code'] ?? null,
'primary_currency_name' => $transaction['primary_currency']['name'] ?? null,
'primary_currency_symbol' => $transaction['primary_currency']['symbol'] ?? null,
'primary_currency_decimal_places' => $transaction['primary_currency']['decimal_places'] ?? null,
// amounts, structured for 6.3.0.
'amount' => $amount,
'pc_amount' => $transaction['pc_amount'] ?? null,
// source balance after
'source_balance_after' => $transaction['source_balance_after'] ?? null,
'source_balance_dirty' => $transaction['source_balance_dirty'],
'foreign_amount' => $foreignAmount,
'pc_foreign_amount' => null,
'source_balance_after' => $transaction['source_balance_after'] ?? null,
'pc_source_balance_after' => null,
// destination balance after
'destination_balance_after' => $transaction['destination_balance_after'] ?? null,
'destination_balance_dirty' => $transaction['destination_balance_dirty'],
'destination_balance_after' => $transaction['destination_balance_after'] ?? null,
'pc_destination_balance_after' => null,
// balance before and after, if not dirty.
// 'running_balance_dirty' => $transaction['balance_dirty'] ?? false,
// 'running_balance_before' => $transaction['balance_before'] ?? null,
// 'running_balance_after' => $transaction['balance_after'] ?? null,
'source_balance_dirty' => $transaction['source_balance_dirty'],
'destination_balance_dirty' => $transaction['destination_balance_dirty'],
'description' => $transaction['description'],
'source_id' => (string) $transaction['source_account_id'],
'source_name' => $transaction['source_account_name'],
'source_iban' => $transaction['source_account_iban'],
'source_type' => $transaction['source_account_type'],
'description' => $transaction['description'],
'destination_id' => (string) $transaction['destination_account_id'],
'destination_name' => $transaction['destination_account_name'],
'destination_iban' => $transaction['destination_account_iban'],
'destination_type' => $transaction['destination_account_type'],
'source_id' => (string) $transaction['source_account_id'],
'source_name' => $transaction['source_account_name'],
'source_iban' => $transaction['source_account_iban'],
'source_type' => $transaction['source_account_type'],
'budget_id' => $this->stringFromArray($transaction, 'budget_id', null),
'budget_name' => $transaction['budget_name'],
'destination_id' => (string) $transaction['destination_account_id'],
'destination_name' => $transaction['destination_account_name'],
'destination_iban' => $transaction['destination_account_iban'],
'destination_type' => $transaction['destination_account_type'],
'category_id' => $this->stringFromArray($transaction, 'category_id', null),
'category_name' => $transaction['category_name'],
'budget_id' => $this->stringFromArray($transaction, 'budget_id', null),
'budget_name' => $transaction['budget_name'],
'bill_id' => $this->stringFromArray($transaction, 'bill_id', null),
'bill_name' => $transaction['bill_name'],
'subscription_id' => $this->stringFromArray($transaction, 'bill_id', null),
'subscription_name' => $transaction['bill_name'],
'category_id' => $this->stringFromArray($transaction, 'category_id', null),
'category_name' => $transaction['category_name'],
'reconciled' => $transaction['reconciled'],
'notes' => $transaction['notes'],
'tags' => $transaction['tags'],
'bill_id' => $this->stringFromArray($transaction, 'bill_id', null),
'bill_name' => $transaction['bill_name'],
'internal_reference' => $transaction['meta']['internal_reference'] ?? null,
'external_id' => $transaction['meta']['external_id'] ?? null,
'original_source' => $transaction['meta']['original_source'] ?? null,
'recurrence_id' => $transaction['meta']['recurrence_id'] ?? null,
'recurrence_total' => $recurrenceTotal,
'recurrence_count' => $recurrenceCount,
'external_url' => $transaction['meta']['external_url'] ?? null,
'import_hash_v2' => $transaction['meta']['import_hash_v2'] ?? null,
'reconciled' => $transaction['reconciled'],
'notes' => $transaction['notes'],
'tags' => $transaction['tags'],
'sepa_cc' => $transaction['meta']['sepa_cc'] ?? null,
'sepa_ct_op' => $transaction['meta']['sepa_ct_op'] ?? null,
'sepa_ct_id' => $transaction['meta']['sepa_ct_id'] ?? null,
'sepa_db' => $transaction['meta']['sepa_db'] ?? null,
'sepa_country' => $transaction['meta']['sepa_country'] ?? null,
'sepa_ep' => $transaction['meta']['sepa_ep'] ?? null,
'sepa_ci' => $transaction['meta']['sepa_ci'] ?? null,
'sepa_batch_id' => $transaction['meta']['sepa_batch_id'] ?? null,
'internal_reference' => $transaction['meta']['internal_reference'] ?? null,
'external_id' => $transaction['meta']['external_id'] ?? null,
'original_source' => $transaction['meta']['original_source'] ?? null,
'recurrence_id' => $transaction['meta']['recurrence_id'] ?? null,
'recurrence_total' => $recurrenceTotal,
'recurrence_count' => $recurrenceCount,
'bunq_payment_id' => $transaction['meta']['bunq_payment_id'] ?? null,
'external_url' => $transaction['meta']['external_url'] ?? null,
'import_hash_v2' => $transaction['meta']['import_hash_v2'] ?? null,
'interest_date' => array_key_exists('interest_date', $transaction['meta_date']) ? $transaction['meta_date']['interest_date']->toW3CString() : null,
'book_date' => array_key_exists('book_date', $transaction['meta_date']) ? $transaction['meta_date']['book_date']->toW3CString() : null,
'process_date' => array_key_exists('process_date', $transaction['meta_date']) ? $transaction['meta_date']['process_date']->toW3CString() : null,
'due_date' => array_key_exists('due_date', $transaction['meta_date']) ? $transaction['meta_date']['due_date']->toW3CString() : null,
'payment_date' => array_key_exists('payment_date', $transaction['meta_date']) ? $transaction['meta_date']['payment_date']->toW3CString() : null,
'invoice_date' => array_key_exists('invoice_date', $transaction['meta_date']) ? $transaction['meta_date']['invoice_date']->toW3CString() : null,
'sepa_cc' => $transaction['meta']['sepa_cc'] ?? null,
'sepa_ct_op' => $transaction['meta']['sepa_ct_op'] ?? null,
'sepa_ct_id' => $transaction['meta']['sepa_ct_id'] ?? null,
'sepa_db' => $transaction['meta']['sepa_db'] ?? null,
'sepa_country' => $transaction['meta']['sepa_country'] ?? null,
'sepa_ep' => $transaction['meta']['sepa_ep'] ?? null,
'sepa_ci' => $transaction['meta']['sepa_ci'] ?? null,
'sepa_batch_id' => $transaction['meta']['sepa_batch_id'] ?? null,
'interest_date' => array_key_exists('interest_date', $transaction['meta_date']) ? $transaction['meta_date']['interest_date']->toW3CString() : null,
'book_date' => array_key_exists('book_date', $transaction['meta_date']) ? $transaction['meta_date']['book_date']->toW3CString() : null,
'process_date' => array_key_exists('process_date', $transaction['meta_date']) ? $transaction['meta_date']['process_date']->toW3CString() : null,
'due_date' => array_key_exists('due_date', $transaction['meta_date']) ? $transaction['meta_date']['due_date']->toW3CString() : null,
'payment_date' => array_key_exists('payment_date', $transaction['meta_date']) ? $transaction['meta_date']['payment_date']->toW3CString() : null,
'invoice_date' => array_key_exists('invoice_date', $transaction['meta_date']) ? $transaction['meta_date']['invoice_date']->toW3CString() : null,
// location data
'longitude' => $transaction['location']['longitude'],
'latitude' => $transaction['location']['latitude'],
'zoom_level' => $transaction['location']['zoom_level'],
'has_attachments' => $transaction['attachment_count'] > 0,
'longitude' => $transaction['location']['longitude'],
'latitude' => $transaction['location']['latitude'],
'zoom_level' => $transaction['location']['zoom_level'],
'has_attachments' => $transaction['attachment_count'] > 0,
];
}
@@ -324,7 +325,7 @@ class TransactionGroupTransformer extends AbstractTransformer
$destination = $this->getDestinationTransaction($journal);
$type = $journal->transactionType->type;
$currency = $source->transactionCurrency;
$amount = app('steam')->bcround($this->getAmount($source->amount), $currency->decimal_places ?? 0);
$amount = Steam::bcround($this->getAmount($source->amount), $currency->decimal_places ?? 0);
$foreignAmount = $this->getForeignAmount($source->foreign_amount ?? null);
$metaFieldData = $this->groupRepos->getMetaFields($journal->id, $this->metaFields);
$metaDates = $this->getDates($this->groupRepos->getMetaDateFields($journal->id, $this->metaDateFields));
@@ -334,7 +335,7 @@ class TransactionGroupTransformer extends AbstractTransformer
$bill = $this->getBill($journal->bill);
if (null !== $foreignAmount && null !== $source->foreignCurrency) {
$foreignAmount = app('steam')->bcround($foreignAmount, $foreignCurrency['decimal_places'] ?? 0);
$foreignAmount = Steam::bcround($foreignAmount, $foreignCurrency['decimal_places'] ?? 0);
}
$longitude = null;
@@ -364,7 +365,7 @@ class TransactionGroupTransformer extends AbstractTransformer
'foreign_currency_symbol' => $foreignCurrency['symbol'],
'foreign_currency_decimal_places' => $foreignCurrency['decimal_places'],
'amount' => app('steam')->bcround($amount, $currency->decimal_places),
'amount' => Steam::bcround($amount, $currency->decimal_places),
'foreign_amount' => $foreignAmount,
'description' => $journal->description,
@@ -458,13 +459,13 @@ class TransactionGroupTransformer extends AbstractTransformer
private function getAmount(string $amount): string
{
return app('steam')->positive($amount);
return Steam::positive($amount);
}
private function getForeignAmount(?string $foreignAmount): ?string
{
if (null !== $foreignAmount && '' !== $foreignAmount && 0 !== bccomp('0', $foreignAmount)) {
return app('steam')->positive($foreignAmount);
return Steam::positive($foreignAmount);
}
return null;

View File

@@ -89,7 +89,7 @@ class UserGroupTransformer extends AbstractTransformer
foreach ($members as $member) {
$mail = $member['user_email'];
$new[$groupId][$mail] ??= [
'user_id' => $member['user_id'],
'user_id' => (string) $member['user_id'],
'user_email' => $member['user_email'],
'you' => $member['you'],
'roles' => [],

View File

@@ -224,9 +224,9 @@ trait TransactionValidation
/** @var AccountRepository $accountRepository */
$accountRepository = app(AccountRepositoryInterface::class);
$defaultCurrency = app('amount')->getPrimaryCurrency();
$sourceCurrency = $accountRepository->getAccountCurrency($source) ?? $defaultCurrency;
$destinationCurrency = $accountRepository->getAccountCurrency($destination) ?? $defaultCurrency;
$primaryCurrency = app('amount')->getPrimaryCurrency();
$sourceCurrency = $accountRepository->getAccountCurrency($source) ?? $primaryCurrency;
$destinationCurrency = $accountRepository->getAccountCurrency($destination) ?? $primaryCurrency;
// if both accounts have the same currency, continue.
if ($sourceCurrency->code === $destinationCurrency->code) {
Log::debug('Both accounts have the same currency, continue.');

10
composer.lock generated
View File

@@ -3711,16 +3711,16 @@
},
{
"name": "nesbot/carbon",
"version": "3.10.1",
"version": "3.10.2",
"source": {
"type": "git",
"url": "https://github.com/CarbonPHP/carbon.git",
"reference": "1fd1935b2d90aef2f093c5e35f7ae1257c448d00"
"reference": "76b5c07b8a9d2025ed1610e14cef1f3fd6ad2c24"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/CarbonPHP/carbon/zipball/1fd1935b2d90aef2f093c5e35f7ae1257c448d00",
"reference": "1fd1935b2d90aef2f093c5e35f7ae1257c448d00",
"url": "https://api.github.com/repos/CarbonPHP/carbon/zipball/76b5c07b8a9d2025ed1610e14cef1f3fd6ad2c24",
"reference": "76b5c07b8a9d2025ed1610e14cef1f3fd6ad2c24",
"shasum": ""
},
"require": {
@@ -3812,7 +3812,7 @@
"type": "tidelift"
}
],
"time": "2025-06-21T15:19:35+00:00"
"time": "2025-08-02T09:36:06+00:00"
},
{
"name": "nette/schema",

View File

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

14
package-lock.json generated
View File

@@ -3148,13 +3148,13 @@
"license": "MIT"
},
"node_modules/@types/node": {
"version": "24.1.0",
"resolved": "https://registry.npmjs.org/@types/node/-/node-24.1.0.tgz",
"integrity": "sha512-ut5FthK5moxFKH2T1CUOC6ctR67rQRvvHdFLCD2Ql6KXmMuCrjsSsRI9UsLCm9M18BMwClv4pn327UvB7eeO1w==",
"version": "24.2.0",
"resolved": "https://registry.npmjs.org/@types/node/-/node-24.2.0.tgz",
"integrity": "sha512-3xyG3pMCq3oYCNg7/ZP+E1ooTaGB4cG8JWRsqqOYQdbWNY4zbaV0Ennrd7stjiJEFZCaybcIgpTjJWHRfBSIDw==",
"dev": true,
"license": "MIT",
"dependencies": {
"undici-types": "~7.8.0"
"undici-types": "~7.10.0"
}
},
"node_modules/@types/node-forge": {
@@ -11327,9 +11327,9 @@
}
},
"node_modules/undici-types": {
"version": "7.8.0",
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.8.0.tgz",
"integrity": "sha512-9UJ2xGDvQ43tYyVMpuHlsgApydB8ZKfVYTsLDhXkFL/6gfkp+U8xTGdh8pMJv1SpZna0zxG1DwsKZsreLbXBxw==",
"version": "7.10.0",
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.10.0.tgz",
"integrity": "sha512-t5Fy/nfn+14LuOc2KNYg75vZqClpAiqscVvMygNnlsHBFpSXdJaYtXMcdNLpl/Qvc3P2cB3s6lOV51nqsFq4ag==",
"dev": true,
"license": "MIT"
},

View File

@@ -3,8 +3,8 @@
"administrations_page_title": "Finanzverwaltungen",
"administrations_index_menu": "Finanzverwaltung",
"expires_at": "G\u00fcltig bis",
"temp_administrations_introduction": "Firefly III will soon get the ability to manage multiple financial administrations. Right now, you only have the one. You can set the title of this administration and its primary currency. This replaces the previous setting where you would set your \"default currency\". This setting is now tied to the financial administration and can be different per administration.",
"administration_currency_form_help": "It may take a long time for the page to load if you change the primary currency because transaction may need to be converted to your (new) primary currency.",
"temp_administrations_introduction": "Firefly III erh\u00e4lt in K\u00fcrze die M\u00f6glichkeit, mehrere Finanzverwaltungen zu verwalten. Derzeit ist nur eine einzige verf\u00fcgbar. Sie k\u00f6nnen den Titel dieser Verwaltung und ihre Landesw\u00e4hrung festlegen. Dies ersetzt die bisherige Einstellung, bei der Sie Ihre \u201eStandardw\u00e4hrung\u201c festlegen mussten. Diese Einstellung ist nun an die Finanzverwaltung gebunden und kann je nach Verwaltung unterschiedlich sein.",
"administration_currency_form_help": "Das Laden der Seite kann lange dauern, wenn Sie die Hauptw\u00e4hrung \u00e4ndern, da Buchungen m\u00f6glicherweise in Ihre (neue) Hauptw\u00e4hrung umgerechnet werden m\u00fcssen.",
"administrations_page_edit_sub_title_js": "Finanzverwaltung \u201e{title}\u201c bearbeiten",
"table": "Tabelle",
"welcome_back": "\u00dcberblick",
@@ -102,7 +102,7 @@
"profile_oauth_client_secret_title": "Client Secret",
"profile_oauth_client_secret_expl": "Hier ist Ihr neuer pers\u00f6nlicher Zugangsschl\u00fcssel. Dies ist das einzige Mal, dass er angezeigt wird, also verlieren Sie ihn nicht! Sie k\u00f6nnen diesen Token jetzt verwenden, um API-Anfragen zu stellen.",
"profile_oauth_confidential": "Vertraulich",
"profile_oauth_confidential_help": "Require the client to authenticate with a secret. Confidential clients can hold credentials in a secure way without exposing them to unauthorized parties. Public applications, such as primary desktop or JavaScript SPA applications, are unable to hold secrets securely.",
"profile_oauth_confidential_help": "Verlangen Sie vom Client, sich mit einem Geheimnis zu authentifizieren. Vertrauliche Clients k\u00f6nnen Anmeldedaten auf sichere Weise speichern, ohne sie Unbefugten zug\u00e4nglich zu machen. \u00d6ffentliche Anwendungen, wie prim\u00e4re Desktop- oder JavaScript-SPA-Anwendungen, sind nicht f\u00e4hig, Geheimnisse sicher zu speichern.",
"multi_account_warning_unknown": "Abh\u00e4ngig von der Art der Buchung, die Sie anlegen, kann das Quell- und\/oder Zielkonto nachfolgender Aufteilungen durch das \u00fcberschrieben werden, was in der ersten Aufteilung der Buchung definiert wurde.",
"multi_account_warning_withdrawal": "Bedenken Sie, dass das Quellkonto nachfolgender Aufteilungen von dem, was in der ersten Aufteilung der Abhebung definiert ist, au\u00dfer Kraft gesetzt wird.",
"multi_account_warning_deposit": "Bedenken Sie, dass das Zielkonto nachfolgender Aufteilungen von dem, was in der ersten Aufteilung der Einnahmen definiert ist, au\u00dfer Kraft gesetzt wird.",
@@ -154,7 +154,7 @@
"url": "URL",
"active": "Aktiv",
"interest_date": "Zinstermin",
"administration_currency": "Primary currency",
"administration_currency": "Standardw\u00e4hrung",
"title": "Titel",
"date": "Datum",
"book_date": "Buchungsdatum",
@@ -174,7 +174,7 @@
"list": {
"title": "Titel",
"active": "Aktiv?",
"primary_currency": "Primary currency",
"primary_currency": "Standardw\u00e4hrung",
"trigger": "Ausl\u00f6ser",
"response": "Antwort",
"delivery": "Zustellung",

View File

@@ -3,8 +3,8 @@
"administrations_page_title": "Ustawienia finansowe",
"administrations_index_menu": "Ustawienia finansowe",
"expires_at": "Expires at",
"temp_administrations_introduction": "Firefly III will soon get the ability to manage multiple financial administrations. Right now, you only have the one. You can set the title of this administration and its primary currency. This replaces the previous setting where you would set your \"default currency\". This setting is now tied to the financial administration and can be different per administration.",
"administration_currency_form_help": "It may take a long time for the page to load if you change the primary currency because transaction may need to be converted to your (new) primary currency.",
"temp_administrations_introduction": "Firefly III wkr\u00f3tce uzyska mo\u017cliwo\u015b\u0107 zarz\u0105dzania wieloma ustawieniami finansowymi. W tej chwili jest tylko jedno domy\u015blne ustawienie. Mo\u017cesz ustawi\u0107 jego tytu\u0142 i walut\u0119 g\u0142\u00f3wn\u0105. To zast\u0119puje poprzednie ustawienia, w kt\u00f3rym mo\u017cna by\u0142o ustawi\u0107 \"domy\u015bln\u0105 walut\u0119\". Jest ona obecnie powi\u0105zana z wybranym ustawieniem finansowym i mo\u017ce by\u0107 zmienione w tej zak\u0142adce.",
"administration_currency_form_help": "Wczytywanie strony mo\u017ce zaj\u0105\u0107 du\u017co czasu, je\u015bli zmienisz walut\u0119 g\u0142\u00f3wn\u0105, poniewa\u017c transakcja mo\u017ce wymaga\u0107 przewalutowania na (now\u0105) walut\u0119 g\u0142\u00f3wn\u0105.",
"administrations_page_edit_sub_title_js": "Edytuj ustawienia finansowe \"{title}\"",
"table": "Tabela",
"welcome_back": "Co jest grane?",
@@ -102,7 +102,7 @@
"profile_oauth_client_secret_title": "Sekret klienta",
"profile_oauth_client_secret_expl": "Oto tw\u00f3j nowy sekret klienta. Jest to jedyny raz, gdy zostanie wy\u015bwietlony, wi\u0119c nie zgub go! Mo\u017cesz teraz u\u017cy\u0107 tego sekretu, aby wykona\u0107 zapytania API.",
"profile_oauth_confidential": "Poufne",
"profile_oauth_confidential_help": "Require the client to authenticate with a secret. Confidential clients can hold credentials in a secure way without exposing them to unauthorized parties. Public applications, such as primary desktop or JavaScript SPA applications, are unable to hold secrets securely.",
"profile_oauth_confidential_help": "Wymagaj od klienta uwierzytelnienia za pomoc\u0105 sekretu. Poufni klienci mog\u0105 przechowywa\u0107 po\u015bwiadczenia w bezpieczny spos\u00f3b bez nara\u017cania ich na dost\u0119p przez nieuprawnione strony. Publiczne aplikacje, takie jak natywne aplikacje desktopowe lub JavaScript SPA, nie s\u0105 w stanie bezpiecznie trzyma\u0107 sekret\u00f3w.",
"multi_account_warning_unknown": "W zale\u017cno\u015bci od rodzaju transakcji, kt\u00f3r\u0105 tworzysz, konto \u017ar\u00f3d\u0142owe i\/lub docelowe kolejnych podzia\u0142\u00f3w mo\u017ce zosta\u0107 ustawione na konto zdefiniowane w pierwszym podziale transakcji.",
"multi_account_warning_withdrawal": "Pami\u0119taj, \u017ce konto \u017ar\u00f3d\u0142owe kolejnych podzia\u0142\u00f3w zostanie ustawione na konto zdefiniowane w pierwszym podziale wyp\u0142aty.",
"multi_account_warning_deposit": "Pami\u0119taj, \u017ce konto docelowe kolejnych podzia\u0142\u00f3w zostanie ustawione na konto zdefiniowane w pierwszym podziale wp\u0142aty.",
@@ -154,7 +154,7 @@
"url": "URL",
"active": "Aktywny",
"interest_date": "Data odsetek",
"administration_currency": "Primary currency",
"administration_currency": "Waluta g\u0142\u00f3wna",
"title": "Tytu\u0142",
"date": "Data",
"book_date": "Data ksi\u0119gowania",
@@ -174,7 +174,7 @@
"list": {
"title": "Tytu\u0142",
"active": "Jest aktywny?",
"primary_currency": "Primary currency",
"primary_currency": "Waluta g\u0142\u00f3wna",
"trigger": "Wyzwalacz",
"response": "Odpowied\u017a",
"delivery": "Dor\u0119czenie",

View File

@@ -30,7 +30,7 @@ export default class Put {
* @returns {Promise<AxiosResponse<any>>}
*/
put(identifier, params) {
return api.put('/api/v2/accounts/' + identifier, params);
return api.put('/api/v1/accounts/' + identifier, params);
}
}

View File

@@ -29,7 +29,7 @@ export default class Get {
* @returns {Promise<AxiosResponse<any>>}
*/
list(params) {
return api.get('/api/v2/currencies', {params: params});
return api.get('/api/v1/currencies', {params: params});
}
}

View File

@@ -30,7 +30,7 @@ export default class Get {
* @returns {Promise<AxiosResponse<any>>}
*/
show(identifier, params) {
return api.get('/api/v2/user-groups/' + identifier, {params: params});
return api.get('/api/v1/user-groups/' + identifier, {params: params});
}
/**
@@ -39,7 +39,7 @@ export default class Get {
* @returns {Promise<AxiosResponse<any>>}
*/
index(params) {
return api.get('/api/v2/user-groups', {params: params});
return api.get('/api/v1/user-groups', {params: params});
}
}

View File

@@ -22,12 +22,12 @@ import {api} from "../../../../boot/axios";
export default class Post {
post(submission) {
let url = './api/v2/user-groups';
let url = './api/v1/user-groups';
return api.post(url, submission);
}
use(groupId) {
let url = './api/v2/user-groups/' + groupId + '/use';
let url = './api/v1/user-groups/' + groupId + '/use';
return api.post(url, {});
}
}

View File

@@ -22,7 +22,7 @@ import {api} from "../../../../boot/axios";
export default class Put {
put(submission, params) {
let url = '/api/v2/user-groups/' + parseInt(params.id);
let url = '/api/v1/user-groups/' + parseInt(params.id);
return api.put(url, submission);
}
}

View File

@@ -25,12 +25,12 @@ export default class Dashboard {
dashboard(start, end) {
let startStr = format(start, 'y-MM-dd');
let endStr = format(end, 'y-MM-dd');
return api.get('/api/v2/chart/account/dashboard', {params: {start: startStr, end: endStr}});
return api.get('/api/v1/chart/account/dashboard', {params: {start: startStr, end: endStr}});
}
expense(start, end) {
let startStr = format(start, 'y-MM-dd');
let endStr = format(end, 'y-MM-dd');
return api.get('/api/v2/chart/account/expense-dashboard', {params: {start: startStr, end: endStr}});
return api.get('/api/v1/chart/account/expense-dashboard', {params: {start: startStr, end: endStr}});
}
}

View File

@@ -25,6 +25,6 @@ export default class Dashboard {
dashboard(start, end) {
let startStr = format(start, 'y-MM-dd');
let endStr = format(end, 'y-MM-dd');
return api.get('/api/v2/chart/budget/dashboard', {params: {start: startStr, end: endStr}});
return api.get('/api/v1/chart/budget/dashboard', {params: {start: startStr, end: endStr}});
}
}

View File

@@ -25,6 +25,6 @@ export default class Dashboard {
dashboard(start, end) {
let startStr = format(start, 'y-MM-dd');
let endStr = format(end, 'y-MM-dd');
return api.get('/api/v2/chart/category/dashboard', {params: {start: startStr, end: endStr}});
return api.get('/api/v1/chart/category/dashboard', {params: {start: startStr, end: endStr}});
}
}

View File

@@ -31,7 +31,7 @@ export default class Get {
* @returns {Promise<AxiosResponse<any>>}
*/
show(identifier, params) {
return api.get('/api/v2/accounts/' + identifier, {params: params});
return api.get('/api/v1/accounts/' + identifier, {params: params});
}
/**
@@ -42,7 +42,7 @@ export default class Get {
index(params) {
// first, check API in some consistent manner.
// then, load if necessary.
const cacheKey = getCacheKey('/api/v2/accounts', params);
const cacheKey = getCacheKey('/api/v1/accounts', params);
const cacheValid = window.store.get('cacheValid');
let cachedData = window.store.get(cacheKey);
@@ -53,7 +53,7 @@ export default class Get {
// if not, store in cache and then return res.
return api.get('/api/v2/accounts', {params: params}).then(response => {
return api.get('/api/v1/accounts', {params: params}).then(response => {
console.log('Cache is invalid, return fresh.');
window.store.set(cacheKey, response.data);
return Promise.resolve({data: response.data.data, meta: response.data.meta});
@@ -77,6 +77,6 @@ export default class Get {
newParams.end = format(params.end, 'y-MM-dd');
}
return api.get('/api/v2/accounts/' + identifier + '/transactions', {params: newParams});
return api.get('/api/v1/accounts/' + identifier + '/transactions', {params: newParams});
}
}

View File

@@ -30,7 +30,7 @@ export default class Put {
* @returns {Promise<AxiosResponse<any>>}
*/
put(identifier, params) {
return api.put('/api/v2/accounts/' + identifier, params);
return api.put('/api/v1/accounts/' + identifier, params);
}
}

View File

@@ -29,7 +29,7 @@ export default class Get {
* @returns {Promise<AxiosResponse<any>>}
*/
list(params) {
return api.get('/api/v2/budgets', {params: params});
return api.get('/api/v1/budgets', {params: params});
}
}

View File

@@ -29,7 +29,7 @@ export default class Get {
* @returns {Promise<AxiosResponse<any>>}
*/
list(params) {
return api.get('/api/v2/currencies', {params: params});
return api.get('/api/v1/currencies', {params: params});
}
}

View File

@@ -29,7 +29,7 @@ export default class Get {
* @returns {Promise<AxiosResponse<any>>}
*/
list(params) {
return api.get('/api/v2/piggy-banks', {params: params});
return api.get('/api/v1/piggy-banks', {params: params});
}
}

View File

@@ -29,14 +29,14 @@ export default class Get {
* @returns {Promise<AxiosResponse<any>>}
*/
list(params) {
return api.get('/api/v2/subscriptions', {params: params});
return api.get('/api/v1/subscriptions', {params: params});
}
paid(params) {
return api.get('/api/v2/subscriptions/sum/paid', {params: params});
return api.get('/api/v1/subscriptions/sum/paid', {params: params});
}
unpaid(params) {
return api.get('/api/v2/subscriptions/sum/unpaid', {params: params});
return api.get('/api/v1/subscriptions/sum/unpaid', {params: params});
}
}

View File

@@ -29,12 +29,12 @@ export default class Get {
* @returns {Promise<AxiosResponse<any>>}
*/
list(params) {
return api.get('/api/v2/transactions', {params: params});
return api.get('/api/v1/transactions', {params: params});
}
infiniteList(params) {
return api.get('/api/v2/infinite/transactions', {params: params});
return api.get('/api/v1/infinite/transactions', {params: params});
}
show(id, params){
return api.get('/api/v2/transactions/' + id, {params: params});
return api.get('/api/v1/transactions/' + id, {params: params});
}
}

View File

@@ -22,7 +22,7 @@ import {api} from "../../../../boot/axios";
export default class Post {
post(submission) {
let url = '/api/v2/transactions';
let url = '/api/v1/transactions';
return api.post(url, submission);
}
}

View File

@@ -22,7 +22,7 @@ import {api} from "../../../../boot/axios";
export default class Put {
put(submission, params) {
let url = '/api/v2/transactions/' + parseInt(params.id);
let url = '/api/v1/transactions/' + parseInt(params.id);
return api.put(url, submission);
}
}

View File

@@ -30,7 +30,7 @@ export default class Get {
* @returns {Promise<AxiosResponse<any>>}
*/
show(identifier, params) {
return api.get('/api/v2/user-groups/' + identifier, {params: params});
return api.get('/api/v1/user-groups/' + identifier, {params: params});
}
/**
@@ -39,7 +39,7 @@ export default class Get {
* @returns {Promise<AxiosResponse<any>>}
*/
index(params) {
return api.get('/api/v2/user-groups', {params: params});
return api.get('/api/v1/user-groups', {params: params});
}
}

View File

@@ -22,12 +22,12 @@ import {api} from "../../../../boot/axios";
export default class Post {
post(submission) {
let url = './api/v2/user-groups';
let url = './api/v1/user-groups';
return api.post(url, submission);
}
use(groupId) {
let url = './api/v2/user-groups/' + groupId + '/use';
let url = './api/v1/user-groups/' + groupId + '/use';
return api.post(url, {});
}
}

View File

@@ -22,7 +22,7 @@ import {api} from "../../../../boot/axios";
export default class Put {
put(submission, params) {
let url = '/api/v2/user-groups/' + parseInt(params.id);
let url = '/api/v1/user-groups/' + parseInt(params.id);
return api.put(url, submission);
}
}

View File

@@ -23,6 +23,6 @@ import {api} from "../../../boot/axios.js";
export default class Summary {
get(start, end, code) {
return api.get('/api/v2/summary/basic', {params: {start: start, end: end, code: code}});
return api.get('/api/v1/summary/basic', {params: {start: start, end: end, code: code}});
}
}

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