Compare commits

..

23 Commits

Author SHA1 Message Date
github-actions
6c655634bc Auto commit for release 'develop' on 2024-12-28 2024-12-28 10:58:01 +01:00
James Cole
f2166b97b8 Various fixes for sqlite databases 2024-12-28 10:52:46 +01:00
James Cole
da88e02be0 Catch exception. 2024-12-28 07:47:32 +01:00
James Cole
0d56b7d251 Ignore error. Must test more with sqlite. 2024-12-28 07:39:39 +01:00
James Cole
0a089efcac Clean up commands. 2024-12-28 07:35:20 +01:00
James Cole
89ab360391 Remove reference to cacheable models 2024-12-28 07:10:33 +01:00
James Cole
2bd97d9a99 Remove references to laravel json api 2024-12-28 07:09:25 +01:00
James Cole
103b9056e4 Expand changelog, remove unused packages. 2024-12-28 06:55:17 +01:00
github-actions
23c4352c18 Auto commit for release 'develop' on 2024-12-27 2024-12-27 22:03:16 +01:00
James Cole
2dddaa36d5 Actions will default to php 8.4 2024-12-27 19:51:20 +01:00
James Cole
8ab7cf2388 Merge branch 'v6.2' into develop
# Conflicts:
#	composer.lock
#	config/firefly.php
#	package-lock.json
2024-12-27 19:50:05 +01:00
James Cole
f191086adb Enable cache again. 2024-12-27 19:47:35 +01:00
James Cole
68b446db18 Final changes. 2024-12-27 19:46:40 +01:00
James Cole
3d49d81856 Expand some multi currency things. 2024-12-27 16:36:01 +01:00
James Cole
96493425d1 Convert more of the budget view to native currency. 2024-12-27 13:42:48 +01:00
James Cole
cea52c0ac7 Merge pull request #9571 from firefly-iii/dependabot/composer/develop/phpunit/phpunit-10.5.40
Bump phpunit/phpunit from 10.5.39 to 10.5.40
2024-12-23 07:02:55 +01:00
James Cole
1b33ff9c25 Merge pull request #9573 from firefly-iii/dependabot/npm_and_yarn/develop/i18next-24.2.0
Bump i18next from 24.1.2 to 24.2.0
2024-12-23 07:02:41 +01:00
James Cole
594ba205bb Merge pull request #9574 from firefly-iii/dependabot/npm_and_yarn/develop/vite-6.0.5
Bump vite from 6.0.3 to 6.0.5
2024-12-23 07:01:29 +01:00
dependabot[bot]
495f5c71c3 Bump vite from 6.0.3 to 6.0.5
Bumps [vite](https://github.com/vitejs/vite/tree/HEAD/packages/vite) from 6.0.3 to 6.0.5.
- [Release notes](https://github.com/vitejs/vite/releases)
- [Changelog](https://github.com/vitejs/vite/blob/main/packages/vite/CHANGELOG.md)
- [Commits](https://github.com/vitejs/vite/commits/v6.0.5/packages/vite)

---
updated-dependencies:
- dependency-name: vite
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-12-23 03:12:47 +00:00
dependabot[bot]
7e80f607b7 Bump i18next from 24.1.2 to 24.2.0
Bumps [i18next](https://github.com/i18next/i18next) from 24.1.2 to 24.2.0.
- [Release notes](https://github.com/i18next/i18next/releases)
- [Changelog](https://github.com/i18next/i18next/blob/master/CHANGELOG.md)
- [Commits](https://github.com/i18next/i18next/compare/v24.1.2...v24.2.0)

---
updated-dependencies:
- dependency-name: i18next
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-12-23 03:12:31 +00:00
github-actions
d93732e451 Auto commit for release 'develop' on 2024-12-23 2024-12-23 04:12:14 +01:00
dependabot[bot]
1b57bc7889 Bump phpunit/phpunit from 10.5.39 to 10.5.40
Bumps [phpunit/phpunit](https://github.com/sebastianbergmann/phpunit) from 10.5.39 to 10.5.40.
- [Release notes](https://github.com/sebastianbergmann/phpunit/releases)
- [Changelog](https://github.com/sebastianbergmann/phpunit/blob/10.5.40/ChangeLog-10.5.md)
- [Commits](https://github.com/sebastianbergmann/phpunit/compare/10.5.39...10.5.40)

---
updated-dependencies:
- dependency-name: phpunit/phpunit
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-12-23 03:01:52 +00:00
github-actions
e2f1fc307f Auto commit for release 'develop' on 2024-12-18 2024-12-18 16:52:40 +01:00
97 changed files with 433 additions and 2606 deletions

View File

@@ -1369,12 +1369,12 @@
},
"type": "library",
"extra": {
"thanks": {
"url": "https://github.com/symfony/contracts",
"name": "symfony/contracts"
},
"branch-alias": {
"dev-main": "3.5-dev"
},
"thanks": {
"name": "symfony/contracts",
"url": "https://github.com/symfony/contracts"
}
},
"autoload": {
@@ -1517,12 +1517,12 @@
},
"type": "library",
"extra": {
"thanks": {
"url": "https://github.com/symfony/contracts",
"name": "symfony/contracts"
},
"branch-alias": {
"dev-main": "3.5-dev"
},
"thanks": {
"name": "symfony/contracts",
"url": "https://github.com/symfony/contracts"
}
},
"autoload": {
@@ -2329,12 +2329,12 @@
},
"type": "library",
"extra": {
"thanks": {
"url": "https://github.com/symfony/contracts",
"name": "symfony/contracts"
},
"branch-alias": {
"dev-main": "3.5-dev"
},
"thanks": {
"name": "symfony/contracts",
"url": "https://github.com/symfony/contracts"
}
},
"autoload": {

View File

@@ -10,7 +10,7 @@ on:
phpversion:
description: 'PHP version'
required: true
default: '8.3'
default: '8.4'
schedule:
- cron: '0 3 * * MON'

View File

@@ -19,7 +19,7 @@ jobs:
- name: Setup PHP with Xdebug
uses: shivammathur/setup-php@v2
with:
php-version: '8.3'
php-version: '8.4'
coverage: xdebug
extensions: >-
bcmath

View File

@@ -28,6 +28,8 @@ use Carbon\Carbon;
use Carbon\Exceptions\InvalidDateException;
use Carbon\Exceptions\InvalidFormatException;
use FireflyIII\Models\Preference;
use FireflyIII\Support\Facades\Amount;
use FireflyIII\Support\Facades\Steam;
use FireflyIII\User;
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
use Illuminate\Foundation\Bus\DispatchesJobs;
@@ -68,8 +70,8 @@ abstract class Controller extends BaseController
function ($request, $next) {
$this->parameters = $this->getParameters();
if (auth()->check()) {
$language = app('steam')->getLanguage();
$this->convertToNative = app('preferences')->get('convert_to_native', false)->data;
$language = Steam::getLanguage();
$this->convertToNative = Amount::convertToNative();
app()->setLocale($language);
}

View File

@@ -123,8 +123,8 @@ class BasicController extends Controller
private function getBalanceInformation(Carbon $start, Carbon $end): array
{
// some config settings
$convertToNative = app('preferences')->get('convert_to_native', false)->data;
$default = app('amount')->getDefaultCurrency();
$convertToNative = Amount::convertToNative();
$default = Amount::getDefaultCurrency();
// prep some arrays:
$incomes = [];
$expenses = [];

View File

@@ -1,113 +0,0 @@
<?php
/*
* AccountController.php
* Copyright (c) 2024 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\V2\Controllers\JsonApi;
use FireflyIII\Http\Controllers\Controller;
use FireflyIII\JsonApi\V2\Accounts\AccountCollectionQuery;
use FireflyIII\JsonApi\V2\Accounts\AccountSchema;
use FireflyIII\JsonApi\V2\Accounts\AccountSingleQuery;
use FireflyIII\Models\Account;
use Illuminate\Contracts\Support\Responsable;
use Illuminate\Http\Response;
use Illuminate\Support\Facades\Log;
use LaravelJsonApi\Core\Responses\DataResponse;
use LaravelJsonApi\Laravel\Http\Controllers\Actions;
/**
* Class AccountController
*
* This class handles api/v2 requests for accounts.
* Most stuff is default stuff.
*/
class AccountController extends Controller
{
use Actions\AttachRelationship;
use Actions\Destroy;
use Actions\DetachRelationship;
use Actions\FetchMany;
// use Actions\FetchOne;
use Actions\FetchRelated;
use Actions\FetchRelationship;
use Actions\Store;
use Actions\Update;
use Actions\UpdateRelationship;
/**
* Fetch zero to many JSON API resources.
*
* @return Responsable|Response
*/
public function index(AccountSchema $schema, AccountCollectionQuery $request)
{
Log::debug(__METHOD__);
$models = $schema
->repository()
->queryAll()
->withRequest($request)
->get()
;
// do something custom...
return new DataResponse($models);
}
/**
* Fetch zero to one JSON API resource by id.
*
* @return Responsable|Response
*/
public function show(AccountSchema $schema, AccountSingleQuery $request, Account $account)
{
Log::debug(__METHOD__);
$model = $schema->repository()
->queryOne($account)
->withRequest($request)
->first()
;
Log::debug(sprintf('%s again!', __METHOD__));
// do something custom...
return new DataResponse($model);
}
// public function readAccountBalances(AnonymousQuery $query, AccountBalanceSchema $schema, Account $account): Responsable
// {
// $schema = JsonApi::server()->schemas()->schemaFor('account-balances');
//
// $models = $schema
// ->repository()
// ->queryAll()
// ->withRequest($query)
// ->withAccount($account)
// ->get()
// ;
//
// return DataResponse::make($models);
// }
}

View File

@@ -59,6 +59,11 @@ class CorrectsNativeAmounts extends Command
*/
public function handle(): int
{
if (!config('cer.enabled')) {
$this->friendlyInfo('This command will not run because currency exchange rates are disabled.');
return 0;
}
Log::debug('Will update all native amounts. This may take some time.');
$this->friendlyWarning('Recalculating native amounts for all objects. This may take some time!');
@@ -124,8 +129,8 @@ class CorrectsNativeAmounts extends Command
foreach ($piggyBank->accounts as $account) {
$account->pivot->native_current_amount = null;
if (0 !== bccomp($account->pivot->current_amount, '0')) {
$account->pivot->native_current_amount = $converter->convert($piggyBank->transactionCurrency, $currency, today(), $account->pivot->current_amount);
if (0 !== bccomp((string) $account->pivot->current_amount, '0')) {
$account->pivot->native_current_amount = $converter->convert($piggyBank->transactionCurrency, $currency, today(), (string) $account->pivot->current_amount);
}
$account->pivot->save();
}

View File

@@ -76,9 +76,6 @@ class AddsTransactionIdentifiers extends Command
$this->updateJournalIdentifiers($journal);
}
if (0 === $this->count) {
$this->friendlyPositive('All split journal transaction identifiers are OK.');
}
if (0 !== $this->count) {
$this->friendlyInfo(sprintf('Fixed %d split journal transaction identifier(s).', $this->count));
}

View File

@@ -70,7 +70,6 @@ class RemovesDatabaseDecryption extends Command
private function decryptTable(string $table, array $fields): void
{
if ($this->isDecrypted($table)) {
$this->friendlyInfo(sprintf('No decryption required for table "%s".', $table));
return;
}

View File

@@ -62,9 +62,6 @@ class UpgradesAccountCurrencies extends Command
}
$this->updateAccountCurrencies();
if (0 === $this->count) {
$this->friendlyPositive('All account currencies are OK.');
}
if (0 !== $this->count) {
$this->friendlyInfo(sprintf('Corrected %d account(s).', $this->count));
}

View File

@@ -73,9 +73,6 @@ class UpgradesAccountMetaData extends Command
$this->markAsExecuted();
if (0 === $count) {
$this->friendlyPositive('All account meta is OK.');
}
if (0 !== $count) {
$this->friendlyInfo(sprintf('Renamed %d account meta entries (entry).', $count));
}

View File

@@ -79,9 +79,6 @@ class UpgradesAttachments extends Command
++$count;
}
}
if (0 === $count) {
$this->friendlyPositive('All attachments are OK.');
}
if (0 !== $count) {
$this->friendlyInfo(sprintf('Updated %d attachment(s).', $count));
}

View File

@@ -73,9 +73,6 @@ class UpgradesBillsToRules extends Command
$this->migrateUser($user);
}
if (0 === $this->count) {
$this->friendlyPositive('All bills are OK.');
}
if (0 !== $this->count) {
$this->friendlyInfo(sprintf('Verified and fixed %d bill(s).', $this->count));
}

View File

@@ -77,9 +77,6 @@ class UpgradesBudgetLimits extends Command
}
}
}
if (0 === $count) {
$this->friendlyPositive('All budget limits are OK.');
}
$this->markAsExecuted();
return 0;

View File

@@ -55,7 +55,6 @@ class UpgradesCreditCardLiabilities extends Command
$ccType = AccountType::where('type', AccountType::CREDITCARD)->first();
$debtType = AccountType::where('type', AccountType::DEBT)->first();
if (null === $ccType || null === $debtType) {
$this->friendlyPositive('No incorrectly stored credit card liabilities.');
$this->markAsExecuted();
return 0;
@@ -73,9 +72,6 @@ class UpgradesCreditCardLiabilities extends Command
'Credit card liability types are no longer supported and have been converted to generic debts. See: https://bit.ly/FF3-credit-cards'
);
}
if (0 === $accounts->count()) {
$this->friendlyPositive('No incorrectly stored credit card liabilities.');
}
$this->markAsExecuted();
return 0;

View File

@@ -57,7 +57,6 @@ class UpgradesDatabase extends Command
'upgrade:480-account-meta',
'upgrade:481-recurrence-meta',
'upgrade:500-tag-locations',
'upgrade:550-recurrence-type',
'upgrade:560-liabilities',
'upgrade:600-liabilities',
'upgrade:550-budget-limit-periods',

View File

@@ -68,7 +68,7 @@ class UpgradesJournalMetaData extends Command
private function isMigrated(): bool
{
$configVar = app('fireflyconfig')->get(self::CONFIG_NAME, false);
$configVar = app('fireflyconfig')->get(UpgradesToGroups::CONFIG_NAME, false);
return (bool) $configVar->data;
}

View File

@@ -72,9 +72,6 @@ class UpgradesJournalNotes extends Command
++$count;
}
if (0 === $count) {
$this->friendlyPositive('No notes to migrate.');
}
if (0 !== $count) {
$this->friendlyInfo(sprintf('Migrated %d note(s).', $count));
}

View File

@@ -52,9 +52,6 @@ class UpgradesRecurrenceMetaData extends Command
}
$count = $this->migrateMetaData();
if (0 === $count) {
$this->friendlyPositive('No recurrence meta data migrated.');
}
if ($count > 0) {
$this->friendlyInfo(sprintf('Migrated %d meta data entries', $count));
}

View File

@@ -1,67 +0,0 @@
<?php
/*
* MigrateRecurrenceType.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\Console\Commands\Upgrade;
use FireflyIII\Console\Commands\ShowsFriendlyMessages;
use Illuminate\Console\Command;
class UpgradesRecurrenceType extends Command
{
use ShowsFriendlyMessages;
public const string CONFIG_NAME = '550_migrate_recurrence_type';
protected $description = 'Migrate transaction type of recurring transaction.';
protected $signature = 'upgrade:550-recurrence-type {--F|force : Force the execution of this command.}';
/**
* Execute the console command.
*/
public function handle(): int
{
if ($this->isExecuted() && true !== $this->option('force')) {
$this->friendlyInfo('This command has already been executed.');
return 0;
}
$this->friendlyWarning('This command has been disabled.');
$this->markAsExecuted();
return 0;
}
private function isExecuted(): bool
{
$configVar = app('fireflyconfig')->get(self::CONFIG_NAME, false);
return (bool) $configVar?->data;
}
private function markAsExecuted(): void
{
app('fireflyconfig')->set(self::CONFIG_NAME, true);
}
}

View File

@@ -72,9 +72,6 @@ class UpgradesToGroups extends Command
if (0 !== $this->count) {
$this->friendlyInfo(sprintf('Migrated %d transaction journal(s).', $this->count));
}
if (0 === $this->count) {
$this->friendlyPositive('No journals to migrate to groups.');
}
$this->markAsMigrated();
return 0;
@@ -363,9 +360,6 @@ class UpgradesToGroups extends Command
$this->giveGroup($array);
}
}
if (0 === $total) {
$this->friendlyPositive('No need to convert transaction journals.');
}
}
private function giveGroup(array $array): void

View File

@@ -68,15 +68,10 @@ class UpgradesTransferCurrencies extends Command
$this->startUpdateRoutine();
$this->markAsExecuted();
if (0 === $this->count) {
$this->friendlyPositive('All transfers have correct currency information.');
return 0;
if ($this->count > 0) {
$this->friendlyInfo(sprintf('Verified currency information of %d transfer(s).', $this->count));
}
$this->friendlyInfo(sprintf('Verified currency information of %d transfer(s).', $this->count));
return 0;
}

View File

@@ -36,8 +36,6 @@ use Illuminate\Session\TokenMismatchException;
use Illuminate\Support\Arr;
use Illuminate\Validation\ValidationException as LaravelValidationException;
use Laravel\Passport\Exceptions\OAuthServerException as LaravelOAuthException;
use LaravelJsonApi\Core\Exceptions\JsonApiException;
use LaravelJsonApi\Exceptions\ExceptionParser;
use League\OAuth2\Server\Exception\OAuthServerException;
use Symfony\Component\HttpFoundation\Exception\SuspiciousOperationException;
use Symfony\Component\HttpFoundation\Response;
@@ -65,18 +63,12 @@ class Handler extends ExceptionHandler
HttpException::class,
SuspiciousOperationException::class,
BadHttpHeaderException::class,
JsonApiException::class,
];
/**
* Register the exception handling callbacks for the application.
*/
public function register(): void
{
$this->renderable(
ExceptionParser::make()->renderable()
);
}
public function register(): void {}
/**
* Render an exception into an HTTP response. It's complex but lucky for us, we never use it because

View File

@@ -27,6 +27,7 @@ namespace FireflyIII\Handlers\Observer;
use FireflyIII\Models\Account;
use FireflyIII\Models\PiggyBank;
use FireflyIII\Repositories\UserGroups\Account\AccountRepositoryInterface;
use FireflyIII\Support\Facades\Amount;
use FireflyIII\Support\Http\Api\ExchangeRateConverter;
use Illuminate\Support\Facades\Log;
@@ -43,6 +44,9 @@ class AccountObserver
private function updateNativeAmount(Account $account): void
{
if (!Amount::convertToNative($account->user)) {
return;
}
$userCurrency = app('amount')->getDefaultCurrencyByUserGroup($account->user->userGroup);
$repository = app(AccountRepositoryInterface::class);
$currency = $repository->getAccountCurrency($account);

View File

@@ -25,6 +25,7 @@ declare(strict_types=1);
namespace FireflyIII\Handlers\Observer;
use FireflyIII\Models\AutoBudget;
use FireflyIII\Support\Facades\Amount;
use FireflyIII\Support\Http\Api\ExchangeRateConverter;
use Illuminate\Support\Facades\Log;
@@ -44,6 +45,9 @@ class AutoBudgetObserver
private function updateNativeAmount(AutoBudget $autoBudget): void
{
if (!Amount::convertToNative($autoBudget->budget->user)) {
return;
}
$userCurrency = app('amount')->getDefaultCurrencyByUserGroup($autoBudget->budget->user->userGroup);
$autoBudget->native_amount = null;
if ($autoBudget->transactionCurrency->id !== $userCurrency->id) {

View File

@@ -25,6 +25,7 @@ declare(strict_types=1);
namespace FireflyIII\Handlers\Observer;
use FireflyIII\Models\AvailableBudget;
use FireflyIII\Support\Facades\Amount;
use FireflyIII\Support\Http\Api\ExchangeRateConverter;
use Illuminate\Support\Facades\Log;
@@ -44,6 +45,9 @@ class AvailableBudgetObserver
private function updateNativeAmount(AvailableBudget $availableBudget): void
{
if (!Amount::convertToNative($availableBudget->user)) {
return;
}
$userCurrency = app('amount')->getDefaultCurrencyByUserGroup($availableBudget->user->userGroup);
$availableBudget->native_amount = null;
if ($availableBudget->transactionCurrency->id !== $userCurrency->id) {

View File

@@ -24,6 +24,7 @@ declare(strict_types=1);
namespace FireflyIII\Handlers\Observer;
use FireflyIII\Models\Bill;
use FireflyIII\Support\Facades\Amount;
use FireflyIII\Support\Http\Api\ExchangeRateConverter;
use Illuminate\Support\Facades\Log;
@@ -55,6 +56,9 @@ class BillObserver
private function updateNativeAmount(Bill $bill): void
{
if (!Amount::convertToNative($bill->user)) {
return;
}
$userCurrency = app('amount')->getDefaultCurrencyByUserGroup($bill->user->userGroup);
$bill->native_amount_min = null;
$bill->native_amount_max = null;

View File

@@ -25,6 +25,7 @@ declare(strict_types=1);
namespace FireflyIII\Handlers\Observer;
use FireflyIII\Models\BudgetLimit;
use FireflyIII\Support\Facades\Amount;
use FireflyIII\Support\Http\Api\ExchangeRateConverter;
use Illuminate\Support\Facades\Log;
@@ -44,6 +45,9 @@ class BudgetLimitObserver
private function updateNativeAmount(BudgetLimit $budgetLimit): void
{
if (!Amount::convertToNative($budgetLimit->budget->user)) {
return;
}
$userCurrency = app('amount')->getDefaultCurrencyByUserGroup($budgetLimit->budget->user->userGroup);
$budgetLimit->native_amount = null;
if ($budgetLimit->transactionCurrency->id !== $userCurrency->id) {

View File

@@ -25,6 +25,7 @@ declare(strict_types=1);
namespace FireflyIII\Handlers\Observer;
use FireflyIII\Models\PiggyBankEvent;
use FireflyIII\Support\Facades\Amount;
use FireflyIII\Support\Http\Api\ExchangeRateConverter;
use Illuminate\Support\Facades\Log;
@@ -44,6 +45,9 @@ class PiggyBankEventObserver
private function updateNativeAmount(PiggyBankEvent $event): void
{
if (!Amount::convertToNative($event->piggyBank->accounts()->first()->user)) {
return;
}
$userCurrency = app('amount')->getDefaultCurrencyByUserGroup($event->piggyBank->accounts()->first()->user->userGroup);
$event->native_amount = null;
if ($event->piggyBank->transactionCurrency->id !== $userCurrency->id) {

View File

@@ -78,6 +78,7 @@ class PiggyBankObserver
if ($piggyBank->transactionCurrency->id !== $userCurrency->id) {
$converter = new ExchangeRateConverter();
$converter->setIgnoreSettings(true);
$converter->setUserGroup($group);
$piggyBank->native_target_amount = $converter->convert($piggyBank->transactionCurrency, $userCurrency, today(), $piggyBank->target_amount);
}
$piggyBank->saveQuietly();

View File

@@ -24,6 +24,7 @@ declare(strict_types=1);
namespace FireflyIII\Handlers\Observer;
use FireflyIII\Models\Transaction;
use FireflyIII\Support\Facades\Amount;
use FireflyIII\Support\Http\Api\ExchangeRateConverter;
use FireflyIII\Support\Models\AccountBalanceCalculator;
use Illuminate\Support\Facades\Log;
@@ -55,7 +56,7 @@ class TransactionObserver
public function updated(Transaction $transaction): void
{
Log::debug('Observe "updated" of a transaction.');
// Log::debug('Observe "updated" of a transaction.');
if (config('firefly.feature_flags.running_balance_column') && self::$recalculate) {
if (1 === bccomp($transaction->amount, '0')) {
Log::debug('Trigger recalculateForJournal');
@@ -67,6 +68,9 @@ class TransactionObserver
private function updateNativeAmount(Transaction $transaction): void
{
if (!Amount::convertToNative($transaction->transactionJournal->user)) {
return;
}
$userCurrency = app('amount')->getDefaultCurrencyByUserGroup($transaction->transactionJournal->user->userGroup);
$transaction->native_amount = null;
$transaction->native_foreign_amount = null;

View File

@@ -66,7 +66,7 @@ class NetWorth implements NetWorthInterface
public function byAccounts(Collection $accounts, Carbon $date): array
{
// start in the past, end in the future? use $date
$convertToNative = app('preferences')->get('convert_to_native', false)->data;
$convertToNative = Amount::convertToNative();
$ids = implode(',', $accounts->pluck('id')->toArray());
$cache = new CacheProperties();
$cache->addProperty($date);
@@ -74,7 +74,7 @@ class NetWorth implements NetWorthInterface
$cache->addProperty('net-worth-by-accounts');
$cache->addProperty($ids);
if ($cache->has()) {
// return $cache->get();
return $cache->get();
}
Log::debug(sprintf('Now in byAccounts("%s", "%s")', $ids, $date->format('Y-m-d H:i:s')));
$default = Amount::getDefaultCurrency();

View File

@@ -90,6 +90,7 @@ class EditController extends Controller
$latitude = null !== $location ? $location->latitude : config('firefly.default_location.latitude');
$longitude = null !== $location ? $location->longitude : config('firefly.default_location.longitude');
$zoomLevel = null !== $location ? $location->zoom_level : config('firefly.default_location.zoom_level');
$canEditCurrency = 0 === $account->piggyBanks()->count();
$hasLocation = null !== $location;
$locations = [
'location' => [
@@ -162,7 +163,7 @@ class EditController extends Controller
$request->session()->flash('preFilled', $preFilled);
return view('accounts.edit', compact('account', 'currency', 'showNetWorth', 'subTitle', 'subTitleIcon', 'locations', 'liabilityDirections', 'objectType', 'roles', 'preFilled', 'liabilityTypes', 'interestPeriods'));
return view('accounts.edit', compact('account', 'currency', 'canEditCurrency', 'showNetWorth', 'subTitle', 'subTitleIcon', 'locations', 'liabilityDirections', 'objectType', 'roles', 'preFilled', 'liabilityTypes', 'interestPeriods'));
}
/**

View File

@@ -36,6 +36,7 @@ use FireflyIII\Repositories\Budget\BudgetLimitRepositoryInterface;
use FireflyIII\Repositories\Budget\BudgetRepositoryInterface;
use FireflyIII\Repositories\Budget\OperationsRepositoryInterface;
use FireflyIII\Repositories\UserGroups\Currency\CurrencyRepositoryInterface;
use FireflyIII\Support\Http\Api\ExchangeRateConverter;
use FireflyIII\Support\Http\Controllers\DateCalculation;
use Illuminate\Contracts\View\Factory;
use Illuminate\Http\JsonResponse;
@@ -161,6 +162,7 @@ class IndexController extends Controller
private function getAllAvailableBudgets(Carbon $start, Carbon $end): array
{
$converter = new ExchangeRateConverter();
// get all available budgets.
$ab = $this->abRepository->get($start, $end);
$availableBudgets = [];
@@ -168,18 +170,22 @@ class IndexController extends Controller
// for each, complement with spent amount:
/** @var AvailableBudget $entry */
foreach ($ab as $entry) {
$array = $entry->toArray();
$array['start_date'] = $entry->start_date;
$array['end_date'] = $entry->end_date;
$array = $entry->toArray();
$array['start_date'] = $entry->start_date;
$array['end_date'] = $entry->end_date;
// spent in period:
$spentArr = $this->opsRepository->sumExpenses($entry->start_date, $entry->end_date, null, null, $entry->transactionCurrency);
$array['spent'] = $spentArr[$entry->transaction_currency_id]['sum'] ?? '0';
$spentArr = $this->opsRepository->sumExpenses($entry->start_date, $entry->end_date, null, null, $entry->transactionCurrency);
$array['spent'] = $spentArr[$entry->transaction_currency_id]['sum'] ?? '0';
$array['native_spent'] = $this->convertToNative && $entry->transaction_currency_id !== $this->defaultCurrency->id ? $converter->convert($entry->transactionCurrency, $this->defaultCurrency, $entry->start_date, $array['spent']) : null;
// budgeted in period:
$budgeted = $this->blRepository->budgeted($entry->start_date, $entry->end_date, $entry->transactionCurrency);
$array['budgeted'] = $budgeted;
$availableBudgets[] = $array;
$budgeted = $this->blRepository->budgeted($entry->start_date, $entry->end_date, $entry->transactionCurrency);
$array['budgeted'] = $budgeted;
$array['native_budgeted'] = $this->convertToNative && $entry->transaction_currency_id !== $this->defaultCurrency->id ? $converter->convert($entry->transactionCurrency, $this->defaultCurrency, $entry->start_date, $budgeted) : null;
// this time, because of complex sums, use the currency converter.
$availableBudgets[] = $array;
unset($spentArr);
}

View File

@@ -426,7 +426,7 @@ class AccountController extends Controller
$cache->addProperty($this->convertToNative);
$cache->addProperty($account->id);
if ($cache->has()) {
// return response()->json($cache->get());
return response()->json($cache->get());
}
// collect and filter balances for the entire period.

View File

@@ -111,7 +111,7 @@ class BillController extends Controller
$cache->addProperty($bill->id);
$cache->addProperty($this->convertToNative);
if ($cache->has()) {
// return response()->json($cache->get());
return response()->json($cache->get());
}
$locale = app('steam')->getLocale();

View File

@@ -92,6 +92,7 @@ class BudgetController extends Controller
$cache = new CacheProperties();
$cache->addProperty($start);
$cache->addProperty($end);
$cache->addProperty($this->convertToNative);
$cache->addProperty('chart.budget.budget');
$cache->addProperty($budget->id);
@@ -108,7 +109,7 @@ class BudgetController extends Controller
while ($end >= $loopStart) {
/** @var Carbon $loopEnd */
$loopEnd = app('navigation')->endOfPeriod($loopStart, $step);
$spent = $this->opsRepository->sumExpenses($loopStart, $loopEnd, null, $collection);
$spent = $this->opsRepository->sumExpenses($loopStart, $loopEnd, null, $collection); // this method already converts to native.
$label = trim(app('navigation')->periodShow($loopStart, $step));
foreach ($spent as $row) {
@@ -157,6 +158,7 @@ class BudgetController extends Controller
$cache = new CacheProperties();
$cache->addProperty($start);
$cache->addProperty($end);
$cache->addProperty($this->convertToNative);
$cache->addProperty('chart.budget.budget.limit');
$cache->addProperty($budgetLimit->id);
$cache->addProperty($budget->id);
@@ -169,9 +171,15 @@ class BudgetController extends Controller
$amount = $budgetLimit->amount;
$budgetCollection = new Collection([$budget]);
$currency = $budgetLimit->transactionCurrency;
if ($this->convertToNative) {
$amount = $budgetLimit->native_amount;
$currency = $this->defaultCurrency;
}
while ($start <= $end) {
$current = clone $start;
$expenses = $this->opsRepository->sumExpenses($current, $current, null, $budgetCollection, $currency);
$expenses = $this->opsRepository->sumExpenses($current, $current, null, $budgetCollection, $budgetLimit->transactionCurrency);
$spent = $expenses[$currency->id]['sum'] ?? '0';
$amount = bcadd($amount, $spent);
$format = $start->isoFormat((string) trans('config.month_and_day_js', [], $locale));
@@ -181,8 +189,8 @@ class BudgetController extends Controller
}
$data = $this->generator->singleSet((string) trans('firefly.left'), $entries);
// add currency symbol from budget limit:
$data['datasets'][0]['currency_symbol'] = $budgetLimit->transactionCurrency->symbol;
$data['datasets'][0]['currency_code'] = $budgetLimit->transactionCurrency->code;
$data['datasets'][0]['currency_symbol'] = $currency->symbol;
$data['datasets'][0]['currency_code'] = $currency->code;
$cache->store($data);
return response()->json($data);
@@ -198,6 +206,7 @@ class BudgetController extends Controller
$budgetLimitId = null === $budgetLimit ? 0 : $budgetLimit->id;
$cache = new CacheProperties();
$cache->addProperty($budget->id);
$cache->addProperty($this->convertToNative);
$cache->addProperty($budgetLimitId);
$cache->addProperty('chart.budget.expense-asset');
$start = session('first', today(config('app.timezone'))->startOfYear());
@@ -222,14 +231,33 @@ class BudgetController extends Controller
// group by asset account ID:
foreach ($journals as $journal) {
$key = sprintf('%d-%d', (int) $journal['source_account_id'], $journal['currency_id']);
$key = sprintf('%d-%d', $journal['source_account_id'], $journal['currency_id']);
$amount = $journal['amount'];
// if convert to native, use the native things, unless it's the foreign amount which is in the native currency.
if ($this->convertToNative && $journal['currency_id'] !== $this->defaultCurrency->id && $journal['foreign_currency_id'] !== $this->defaultCurrency->id) {
$key = sprintf('%d-%d', $journal['source_account_id'], $this->defaultCurrency->id);
$symbol = $this->defaultCurrency->symbol;
$code = $this->defaultCurrency->code;
$name = $this->defaultCurrency->name;
$amount = $journal['native_amount'];
}
if ($this->convertToNative && $journal['currency_id'] !== $this->defaultCurrency->id && $journal['foreign_currency_id'] === $this->defaultCurrency->id) {
$key = sprintf('%d-%d', $journal['source_account_id'], $this->defaultCurrency->id);
$symbol = $this->defaultCurrency->symbol;
$code = $this->defaultCurrency->code;
$name = $this->defaultCurrency->name;
$amount = $journal['foreign_amount'];
}
$result[$key] ??= [
'amount' => '0',
'currency_symbol' => $journal['currency_symbol'],
'currency_code' => $journal['currency_code'],
'currency_name' => $journal['currency_name'],
'currency_symbol' => $symbol,
'currency_code' => $code,
'currency_name' => $name,
];
$result[$key]['amount'] = bcadd($journal['amount'], $result[$key]['amount']);
$result[$key]['amount'] = bcadd($amount, $result[$key]['amount']);
}
$names = $this->getAccountNames(array_keys($result));
@@ -261,6 +289,7 @@ class BudgetController extends Controller
$budgetLimitId = null === $budgetLimit ? 0 : $budgetLimit->id;
$cache = new CacheProperties();
$cache->addProperty($budget->id);
$cache->addProperty($this->convertToNative);
$cache->addProperty($budgetLimitId);
$cache->addProperty('chart.budget.expense-category');
$start = session('first', today(config('app.timezone'))->startOfYear());
@@ -283,13 +312,36 @@ class BudgetController extends Controller
$chartData = [];
foreach ($journals as $journal) {
$key = sprintf('%d-%d', $journal['category_id'], $journal['currency_id']);
$symbol = $journal['currency_symbol'];
$code = $journal['currency_code'];
$name = $journal['currency_name'];
$amount = $journal['amount'];
// if convert to native, use the native things, unless it's the foreign amount which is in the native currency.
if ($this->convertToNative && $journal['currency_id'] !== $this->defaultCurrency->id && $journal['foreign_currency_id'] !== $this->defaultCurrency->id
) {
$key = sprintf('%d-%d', $journal['category_id'], $this->defaultCurrency->id);
$symbol = $this->defaultCurrency->symbol;
$code = $this->defaultCurrency->code;
$name = $this->defaultCurrency->name;
$amount = $journal['native_amount'];
}
if ($this->convertToNative && $journal['currency_id'] !== $this->defaultCurrency->id && $journal['foreign_currency_id'] === $this->defaultCurrency->id
) {
$key = sprintf('%d-%d', $journal['category_id'], $this->defaultCurrency->id);
$symbol = $this->defaultCurrency->symbol;
$code = $this->defaultCurrency->code;
$name = $this->defaultCurrency->name;
$amount = $journal['foreign_amount'];
}
$result[$key] ??= [
'amount' => '0',
'currency_symbol' => $journal['currency_symbol'],
'currency_code' => $journal['currency_code'],
'currency_name' => $journal['currency_name'],
'currency_symbol' => $symbol,
'currency_code' => $code,
'currency_name' => $name,
];
$result[$key]['amount'] = bcadd($journal['amount'], $result[$key]['amount']);
$result[$key]['amount'] = bcadd($amount, $result[$key]['amount']);
}
$names = $this->getCategoryNames(array_keys($result));
@@ -320,6 +372,7 @@ class BudgetController extends Controller
$cache = new CacheProperties();
$cache->addProperty($budget->id);
$cache->addProperty($budgetLimitId);
$cache->addProperty($this->convertToNative);
$cache->addProperty('chart.budget.expense-expense');
$start = session('first', today(config('app.timezone'))->startOfYear());
$end = today();
@@ -343,13 +396,32 @@ class BudgetController extends Controller
/** @var array $journal */
foreach ($journals as $journal) {
$key = sprintf('%d-%d', $journal['destination_account_id'], $journal['currency_id']);
$amount = $journal['amount'];
// if convert to native, use the native things, unless it's the foreign amount which is in the native currency.
if ($this->convertToNative && $journal['currency_id'] !== $this->defaultCurrency->id && $journal['foreign_currency_id'] !== $this->defaultCurrency->id) {
$key = sprintf('%d-%d', $journal['destination_account_id'], $this->defaultCurrency->id);
$symbol = $this->defaultCurrency->symbol;
$code = $this->defaultCurrency->code;
$name = $this->defaultCurrency->name;
$amount = $journal['native_amount'];
}
if ($this->convertToNative && $journal['currency_id'] !== $this->defaultCurrency->id && $journal['foreign_currency_id'] === $this->defaultCurrency->id) {
$key = sprintf('%d-%d', $journal['destination_account_id'], $this->defaultCurrency->id);
$symbol = $this->defaultCurrency->symbol;
$code = $this->defaultCurrency->code;
$name = $this->defaultCurrency->name;
$amount = $journal['foreign_amount'];
}
$result[$key] ??= [
'amount' => '0',
'currency_symbol' => $journal['currency_symbol'],
'currency_code' => $journal['currency_code'],
'currency_name' => $journal['currency_name'],
'currency_symbol' => $symbol,
'currency_code' => $code,
'currency_name' => $name,
];
$result[$key]['amount'] = bcadd($journal['amount'], $result[$key]['amount']);
$result[$key]['amount'] = bcadd($amount, $result[$key]['amount']);
}
$names = $this->getAccountNames(array_keys($result));
@@ -385,7 +457,7 @@ class BudgetController extends Controller
$cache->addProperty($this->convertToNative);
$cache->addProperty('chart.budget.frontpage');
if ($cache->has()) {
// return response()->json($cache->get());
return response()->json($cache->get());
}
Log::debug('Regenerate frontpage chart from scratch.');
$chartGenerator = app(FrontpageChartGenerator::class);

View File

@@ -70,24 +70,27 @@ class CategoryController extends Controller
public function all(Category $category): JsonResponse
{
// cache results:
$cache = new CacheProperties();
$cache = new CacheProperties();
$cache->addProperty('chart.category.all');
$cache->addProperty($category->id);
$cache->addProperty($this->convertToNative);
if ($cache->has()) {
return response()->json($cache->get());
}
/** @var CategoryRepositoryInterface $repository */
$repository = app(CategoryRepositoryInterface::class);
$start = $repository->firstUseDate($category) ?? $this->getDate();
$range = app('navigation')->getViewRange(false);
$start = app('navigation')->startOfPeriod($start, $range);
$end = $this->getDate();
$repository = app(CategoryRepositoryInterface::class);
$start = $repository->firstUseDate($category) ?? $this->getDate();
$range = app('navigation')->getViewRange(false);
$start = app('navigation')->startOfPeriod($start, $range);
$end = $this->getDate();
/** @var WholePeriodChartGenerator $chartGenerator */
$chartGenerator = app(WholePeriodChartGenerator::class);
$chartData = $chartGenerator->generate($category, $start, $end);
$data = $this->generator->multiSet($chartData);
$chartGenerator = app(WholePeriodChartGenerator::class);
$chartGenerator->convertToNative = $this->convertToNative;
$chartData = $chartGenerator->generate($category, $start, $end);
$data = $this->generator->multiSet($chartData);
$cache->store($data);
return response()->json($data);
@@ -113,7 +116,7 @@ class CategoryController extends Controller
$cache->addProperty($this->convertToNative);
$cache->addProperty('chart.category.frontpage');
if ($cache->has()) {
// return response()->json($cache->get());
return response()->json($cache->get());
}
$frontpageGenerator = new FrontpageChartGenerator($start, $end);
@@ -136,6 +139,7 @@ class CategoryController extends Controller
$cache->addProperty('chart.category.period');
$cache->addProperty($accounts->pluck('id')->toArray());
$cache->addProperty($category);
$cache->addProperty($this->convertToNative);
if ($cache->has()) {
return response()->json($cache->get());
}

View File

@@ -145,7 +145,7 @@ class ReportController extends Controller
$cache->addProperty($accounts);
$cache->addProperty($end);
if ($cache->has()) {
// return response()->json($cache->get());
return response()->json($cache->get());
}
Log::debug('Going to do operations for accounts ', $accounts->pluck('id')->toArray());

View File

@@ -24,6 +24,7 @@ declare(strict_types=1);
namespace FireflyIII\Http\Controllers;
use FireflyIII\Models\TransactionCurrency;
use FireflyIII\Support\Facades\Amount;
use FireflyIII\Support\Facades\Steam;
use FireflyIII\Support\Http\Controllers\RequestInformation;
use FireflyIII\Support\Http\Controllers\UserNavigation;
@@ -121,7 +122,7 @@ abstract class Controller extends BaseController
$language = Steam::getLanguage();
$locale = Steam::getLocale();
$darkMode = app('preferences')->get('darkMode', 'browser')->data;
$this->convertToNative =app('preferences')->get('convert_to_native', false)->data;
$this->convertToNative =Amount::convertToNative();
$page = $this->getPageName();
$shownDemo = $this->hasSeenDemo();
View::share('language', $language);

View File

@@ -76,6 +76,10 @@ class DebugController extends Controller
|| str_starts_with($route->uri(), '_debugbar')
|| str_starts_with($route->uri(), '_ignition')
|| str_starts_with($route->uri(), 'oauth')
|| str_starts_with($route->uri(), 'chart')
|| str_starts_with($route->uri(), 'v1/jscript')
|| str_starts_with($route->uri(), 'v2/jscript')
|| str_starts_with($route->uri(), 'json')
|| str_starts_with($route->uri(), 'sanctum')
) {
continue;

View File

@@ -27,6 +27,7 @@ namespace FireflyIII\Http\Controllers\ExchangeRates;
use FireflyIII\Http\Controllers\Controller;
use FireflyIII\Models\TransactionCurrency;
use Illuminate\View\View;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
class IndexController extends Controller
{
@@ -46,6 +47,9 @@ class IndexController extends Controller
return $next($request);
}
);
if (!config('cer.enabled')) {
throw new NotFoundHttpException();
}
}
public function index(): View

View File

@@ -49,14 +49,13 @@ class JavascriptController extends Controller
$accounts = $repository->getAccountsByType(
[AccountType::DEFAULT, AccountType::ASSET, AccountType::DEBT, AccountType::LOAN, AccountType::MORTGAGE, AccountType::CREDITCARD]
);
$default = app('amount')->getDefaultCurrency();
$data = ['accounts' => []];
/** @var Account $account */
foreach ($accounts as $account) {
$accountId = $account->id;
$currency = (int) $repository->getMetaValue($account, 'currency_id');
$currency = 0 === $currency ? $default->id : $currency;
$currency = 0 === $currency ? $this->defaultCurrency->id : $currency;
$entry = ['preferredCurrency' => $currency, 'name' => $account->name];
$data['accounts'][$accountId] = $entry;
}
@@ -96,9 +95,9 @@ class JavascriptController extends Controller
public function variables(Request $request, AccountRepositoryInterface $repository): Response
{
$account = $repository->find((int) $request->get('account'));
$currency = app('amount')->getDefaultCurrency();
$currency = $this->defaultCurrency;
if (null !== $account) {
$currency = $repository->getAccountCurrency($account) ?? $currency;
$currency = $repository->getAccountCurrency($account) ?? $this->defaultCurrency;
}
$locale = app('steam')->getLocale();
$accounting = app('amount')->getJsConfig();

View File

@@ -1,54 +0,0 @@
<?php
/*
* IsValidFilter.php
* Copyright (c) 2024 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\JsonApi\Rules;
use Illuminate\Contracts\Validation\ValidationRule;
class IsValidFilter implements ValidationRule
{
private array $allowed;
public function __construct(array $keys)
{
$this->allowed = $keys;
$this->allowed[] = 'user_group_id';
}
#[\Override]
public function validate(string $attribute, mixed $value, \Closure $fail): void
{
if ('filter' !== $attribute) {
$fail('validation.bad_api_filter')->translate();
}
if (!is_array($value)) {
$value = explode(',', $value);
}
foreach ($value as $key => $val) {
if (!in_array($key, $this->allowed, true)) {
$fail('validation.bad_api_filter')->translate(['filter' => $key]);
}
}
}
}

View File

@@ -1,53 +0,0 @@
<?php
/*
* IsValidFilter.php
* Copyright (c) 2024 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\JsonApi\Rules;
use Illuminate\Contracts\Validation\ValidationRule;
class IsValidPage implements ValidationRule
{
private array $allowed;
public function __construct(array $keys)
{
$this->allowed = $keys;
}
#[\Override]
public function validate(string $attribute, mixed $value, \Closure $fail): void
{
if ('page' !== $attribute) {
$fail('validation.bad_api_filter')->translate();
}
if (!is_array($value)) {
$value = explode(',', $value);
}
foreach ($value as $key => $val) {
if (!in_array($key, $this->allowed, true)) {
$fail('validation.bad_api_page')->translate();
}
}
}
}

View File

@@ -1,46 +0,0 @@
<?php
/*
* AccountBalanceRepository.php
* Copyright (c) 2024 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\JsonApi\V2\AccountBalances;
use FireflyIII\Entities\AccountBalance;
use LaravelJsonApi\Contracts\Store\QueriesAll;
use LaravelJsonApi\NonEloquent\AbstractRepository;
class AccountBalanceRepository extends AbstractRepository implements QueriesAll
{
#[\Override]
public function find(string $resourceId): ?object
{
return AccountBalance::fromArray();
}
public function queryAll(): Capabilities\AccountBalanceQuery
{
return Capabilities\AccountBalanceQuery::make()
->withServer($this->server)
->withSchema($this->schema)
;
}
}

View File

@@ -1,44 +0,0 @@
<?php
declare(strict_types=1);
namespace FireflyIII\JsonApi\V2\AccountBalances;
use Illuminate\Http\Request;
use LaravelJsonApi\Core\Resources\JsonApiResource;
class AccountBalanceResource extends JsonApiResource
{
/**
* Get the resource's attributes.
*
* @param null|Request $request
*/
public function attributes($request): iterable
{
return [
'name' => $this->resource->amount,
'amount' => $this->resource->amount,
];
}
/**
* Get the resource id.
*/
public function id(): string
{
return $this->resource->id;
}
/**
* Get the resource's relationships.
*
* @param null|Request $request
*/
public function relationships($request): iterable
{
return [
$this->relation('account')->withData($this->resource->getAccount()),
];
}
}

View File

@@ -1,50 +0,0 @@
<?php
declare(strict_types=1);
namespace FireflyIII\JsonApi\V2\AccountBalances;
use FireflyIII\Entities\AccountBalance;
use LaravelJsonApi\Core\Schema\Schema;
use LaravelJsonApi\Eloquent\Fields\Relations\HasOne;
use LaravelJsonApi\NonEloquent\Fields\Attribute;
use LaravelJsonApi\NonEloquent\Fields\ID;
class AccountBalanceSchema extends Schema
{
/**
* The model the schema corresponds to.
*/
public static string $model = AccountBalance::class;
/**
* Get the resource fields.
*/
public function fields(): array
{
return [
ID::make(),
Attribute::make('name'),
Attribute::make('amount'),
HasOne::make('account'),
];
}
/**
* Get the resource filters.
*/
public function filters(): array
{
return [
// Filter::make('id'),
];
}
public function repository(): AccountBalanceRepository
{
return AccountBalanceRepository::make()
->withServer($this->server)
->withSchema($this)
;
}
}

View File

@@ -1,59 +0,0 @@
<?php
/*
* AccountBalanceQuery.php
* Copyright (c) 2024 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\JsonApi\V2\AccountBalances\Capabilities;
use FireflyIII\Entities\AccountBalance;
use FireflyIII\Models\Account;
use LaravelJsonApi\NonEloquent\Capabilities\QueryAll;
class AccountBalanceQuery extends QueryAll
{
private Account $account;
/**
* QuerySites constructor.
*/
public function __construct()
{
parent::__construct();
}
public function get(): iterable
{
return [
AccountBalance::fromArray(),
AccountBalance::fromArray(),
AccountBalance::fromArray(),
AccountBalance::fromArray(),
];
}
public function withAccount(Account $account): self
{
$this->account = $account;
return $this;
}
}

View File

@@ -1,77 +0,0 @@
<?php
declare(strict_types=1);
namespace FireflyIII\JsonApi\V2\Accounts;
use FireflyIII\Models\Account;
use FireflyIII\Rules\Account\IsValidAccountType;
use FireflyIII\Rules\IsAllowedGroupAction;
use FireflyIII\Rules\IsDateOrTime;
use FireflyIII\Rules\IsValidDateRange;
use Illuminate\Support\Facades\Log;
use LaravelJsonApi\Laravel\Http\Requests\ResourceQuery;
use LaravelJsonApi\Validation\Rule as JsonApiRule;
class AccountCollectionQuery extends ResourceQuery
{
/**
* Get the validation rules that apply to the request query parameters.
*/
public function rules(): array
{
Log::debug(__METHOD__);
$validFilters = config('api.valid_api_filters')[Account::class];
return [
'fields' => [
'nullable',
'array',
JsonApiRule::fieldSets(),
],
'userGroupId' => [
'nullable',
'integer',
new IsAllowedGroupAction(Account::class, request()->method()),
],
'startPeriod' => [
'nullable',
'date',
new IsDateOrTime(),
new IsValidDateRange(),
],
'endPeriod' => [
'nullable',
'date',
new IsDateOrTime(),
new IsValidDateRange(),
],
'filter' => [
'nullable',
'array',
JsonApiRule::filter($validFilters),
new IsValidAccountType(),
],
'include' => [
'nullable',
'string',
JsonApiRule::includePaths(),
],
'page' => [
'nullable',
'array',
JsonApiRule::page(),
],
'sort' => [
'nullable',
'string',
JsonApiRule::sort(),
],
'withCount' => [
'nullable',
'string',
JsonApiRule::countable(),
],
];
}
}

View File

@@ -1,113 +0,0 @@
<?php
/*
* AccountRepository.php
* Copyright (c) 2024 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\JsonApi\V2\Accounts;
use FireflyIII\Models\Account;
use FireflyIII\Support\JsonApi\Concerns\UsergroupAware;
use FireflyIII\Support\JsonApi\Enrichments\AccountEnrichment;
use Illuminate\Support\Facades\Log;
use LaravelJsonApi\Contracts\Store\CreatesResources;
use LaravelJsonApi\Contracts\Store\QueriesAll;
use LaravelJsonApi\NonEloquent\AbstractRepository;
use LaravelJsonApi\NonEloquent\Capabilities\CrudRelations;
use LaravelJsonApi\NonEloquent\Concerns\HasCrudCapability;
use LaravelJsonApi\NonEloquent\Concerns\HasRelationsCapability;
/**
* Class AccountRepository
*
* The repository collects a single or many (account) objects from the database and returns them to the
* account resource. The account resource links all account properties to the JSON properties.
*
* For the queryAll thing, a separate query is constructed that does the actual querying of the database.
* This is necessary because the user can't just query all accounts (it would return other user's data)
* and because we also need to collect all kinds of metadata, like the currency and user info.
*/
class AccountRepository extends AbstractRepository implements QueriesAll, CreatesResources
{
use HasCrudCapability;
use HasRelationsCapability;
use UsergroupAware;
/**
* SiteRepository constructor.
*/
public function __construct()
{
Log::debug(__METHOD__);
}
public function exists(string $resourceId): bool
{
$result = null !== Account::find((int) $resourceId);
Log::debug(sprintf('%s: %s', __METHOD__, var_export($result, true)));
return $result;
}
public function find(string $resourceId): ?object
{
exit(__METHOD__);
Log::debug(__METHOD__);
// throw new \RuntimeException('trace me');
$account = Account::find((int) $resourceId);
if (null === $account) {
return null;
}
// enrich the collected data
$enrichment = new AccountEnrichment();
return $enrichment->enrichSingle($account);
}
public function queryAll(): Capabilities\AccountQuery
{
Log::debug(__METHOD__);
return Capabilities\AccountQuery::make()
->withUserGroup($this->userGroup)
->withServer($this->server)
->withSchema($this->schema)
;
}
protected function crud(): Capabilities\CrudAccount
{
Log::debug(__METHOD__);
return Capabilities\CrudAccount::make();
}
/**
* TODO piggy banks
* TODO transactions
*/
protected function relations(): CrudRelations
{
Log::debug(__METHOD__);
return Capabilities\CrudAccountRelations::make();
}
}

View File

@@ -1,53 +0,0 @@
<?php
declare(strict_types=1);
namespace FireflyIII\JsonApi\V2\Accounts;
use FireflyIII\Support\Request\ConvertsDataTypes;
use Illuminate\Support\Facades\Log;
use LaravelJsonApi\Laravel\Http\Requests\ResourceRequest;
class AccountRequest extends ResourceRequest
{
use ConvertsDataTypes;
/**
* Get the validation rules for the resource.
*/
public function rules(): array
{
Log::debug(__METHOD__);
$accountRoles = implode(',', config('firefly.accountRoles'));
$ccPaymentTypes = implode(',', array_keys(config('firefly.ccTypes')));
$types = implode(',', array_keys(config('firefly.subTitlesByIdentifier')));
$type = $this->convertString('type');
// var_dump($types);exit;
return [
'name' => ['required', 'max:1024', 'min:1'], // , new IsUniqueAccount()
'account_type' => ['required', 'max:1024', 'min:1', sprintf('in:%s', $types)],
// 'iban' => ['iban', 'nullable', new UniqueIban(null, $type)],
// 'bic' => 'bic|nullable',
// 'account_number' => ['min:1', 'max:255', 'nullable', new UniqueAccountNumber(null, $type)],
// 'opening_balance' => 'numeric|required_with:opening_balance_date|nullable',
// 'opening_balance_date' => 'date|required_with:opening_balance|nullable',
// 'virtual_balance' => 'numeric|nullable',
// 'order' => 'numeric|nullable',
// 'currency_id' => 'numeric|exists:transaction_currencies,id',
// 'currency_code' => 'min:3|max:3|exists:transaction_currencies,code',
// 'active' => [new IsBoolean()],
// 'include_net_worth' => [new IsBoolean()],
// 'account_role' => sprintf('nullable|in:%s|required_if:type,asset', $accountRoles),
// 'credit_card_type' => sprintf('nullable|in:%s|required_if:account_role,ccAsset', $ccPaymentTypes),
// 'monthly_payment_date' => 'nullable|date|required_if:account_role,ccAsset|required_if:credit_card_type,monthlyFull',
// 'liability_type' => 'nullable|required_if:type,liability|required_if:type,liabilities|in:loan,debt,mortgage',
// 'liability_amount' => ['required_with:liability_start_date', new IsValidPositiveAmount()],
// 'liability_start_date' => 'required_with:liability_amount|date',
// 'liability_direction' => 'nullable|required_if:type,liability|required_if:type,liabilities|in:credit,debit',
// 'interest' => 'min:0|max:100|numeric',
// 'interest_period' => sprintf('nullable|in:%s', implode(',', config('firefly.interest_periods'))),
// 'notes' => 'min:0|max:32768',
];
}
}

View File

@@ -1,83 +0,0 @@
<?php
declare(strict_types=1);
namespace FireflyIII\JsonApi\V2\Accounts;
use FireflyIII\Models\Account;
use Illuminate\Http\Request;
use LaravelJsonApi\Core\Resources\JsonApiResource;
/**
* @property Account $resource
*/
class AccountResource extends JsonApiResource
{
/**
* Get the resource's attributes.
*
* @param null|Request $request
*/
public function attributes($request): iterable
{
// Log::debug(__METHOD__);
return [
'created_at' => $this->resource->created_at,
'updated_at' => $this->resource->updated_at,
'name' => $this->resource->name,
'active' => $this->resource->active,
'order' => $this->resource->order,
'iban' => $this->resource->iban,
'account_type' => $this->resource->account_type_string,
'account_role' => $this->resource->account_role,
'account_number' => '' === $this->resource->account_number ? null : $this->resource->account_number,
// currency (if the account has a currency setting, otherwise NULL).
'currency_id' => $this->resource->currency_id,
'currency_name' => $this->resource->currency_name,
'currency_code' => $this->resource->currency_code,
'currency_symbol' => $this->resource->currency_symbol,
'currency_decimal_places' => $this->resource->currency_decimal_places,
'is_multi_currency' => '1' === $this->resource->is_multi_currency,
// balances
'balance' => $this->resource->balance,
'native_balance' => $this->resource->native_balance,
// liability things
'liability_direction' => $this->resource->liability_direction,
'interest' => $this->resource->interest,
'interest_period' => $this->resource->interest_period,
'current_debt' => $this->resource->current_debt, // TODO may be removed in the future.
// other things
'last_activity' => $this->resource->last_activity,
// object group
'object_group_id' => $this->resource->object_group_id,
'object_group_title' => $this->resource->object_group_title,
'object_group_order' => $this->resource->object_group_order,
];
}
/**
* Get the resource id.
*/
public function id(): string
{
return (string) $this->resource->id;
}
/**
* Get the resource's relationships.
*
* @param null|Request $request
*/
public function relationships($request): iterable
{
return [
$this->relation('user')->withData($this->resource->user),
];
}
}

View File

@@ -1,115 +0,0 @@
<?php
declare(strict_types=1);
namespace FireflyIII\JsonApi\V2\Accounts;
use FireflyIII\Models\Account;
use Illuminate\Http\Request;
use LaravelJsonApi\Core\Resources\JsonApiResource;
/**
* @property Account $resource
*
* This class collects the resources attributes, the account in this case.
* Generally speaking, each property here is directly related to a property on the account object itself.
* However, many properties are collected from other sources, like the user or the currency.
* As a result, the account repository is where it's at, which is where the collection takes place and is optimised.
*/
class AccountResourceOld extends JsonApiResource
{
/**
* Get the resource's attributes.
*
* @param null|Request $request
*/
public function attributes($request): iterable
{
// fields removed here have been migrated.
return [
'created_at' => $this->resource->created_at,
'updated_at' => $this->resource->updated_at,
'name' => $this->resource->name,
// 'virtual_balance' => $this->resource->virtual_balance,
// 'native_balance' => $this->resource->native_balance,
// 'user' => $this->resource->user_array,
// 'balances' => []
//
// balance (in currency, on date)
// 'current_balance' => $this->resource->current_balance,
// 'current_balance' => app('steam')->bcround(app('steam')->balance($account, $date), $decimalPlaces),
// 'current_balance_date' => $date->toAtomString(),
// 'notes' => $this->repository->getNoteText($account),
// 'monthly_payment_date' => $monthlyPaymentDate,
// 'credit_card_type' => $creditCardType,
// 'account_number' => $this->repository->getMetaValue($account, 'account_number'),
// 'bic' => $this->repository->getMetaValue($account, 'BIC'),
// 'opening_balance' => $openingBalance,
// 'opening_balance_date' => $openingBalanceDate,
// 'liability_type' => $liabilityType,
// 'liability_direction' => $liabilityDirection,
// 'interest' => $interest,
// 'interest_period' => $interestPeriod,
// 'current_debt' => $this->repository->getMetaValue($account, 'current_debt'),
// 'include_net_worth' => $includeNetWorth,
// 'longitude' => $longitude,
// 'latitude' => $latitude,
// 'zoom_level' => $zoomLevel,
// 'order' => $order,
// 'native_currency_id' => (string) $this->default->id,
// 'native_currency_code' => $this->default->code,
// 'native_currency_symbol' => $this->default->symbol,
// 'native_currency_decimal_places' => $this->default->decimal_places,
//
// // balance:
// 'current_balance' => $balance,
// 'native_current_balance' => $nativeBalance,
// 'current_balance_date' => $this->getDate()->endOfDay()->toAtomString(),
//
// // balance difference
// 'balance_difference' => $balanceDiff,
// 'native_balance_difference' => $nativeBalanceDiff,
// 'balance_difference_start' => $diffStart,
// 'balance_difference_end' => $diffEnd,
//
//
// // liability stuff
// 'liability_type' => $liabilityType,
//
// // object group
// 'object_group_id' => null !== $objectGroupId ? (string) $objectGroupId : null,
// 'object_group_order' => $objectGroupOrder,
// 'object_group_title' => $objectGroupTitle,
// 'notes' => $this->repository->getNoteText($account),
// 'monthly_payment_date' => $monthlyPaymentDate,
// 'credit_card_type' => $creditCardType,
// 'bic' => $this->repository->getMetaValue($account, 'BIC'),
// 'virtual_balance' => number_format((float) $account->virtual_balance, $decimalPlaces, '.', ''),
// 'opening_balance' => $openingBalance,
// 'opening_balance_date' => $openingBalanceDate,
// 'include_net_worth' => $includeNetWorth,
// 'longitude' => $longitude,
// 'latitude' => $latitude,
// 'zoom_level' => $zoomLevel,
];
}
/**
* Get the resource's relationships.
*
* @param null|Request $request
*/
public function relationships($request): iterable
{
return [
$this->relation('user')->withData($this->resource->user),
$this->relation('currency')->withData($this->resource->transactionCurrency),
// $this->relation('account_balances')->withData($this->resource->balances),
];
}
}

View File

@@ -1,112 +0,0 @@
<?php
declare(strict_types=1);
namespace FireflyIII\JsonApi\V2\Accounts;
use FireflyIII\Models\Account;
use FireflyIII\Support\JsonApi\Concerns\UsergroupAware;
use Illuminate\Support\Facades\Log;
use LaravelJsonApi\Core\Schema\Schema;
use LaravelJsonApi\Eloquent\Fields\Relations\HasOne;
use LaravelJsonApi\NonEloquent\Fields\Attribute;
use LaravelJsonApi\NonEloquent\Fields\ID;
use LaravelJsonApi\NonEloquent\Filters\Filter;
use LaravelJsonApi\NonEloquent\Pagination\EnumerablePagination;
class AccountSchema extends Schema
{
use UsergroupAware;
/**
* The model the schema corresponds to.
*/
public static string $model = Account::class;
/**
* Get the resource fields.
*/
public function fields(): array
{
return [
ID::make(),
Attribute::make('created_at'),
Attribute::make('updated_at'),
// basic info and meta data
Attribute::make('name')->sortable(),
Attribute::make('active')->sortable(),
Attribute::make('order')->sortable(),
Attribute::make('iban')->sortable(),
Attribute::make('account_type'),
Attribute::make('account_role'),
Attribute::make('account_number')->sortable(),
// currency
Attribute::make('currency_id'),
Attribute::make('currency_name'),
Attribute::make('currency_code'),
Attribute::make('currency_symbol'),
Attribute::make('currency_decimal_places'),
Attribute::make('is_multi_currency'),
// balance
Attribute::make('balance')->sortable(),
Attribute::make('native_balance')->sortable(),
// liability things
Attribute::make('liability_direction'),
Attribute::make('interest'),
Attribute::make('interest_period'),
// Attribute::make('current_debt')->sortable(),
// TODO credit card fields.
// dynamic data
Attribute::make('last_activity')->sortable(),
Attribute::make('balance_difference')->sortable(), // only used for sort.
// group
Attribute::make('object_group_id'),
Attribute::make('object_group_title'),
Attribute::make('object_group_order'),
// relations.
HasOne::make('user')->readOnly(),
];
}
/**
* Get the resource filters.
*/
public function filters(): array
{
Log::debug(__METHOD__);
$array = [];
$config = config('api.valid_api_filters')[Account::class];
foreach ($config as $entry) {
$array[] = Filter::make($entry);
}
return $array;
}
public function pagination(): EnumerablePagination
{
Log::debug(__METHOD__);
return EnumerablePagination::make();
}
public function repository(): AccountRepository
{
Log::debug(__METHOD__);
$this->setUserGroup($this->server->getUsergroup());
return AccountRepository::make()
->withServer($this->server)
->withSchema($this)
->withUserGroup($this->userGroup)
;
}
}

View File

@@ -1,69 +0,0 @@
<?php
declare(strict_types=1);
namespace FireflyIII\JsonApi\V2\Accounts;
use FireflyIII\Models\Account;
use LaravelJsonApi\Eloquent\Contracts\Paginator;
use LaravelJsonApi\Eloquent\Fields\DateTime;
use LaravelJsonApi\Eloquent\Fields\ID;
use LaravelJsonApi\Eloquent\Fields\Relations\HasOne;
use LaravelJsonApi\Eloquent\Fields\Str;
use LaravelJsonApi\Eloquent\Filters\WhereIdIn;
use LaravelJsonApi\Eloquent\Pagination\PagePagination;
use LaravelJsonApi\Eloquent\Schema;
/**
* Class AccountSchema
*
* This is the schema of all fields that an account exposes to the world.
* Fields do not have to have a relation to the actual model.
* Fields mentioned here still need to be filled in by the AccountResource.
*/
class AccountSchemaOld extends Schema
{
/**
* The model the schema corresponds to.
*/
public static string $model = Account::class;
/**
* Get the resource fields.
*/
public function fields(): array
{
return [
ID::make(),
DateTime::make('created_at')->sortable()->readOnly(),
DateTime::make('updated_at')->sortable()->readOnly(),
Str::make('name')->sortable(),
// Str::make('account_type'),
// Str::make('virtual_balance'),
// Str::make('iban'),
// Boolean::make('active'),
// Number::make('order'),
HasOne::make('user')->readOnly(),
// HasMany::make('account_balances'),
];
}
/**
* Filters mentioned here can be used to filter the results.
* TODO write down exactly how this works.
*/
public function filters(): array
{
return [
WhereIdIn::make($this),
];
}
/**
* Get the resource paginator.
*/
public function pagination(): ?Paginator
{
return PagePagination::make();
}
}

View File

@@ -1,45 +0,0 @@
<?php
declare(strict_types=1);
namespace FireflyIII\JsonApi\V2\Accounts;
use Illuminate\Support\Facades\Log;
use LaravelJsonApi\Laravel\Http\Requests\ResourceQuery;
use LaravelJsonApi\Validation\Rule as JsonApiRule;
class AccountSingleQuery extends ResourceQuery
{
/**
* Get the validation rules that apply to the request query parameters.
*/
public function rules(): array
{
Log::debug(__METHOD__);
return [
'fields' => [
'nullable',
'array',
JsonApiRule::fieldSets(),
],
'filter' => [
'nullable',
'array',
JsonApiRule::filter()->forget('id'),
],
'include' => [
'nullable',
'string',
JsonApiRule::includePaths(),
],
'page' => JsonApiRule::notSupported(),
'sort' => JsonApiRule::notSupported(),
'withCount' => [
'nullable',
'string',
JsonApiRule::countable(),
],
];
}
}

View File

@@ -1,142 +0,0 @@
<?php
/*
* AccountQuery.php
* Copyright (c) 2024 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\JsonApi\V2\Accounts\Capabilities;
use FireflyIII\Models\Account;
use FireflyIII\Support\Http\Api\AccountFilter;
use FireflyIII\Support\JsonApi\CollectsCustomParameters;
use FireflyIII\Support\JsonApi\Concerns\UsergroupAware;
use FireflyIII\Support\JsonApi\Enrichments\AccountEnrichment;
use FireflyIII\Support\JsonApi\ExpandsQuery;
use FireflyIII\Support\JsonApi\FiltersPagination;
use FireflyIII\Support\JsonApi\SortsCollection;
use FireflyIII\Support\JsonApi\SortsQueryResults;
use FireflyIII\Support\JsonApi\ValidateSortParameters;
use Illuminate\Pagination\LengthAwarePaginator;
use Illuminate\Support\Facades\Log;
use LaravelJsonApi\Contracts\Pagination\Page;
use LaravelJsonApi\Contracts\Store\HasPagination;
use LaravelJsonApi\NonEloquent\Capabilities\QueryAll;
class AccountQuery extends QueryAll implements HasPagination
{
use AccountFilter;
use CollectsCustomParameters;
use ExpandsQuery;
use FiltersPagination;
use SortsCollection;
use SortsQueryResults;
use UsergroupAware;
use ValidateSortParameters;
// use PaginatesEnumerables;
#[\Override]
/**
* This method returns all accounts, given a bunch of filters and sort fields, together with pagination.
*
* It is only used on the index, and nowhere else.
*/
public function get(): iterable
{
Log::debug(__METHOD__);
// collect sort options
$sort = $this->queryParameters->sortFields();
// collect pagination based on the page
$pagination = $this->filtersPagination($this->queryParameters->page());
// check if we need all accounts, regardless of pagination
// This is necessary when the user wants to sort on specific params.
$needsAll = $this->needsFullDataset(Account::class, $sort);
// params that were not recognised, may be my own custom stuff.
$otherParams = $this->getOtherParams($this->queryParameters->unrecognisedParameters());
// start the query
$query = $this->userGroup->accounts();
// add sort and filter parameters to the query.
$query = $this->addSortParams(Account::class, $query, $sort);
$query = $this->addFilterParams(Account::class, $query, $this->queryParameters->filter());
// collect the result.
$collection = $query->get(['accounts.*']);
// sort the data after the query, and return it right away.
$collection = $this->sortCollection(Account::class, $collection, $sort);
// if the entire collection needs to be enriched and sorted, do so now:
$totalCount = $collection->count();
Log::debug(sprintf('Total is %d', $totalCount));
if ($needsAll) {
Log::debug('Needs the entire collection');
// enrich the entire collection
$enrichment = new AccountEnrichment();
$enrichment->setStart($otherParams['start'] ?? null);
$enrichment->setEnd($otherParams['end'] ?? null);
$collection = $enrichment->enrich($collection);
// TODO sort the set based on post-query sort options:
$collection = $this->postQuerySort(Account::class, $collection, $sort);
// take the current page from the enriched set.
$currentPage = $collection->skip(($pagination['number'] - 1) * $pagination['size'])->take($pagination['size']);
}
if (!$needsAll) {
Log::debug('Needs only partial collection');
// take from the collection the filtered page + page number:
$currentPage = $collection->skip(($pagination['number'] - 1) * $pagination['size'])->take($pagination['size']);
// enrich only the current page.
$enrichment = new AccountEnrichment();
$enrichment->setStart($otherParams['start'] ?? null);
$enrichment->setEnd($otherParams['end'] ?? null);
$currentPage = $enrichment->enrich($currentPage);
}
// get current page?
Log::debug(sprintf('Skip %d, take %d', ($pagination['number'] - 1) * $pagination['size'], $pagination['size']));
// $currentPage = $collection->skip(($pagination['number'] - 1) * $pagination['size'])->take($pagination['size']);
Log::debug(sprintf('New collection size: %d', $currentPage->count()));
// TODO add filters after the query, if there are filters that cannot be applied to the database
// TODO same for sort things.
return new LengthAwarePaginator($currentPage, $totalCount, $pagination['size'], $pagination['number']);
}
#[\Override]
public function getOrPaginate(?array $page): iterable
{
exit('here weare');
// TODO: Implement getOrPaginate() method.
}
#[\Override]
public function paginate(array $page): Page
{
exit('here weare');
// TODO: Implement paginate() method.
}
}

View File

@@ -1,61 +0,0 @@
<?php
/*
* CrudAccount.php
* Copyright (c) 2024 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\JsonApi\V2\Accounts\Capabilities;
use FireflyIII\Models\Account;
use FireflyIII\Support\JsonApi\CollectsCustomParameters;
use FireflyIII\Support\JsonApi\Enrichments\AccountEnrichment;
use Illuminate\Support\Facades\Log;
use LaravelJsonApi\NonEloquent\Capabilities\CrudResource;
class CrudAccount extends CrudResource
{
use CollectsCustomParameters;
public function create(array $validatedData): Account
{
var_dump($validatedData);
exit;
}
/**
* Read the supplied site.
*/
public function read(Account $account): ?Account
{
$otherParams = $this->getOtherParams($this->request->query->all());
Log::debug(__METHOD__);
// enrich the collected data
$enrichment = new AccountEnrichment();
// set start and date, if present.
$enrichment->setStart($otherParams['start'] ?? null);
$enrichment->setEnd($otherParams['end'] ?? null);
return $enrichment->enrichSingle($account);
}
}

View File

@@ -1,29 +0,0 @@
<?php
/*
* CrudAccountRelations.php
* Copyright (c) 2024 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\JsonApi\V2\Accounts\Capabilities;
use LaravelJsonApi\NonEloquent\Capabilities\CrudRelations;
class CrudAccountRelations extends CrudRelations {}

View File

@@ -1,53 +0,0 @@
<?php
declare(strict_types=1);
namespace FireflyIII\JsonApi\V2;
use FireflyIII\JsonApi\V2\Accounts\AccountSchema;
use FireflyIII\JsonApi\V2\Users\UserSchema;
use FireflyIII\Support\JsonApi\Concerns\UsergroupAware;
use FireflyIII\Support\JsonApi\Concerns\UserGroupDetectable;
use Illuminate\Support\Facades\Log;
use LaravelJsonApi\Core\Server\Server as BaseServer;
/**
* Class Server
*
* This class serves as a generic class for the v2 API "server".
*/
class Server extends BaseServer
{
use UsergroupAware;
use UserGroupDetectable;
/**
* The base URI namespace for this server.
*/
protected string $baseUri = '/api/v2';
/**
* Bootstrap the server when it is handling an HTTP request.
*/
public function serving(): void
{
Log::debug(__METHOD__);
// at this point the user may not actually have access to this user group.
$res = $this->detectUserGroup();
$this->setUserGroup($res);
}
/**
* Get the server's list of schemas.
*/
protected function allSchemas(): array
{
// Log::debug(__METHOD__);
return [
AccountSchema::class,
UserSchema::class,
// AccountBalanceSchema::class,
];
}
}

View File

@@ -1,41 +0,0 @@
<?php
declare(strict_types=1);
namespace FireflyIII\JsonApi\V2\Users;
use FireflyIII\Models\User;
use Illuminate\Http\Request;
use LaravelJsonApi\Core\Resources\JsonApiResource;
/**
* @property User $resource
*/
class UserResource extends JsonApiResource
{
/**
* Get the resource's attributes.
*
* @param null|Request $request
*/
public function attributes($request): iterable
{
return [
'created_at' => $this->resource->created_at,
'updated_at' => $this->resource->updated_at,
'email' => $this->resource->email,
];
}
/**
* Get the resource's relationships.
*
* @param null|Request $request
*/
public function relationships($request): iterable
{
return [
// @TODO
];
}
}

View File

@@ -1,55 +0,0 @@
<?php
declare(strict_types=1);
namespace FireflyIII\JsonApi\V2\Users;
use FireflyIII\User;
use LaravelJsonApi\Eloquent\Contracts\Paginator;
use LaravelJsonApi\Eloquent\Fields\DateTime;
use LaravelJsonApi\Eloquent\Fields\ID;
use LaravelJsonApi\Eloquent\Fields\Relations\HasMany;
use LaravelJsonApi\Eloquent\Fields\Str;
use LaravelJsonApi\Eloquent\Filters\WhereIdIn;
use LaravelJsonApi\Eloquent\Pagination\PagePagination;
use LaravelJsonApi\Eloquent\Schema;
class UserSchema extends Schema
{
/**
* The model the schema corresponds to.
*/
public static string $model = User::class;
/**
* Get the resource fields.
*/
public function fields(): array
{
return [
ID::make(),
DateTime::make('created_at')->sortable()->readOnly(),
DateTime::make('updated_at')->sortable()->readOnly(),
Str::make('email'),
HasMany::make('accounts'),
];
}
/**
* Get the resource filters.
*/
public function filters(): array
{
return [
WhereIdIn::make($this),
];
}
/**
* Get the resource paginator.
*/
public function pagination(): ?Paginator
{
return PagePagination::make();
}
}

View File

@@ -26,7 +26,6 @@ namespace FireflyIII\Models;
use FireflyIII\Support\Models\ReturnsIntegerIdTrait;
use FireflyIII\Support\Models\ReturnsIntegerUserIdTrait;
use FireflyIII\User;
use GeneaLabs\LaravelModelCaching\Traits\Cachable;
use Illuminate\Database\Eloquent\Builder as EloquentBuilder;
use Illuminate\Database\Eloquent\Casts\Attribute;
use Illuminate\Database\Eloquent\Factories\HasFactory;
@@ -44,7 +43,6 @@ use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
*/
class Account extends Model
{
use Cachable;
use HasFactory;
use ReturnsIntegerIdTrait;
use ReturnsIntegerUserIdTrait;

View File

@@ -25,7 +25,6 @@ namespace FireflyIII\Models;
use Carbon\Carbon;
use FireflyIII\Support\Models\ReturnsIntegerIdTrait;
use GeneaLabs\LaravelModelCaching\Traits\Cachable;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Casts\Attribute;
use Illuminate\Database\Eloquent\Factories\HasFactory;
@@ -39,7 +38,6 @@ use Illuminate\Database\Eloquent\SoftDeletes;
*/
class Transaction extends Model
{
use Cachable;
use HasFactory;
use ReturnsIntegerIdTrait;
use SoftDeletes;

View File

@@ -527,7 +527,7 @@ class BillRepository implements BillRepositoryInterface
Log::debug(sprintf('sumPaidInRange from %s to %s', $start->toW3cString(), $end->toW3cString()));
$bills = $this->getActiveBills();
$return = [];
$convertToNative = app('preferences')->getForUser($this->user, 'convert_to_native', false)->data;
$convertToNative = Amount::convertToNative($this->user);
$default = app('amount')->getDefaultCurrency();
/** @var Bill $bill */
@@ -572,7 +572,7 @@ class BillRepository implements BillRepositoryInterface
app('log')->debug(sprintf('Now in sumUnpaidInRange("%s", "%s")', $start->format('Y-m-d'), $end->format('Y-m-d')));
$bills = $this->getActiveBills();
$return = [];
$convertToNative = app('preferences')->getForUser($this->user, 'convert_to_native', false)->data;
$convertToNative = Amount::convertToNative($this->user);
$default = app('amount')->getDefaultCurrency();
/** @var Bill $bill */

View File

@@ -27,6 +27,7 @@ namespace FireflyIII\Repositories\Budget;
use Carbon\Carbon;
use FireflyIII\Models\AvailableBudget;
use FireflyIII\Models\TransactionCurrency;
use FireflyIII\Support\Facades\Amount;
use FireflyIII\User;
use Illuminate\Contracts\Auth\Authenticatable;
use Illuminate\Database\Eloquent\Builder;
@@ -134,8 +135,8 @@ class AvailableBudgetRepository implements AvailableBudgetRepositoryInterface
;
// use native amount if necessary?
$convertToNative = app('preferences')->getForUser($this->user, 'convert_to_native', false)->data;
$default = app('amount')->getDefaultCurrency();
$convertToNative = Amount::convertToNative($this->user);
$default = Amount::getDefaultCurrency();
/** @var AvailableBudget $availableBudget */
foreach ($availableBudgets as $availableBudget) {

View File

@@ -214,7 +214,6 @@ class OperationsRepository implements OperationsRepositoryInterface
Log::debug('Start of sumExpenses.');
// 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.
// TODO this filter must be somewhere in AccountRepositoryInterface because I suspect its needed more often (A113)
// 2024-12-24 disable the exclusion for now.
@@ -224,8 +223,8 @@ class OperationsRepository implements OperationsRepositoryInterface
$selection = new Collection();
// default currency information for native stuff.
$convertToNative = app('preferences')->get('convert_to_native', false)->data;
$default = app('amount')->getDefaultCurrency();
$convertToNative = Amount::convertToNative($this->user);
$default = Amount::getDefaultCurrency();
/** @var Account $account */
foreach ($subset as $account) {
@@ -258,30 +257,10 @@ class OperationsRepository implements OperationsRepositoryInterface
// same but for transactions in the foreign currency:
if (null !== $currency) {
Log::debug('STOP looking for transactions in the foreign currency.');
// Log::debug(sprintf('Look for transactions with foreign currency %s', $currency->code));
// // app('log')->debug(sprintf('Currency is "%s".', $currency->name));
// /** @var GroupCollectorInterface $collector */
// $collector = app(GroupCollectorInterface::class);
// $collector->setUser($this->user)->setRange($start, $end)->setTypes([TransactionTypeEnum::WITHDRAWAL->value])->setForeignCurrency($currency)->setBudgets($budgets);
//
// if (null !== $accounts) {
// $collector->setAccounts($accounts);
// }
// $result = $collector->getExtractedJournals();
// // app('log')->debug(sprintf('Found %d journals with currency %s.', count($result), $currency->code));
// // do not use array_merge because you want keys to overwrite (otherwise you get double results):
// Log::debug(sprintf('Found %d extra journals in foreign currency.', count($result)));
// $journals = $result + $journals;
}
$array = [];
foreach ($journals as $journal) {
// Log::debug(sprintf('Journal #%d.', $journal['transaction_journal_id']));
// Log::debug(sprintf('Amounts: %1$s %2$s (amount), %3$s %4$s (foreign_amount), %5$s %6$s (native_amount) %5$s %7$s (foreign native amount)',
// $journal['currency_code'], $journal['amount'], $journal['foreign_currency_code'], $journal['foreign_amount'],
// $default->code, $journal['native_amount'], $journal['native_foreign_amount'])
// );
// TODO same as in category::sumexpenses
$amount = '0';
$currencyId = (int) $journal['currency_id'];

View File

@@ -154,8 +154,8 @@ class NoCategoryRepository implements NoCategoryRepositoryInterface
$journals = $collector->getExtractedJournals();
$array = [];
// default currency information for native stuff.
$convertToNative = app('preferences')->get('convert_to_native', false)->data;
$default = app('amount')->getDefaultCurrency();
$convertToNative = Amount::convertToNative($this->user);
$default = Amount::getDefaultCurrency();
foreach ($journals as $journal) {
// Almost the same as in \FireflyIII\Repositories\Budget\OperationsRepository::sumExpenses

View File

@@ -331,8 +331,8 @@ class OperationsRepository implements OperationsRepositoryInterface
$collector->setUser($this->user)->setRange($start, $end)->setTypes([TransactionTypeEnum::WITHDRAWAL->value]);
// default currency information for native stuff.
$convertToNative = app('preferences')->get('convert_to_native', false)->data;
$default = app('amount')->getDefaultCurrency();
$convertToNative = Amount::convertToNative($this->user);
$default = Amount::getDefaultCurrency();
if (null !== $accounts && $accounts->count() > 0) {
$collector->setAccounts($accounts);
}
@@ -355,15 +355,21 @@ class OperationsRepository implements OperationsRepositoryInterface
$currencyCode = $journal['currency_code'];
$currencyDecimalPlaces = $journal['currency_decimal_places'];
if ($convertToNative) {
$useNative = $default->id !== (int) $journal['currency_id'];
$amount = Amount::getAmountFromJournal($journal);
if ($useNative) {
$amount = Amount::getAmountFromJournal($journal);
if ($default->id !== (int) $journal['currency_id'] && $default->id !== (int) $journal['foreign_currency_id']) {
$currencyId = $default->id;
$currencyName = $default->name;
$currencySymbol = $default->symbol;
$currencyCode = $default->code;
$currencyDecimalPlaces = $default->decimal_places;
}
if ($default->id !== (int) $journal['currency_id'] && $default->id === (int) $journal['foreign_currency_id']) {
$currencyId = $journal['foreign_currency_id'];
$currencyName = $journal['foreign_currency_name'];
$currencySymbol = $journal['foreign_currency_symbol'];
$currencyCode = $journal['foreign_currency_code'];
$currencyDecimalPlaces = $journal['foreign_currency_decimal_places'];
}
Log::debug(sprintf('[a] Add amount %s %s', $currencyCode, $amount));
}
if (!$convertToNative) {
@@ -372,7 +378,6 @@ class OperationsRepository implements OperationsRepositoryInterface
$amount = $journal['amount'];
}
$array[$currencyId] ??= [
'sum' => '0',
'currency_id' => (string) $currencyId,
@@ -393,9 +398,9 @@ class OperationsRepository implements OperationsRepositoryInterface
public function sumIncome(Carbon $start, Carbon $end, ?Collection $accounts = null, ?Collection $categories = null): array
{
/** @var GroupCollectorInterface $collector */
$collector = app(GroupCollectorInterface::class);
$collector = app(GroupCollectorInterface::class);
$collector->setUser($this->user)->setRange($start, $end)
->setTypes([TransactionType::DEPOSIT])
->setTypes([TransactionTypeEnum::DEPOSIT->value])
;
if (null !== $accounts && $accounts->count() > 0) {
@@ -405,20 +410,52 @@ class OperationsRepository implements OperationsRepositoryInterface
$categories = $this->getCategories();
}
$collector->setCategories($categories);
$journals = $collector->getExtractedJournals();
$array = [];
$journals = $collector->getExtractedJournals();
$convertToNative = Amount::convertToNative($this->user);
$default = Amount::getDefaultCurrency();
$array = [];
foreach ($journals as $journal) {
// Almost the same as in \FireflyIII\Repositories\Budget\OperationsRepository::sumExpenses
$amount = '0';
$currencyId = (int) $journal['currency_id'];
$currencyName = $journal['currency_name'];
$currencySymbol = $journal['currency_symbol'];
$currencyCode = $journal['currency_code'];
$currencyDecimalPlaces = $journal['currency_decimal_places'];
if ($convertToNative) {
$amount = Amount::getAmountFromJournal($journal);
if ($default->id !== (int) $journal['currency_id'] && $default->id !== (int) $journal['foreign_currency_id']) {
$currencyId = $default->id;
$currencyName = $default->name;
$currencySymbol = $default->symbol;
$currencyCode = $default->code;
$currencyDecimalPlaces = $default->decimal_places;
}
if ($default->id !== (int) $journal['currency_id'] && $default->id === (int) $journal['foreign_currency_id']) {
$currencyId = $journal['foreign_currency_id'];
$currencyName = $journal['foreign_currency_name'];
$currencySymbol = $journal['foreign_currency_symbol'];
$currencyCode = $journal['foreign_currency_code'];
$currencyDecimalPlaces = $journal['foreign_currency_decimal_places'];
}
Log::debug(sprintf('[a] Add amount %s %s', $currencyCode, $amount));
}
if (!$convertToNative) {
// ignore the amount in foreign currency.
Log::debug(sprintf('[b] Add amount %s %s', $currencyCode, $journal['amount']));
$amount = $journal['amount'];
}
$array[$currencyId] ??= [
'sum' => '0',
'currency_id' => (string) $currencyId,
'currency_name' => $journal['currency_name'],
'currency_symbol' => $journal['currency_symbol'],
'currency_code' => $journal['currency_code'],
'currency_decimal_places' => $journal['currency_decimal_places'],
'currency_name' => $currencyName,
'currency_symbol' => $currencySymbol,
'currency_code' => $currencyCode,
'currency_decimal_places' => $currencyDecimalPlaces,
];
$array[$currencyId]['sum'] = bcadd($array[$currencyId]['sum'], app('steam')->positive($journal['amount']));
$array[$currencyId]['sum'] = bcadd($array[$currencyId]['sum'], app('steam')->positive($amount));
}
return $array;

View File

@@ -30,7 +30,7 @@ use FireflyIII\Models\TransactionJournal;
use FireflyIII\Models\UserGroup;
use FireflyIII\User;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Log;
use FireflyIII\Support\Facades\Preferences;
/**
* Class Amount.
@@ -54,13 +54,13 @@ class Amount
*/
public function getAmountFromJournal(array $journal): string
{
$convertToNative = app('preferences')->get('convert_to_native', false)->data;
$currency = app('amount')->getDefaultCurrency();
$convertToNative = $this->convertToNative();
$currency = $this->getDefaultCurrency();
$field = $convertToNative && $currency->id !== $journal['currency_id'] ? 'native_amount' : 'amount';
$amount = $journal[$field] ?? '0';
// Log::debug(sprintf('Field is %s, amount is %s', $field, $amount));
// fallback, the transaction has a foreign amount in $currency.
if ($convertToNative && null !== $journal['foreign_amount'] && $currency->id === (int)$journal['foreign_currency_id']) {
if ($convertToNative && null !== $journal['foreign_amount'] && $currency->id === (int) $journal['foreign_currency_id']) {
$amount = $journal['foreign_amount'];
// Log::debug(sprintf('Overruled, amount is now %s', $amount));
}
@@ -68,14 +68,23 @@ class Amount
return $amount;
}
public function convertToNative(?User $user = null): bool
{
if (null === $user) {
return Preferences::get('convert_to_native', false)->data && config('cer.enabled');
}
return Preferences::getForUser($user, 'convert_to_native', false)->data && config('cer.enabled');
}
/**
* Experimental function to see if we can quickly and quietly get the amount from a journal.
* This depends on the user's default currency and the wish to have it converted.
*/
public function getAmountFromJournalObject(TransactionJournal $journal): string
{
$convertToNative = app('preferences')->get('convert_to_native', false)->data;
$currency = app('amount')->getDefaultCurrency();
$convertToNative = $this->convertToNative();
$currency = $this->getDefaultCurrency();
$field = $convertToNative && $currency->id !== $journal->transaction_currency_id ? 'native_amount' : 'amount';
/** @var null|Transaction $sourceTransaction */
@@ -83,7 +92,7 @@ class Amount
if (null === $sourceTransaction) {
return '0';
}
$amount = $sourceTransaction->{$field};
$amount = $sourceTransaction->{$field} ?? '0';
if ((int) $sourceTransaction->foreign_currency_id === $currency->id) {
// use foreign amount instead!
$amount = (string) $sourceTransaction->foreign_amount; // hard coded to be foreign amount.

View File

@@ -25,7 +25,7 @@ declare(strict_types=1);
namespace FireflyIII\Support\Chart\Category;
use Carbon\Carbon;
use FireflyIII\Models\AccountType;
use FireflyIII\Enums\AccountTypeEnum;
use FireflyIII\Models\Category;
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
use FireflyIII\Repositories\Category\OperationsRepositoryInterface;
@@ -36,6 +36,8 @@ use Illuminate\Support\Collection;
*/
class WholePeriodChartGenerator
{
public bool $convertToNative;
public function generate(Category $category, Carbon $start, Carbon $end): array
{
$collection = new Collection([$category]);
@@ -46,7 +48,7 @@ class WholePeriodChartGenerator
/** @var AccountRepositoryInterface $accountRepository */
$accountRepository = app(AccountRepositoryInterface::class);
$types = [AccountType::DEFAULT, AccountType::ASSET, AccountType::LOAN, AccountType::DEBT, AccountType::MORTGAGE];
$types = [AccountTypeEnum::DEFAULT->value, AccountTypeEnum::ASSET->value, AccountTypeEnum::LOAN->value, AccountTypeEnum::DEBT->value, AccountTypeEnum::MORTGAGE->value];
$accounts = $accountRepository->getAccountsByType($types);
$step = $this->calculateStep($start, $end);
$chartData = [];

View File

@@ -30,6 +30,7 @@ use FireflyIII\Models\CurrencyExchangeRate;
use FireflyIII\Models\TransactionCurrency;
use FireflyIII\Models\UserGroup;
use FireflyIII\Support\CacheProperties;
use FireflyIII\Support\Facades\Steam;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\Log;
@@ -72,7 +73,7 @@ class ExchangeRateConverter
}
$rate = $this->getCurrencyRate($from, $to, $date);
return bcmul($amount, $rate);
return Steam::bcround(bcmul($amount, $rate), $to->decimal_places);
}
public function enabled(): bool

View File

@@ -1,75 +0,0 @@
<?php
/*
* ParsesQueryFilters.php
* Copyright (c) 2024 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\Http\Api;
use Carbon\Carbon;
use Carbon\Exceptions\InvalidFormatException;
use Illuminate\Support\Facades\Log;
use LaravelJsonApi\Core\Query\QueryParameters;
trait ParsesQueryFilters
{
private function arrayOfStrings(QueryParameters $parameters, string $field): array
{
$array = $parameters->filter()?->value($field, []) ?? [];
return is_string($array) ? [$array] : $array;
}
private function dateOrToday(QueryParameters $parameters, string $field): Carbon
{
$date = today();
$value = $parameters->filter()?->value($field, date('Y-m-d'));
if (is_array($value)) {
Log::error(sprintf('Multiple values for date field "%s". Using first value.', $field));
$value = $value[0];
}
try {
$date = Carbon::createFromFormat('Y-m-d', $value, config('app.timezone'));
} catch (InvalidFormatException $e) {
Log::debug(sprintf('Invalid date format in request. Using today: %s', $e->getMessage()));
}
return $date;
}
private function integerFromQueryParams(QueryParameters $parameters, string $field, int $default): int
{
return (int) ($parameters->page()[$field] ?? $default);
}
private function stringFromFilterParams(QueryParameters $parameters, string $field, string $default): string
{
return (string) $parameters->filter()?->value($field, $default) ?? $default;
}
private function stringFromQueryParams(QueryParameters $parameters, string $field, string $default): string
{
return (string) ($parameters->page()[$field] ?? $default);
}
}

View File

@@ -179,6 +179,7 @@ trait AugumentData
$cache->addProperty($start);
$cache->addProperty($end);
$cache->addProperty($budget->id);
$cache->addProperty($this->convertToNative);
$cache->addProperty('get-limits');
if ($cache->has()) {
@@ -186,15 +187,18 @@ trait AugumentData
}
$set = $blRepository->getBudgetLimits($budget, $start, $end);
$limits = new Collection();
$budgetCollection = new Collection([$budget]);
// merge sets based on a key, in case of convert to native
$limits = new Collection();
/** @var BudgetLimit $entry */
foreach ($set as $entry) {
$currency = $entry->transactionCurrency;
if (null === $currency) {
$currency = app('amount')->getDefaultCurrency();
if ($this->convertToNative) {
// the sumExpenses method already handles this.
$currency = $this->defaultCurrency;
}
// clone because these objects change each other.
@@ -214,7 +218,7 @@ trait AugumentData
}
$cache->store($limits);
return $set;
return $limits;
}
/**

View File

@@ -30,6 +30,7 @@ use FireflyIII\Generator\Chart\Basic\GeneratorInterface;
use FireflyIII\Models\Account;
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
use FireflyIII\Support\CacheProperties;
use FireflyIII\Support\Facades\Amount;
use FireflyIII\Support\Facades\Steam;
use Illuminate\Support\Collection;
@@ -46,7 +47,7 @@ trait ChartGeneration
protected function accountBalanceChart(Collection $accounts, Carbon $start, Carbon $end): array // chart helper method.
{
// chart properties for cache:
$convertToNative = app('preferences')->get('convert_to_native', false)->data;
$convertToNative = Amount::convertToNative();
$cache = new CacheProperties();
$cache->addProperty($start);
$cache->addProperty($end);
@@ -54,7 +55,7 @@ trait ChartGeneration
$cache->addProperty($accounts);
$cache->addProperty($convertToNative);
if ($cache->has()) {
// return $cache->get();
return $cache->get();
}
app('log')->debug('Regenerate chart.account.account-balance-chart from scratch.');
$locale = app('steam')->getLocale();

View File

@@ -198,36 +198,43 @@ trait PeriodOverview
/** @var array $journal */
foreach ($journals as $journal) {
$currencyId = (int) $journal['currency_id'];
$currencyCode = $journal['currency_code'];
$currencyName = $journal['currency_name'];
$currencySymbol = $journal['currency_symbol'];
$currencyDecimalPlaces = $journal['currency_decimal_places'];
$foreignCurrencyId = $journal['foreign_currency_id'];
if (!array_key_exists($currencyId, $return)) {
$return[$currencyId] = [
'amount' => '0',
'count' => 0,
'currency_id' => $currencyId,
'currency_name' => $journal['currency_name'],
'currency_code' => $journal['currency_code'],
'currency_symbol' => $journal['currency_symbol'],
'currency_decimal_places' => $journal['currency_decimal_places'],
];
}
$return[$currencyId]['amount'] = bcadd($return[$currencyId]['amount'], $journal['amount'] ?? '0');
++$return[$currencyId]['count'];
$amount = $journal['amount'];
if (null !== $foreignCurrencyId && null !== $journal['foreign_amount']) {
if (!array_key_exists($foreignCurrencyId, $return)) {
$return[$foreignCurrencyId] = [
'amount' => '0',
'count' => 0,
'currency_id' => (int) $foreignCurrencyId,
'currency_name' => $journal['foreign_currency_name'],
'currency_code' => $journal['foreign_currency_code'],
'currency_symbol' => $journal['foreign_currency_symbol'],
'currency_decimal_places' => $journal['foreign_currency_decimal_places'],
];
}
++$return[$foreignCurrencyId]['count'];
$return[$foreignCurrencyId]['amount'] = bcadd($return[$foreignCurrencyId]['amount'], $journal['foreign_amount']);
if ($this->convertToNative && $currencyId !== $this->defaultCurrency->id && $foreignCurrencyId !== $this->defaultCurrency->id) {
$amount = $journal['native_amount'];
$currencyId = $this->defaultCurrency->id;
$currencyCode = $this->defaultCurrency->code;
$currencyName = $this->defaultCurrency->name;
$currencySymbol = $this->defaultCurrency->symbol;
$currencyDecimalPlaces = $this->defaultCurrency->decimal_places;
}
if ($this->convertToNative && $currencyId !== $this->defaultCurrency->id && $foreignCurrencyId === $this->defaultCurrency->id) {
$currencyId = (int) $foreignCurrencyId;
$currencyCode = $journal['foreign_currency_code'];
$currencyName = $journal['foreign_currency_name'];
$currencySymbol = $journal['foreign_currency_symbol'];
$currencyDecimalPlaces = $journal['foreign_currency_decimal_places'];
$amount = $journal['foreign_amount'];
}
$return[$currencyId] ??= [
'amount' => '0',
'count' => 0,
'currency_id' => $currencyId,
'currency_name' => $currencyName,
'currency_code' => $currencyCode,
'currency_symbol' => $currencySymbol,
'currency_decimal_places' => $currencyDecimalPlaces,
];
$return[$currencyId]['amount'] = bcadd($return[$currencyId]['amount'], $amount);
++$return[$currencyId]['count'];
}
return $return;
@@ -322,6 +329,7 @@ trait PeriodOverview
$cache = new CacheProperties();
$cache->addProperty($start);
$cache->addProperty($end);
$cache->addProperty($this->convertToNative);
$cache->addProperty('no-budget-period-entries');
if ($cache->has()) {

View File

@@ -28,8 +28,6 @@ use FireflyIII\Models\Account;
use FireflyIII\Support\Http\Api\AccountFilter;
use Illuminate\Contracts\Database\Eloquent\Builder;
use Illuminate\Support\Facades\Log;
use LaravelJsonApi\Core\Query\FilterParameters;
use LaravelJsonApi\Core\Query\SortFields;
trait ExpandsQuery
{

View File

@@ -26,7 +26,6 @@ namespace FireflyIII\Support\JsonApi;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Log;
use LaravelJsonApi\Core\Query\SortFields;
trait SortsCollection
{

View File

@@ -27,8 +27,6 @@ namespace FireflyIII\Support\JsonApi;
use FireflyIII\Models\Account;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Log;
use LaravelJsonApi\Core\Query\SortField;
use LaravelJsonApi\Core\Query\SortFields;
trait SortsQueryResults
{

View File

@@ -25,7 +25,6 @@ declare(strict_types=1);
namespace FireflyIII\Support\JsonApi;
use Illuminate\Support\Facades\Log;
use LaravelJsonApi\Core\Query\SortFields;
trait ValidateSortParameters
{

View File

@@ -31,6 +31,7 @@ use FireflyIII\Models\TransactionCurrency;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;
use FireflyIII\Support\Facades\Amount;
/**
* Class Steam.
@@ -84,7 +85,7 @@ class Steam
$cache->addProperty($start);
$cache->addProperty($end);
if ($cache->has()) {
// return $cache->get();
return $cache->get();
}
$balances = [];
@@ -310,8 +311,8 @@ class Steam
public function finalAccountBalance(Account $account, Carbon $date): array
{
Log::debug(sprintf('Now in finalAccountBalance(#%d, "%s", "%s")', $account->id, $account->name, $date->format('Y-m-d H:i:s')));
$native = app('amount')->getDefaultCurrencyByUserGroup($account->user->userGroup);
$convertToNative = app('preferences')->getForUser($account->user, 'convert_to_native', false)->data;
$native = Amount::getDefaultCurrencyByUserGroup($account->user->userGroup);
$convertToNative = Amount::convertToNative($account->user);
$accountCurrency = $this->getAccountCurrency($account);
$hasCurrency = null !== $accountCurrency;
$currency = $hasCurrency ? $accountCurrency : $native;

View File

@@ -70,7 +70,7 @@ class General extends AbstractExtension
$info = Steam::finalAccountBalance($account, $date);
$currency = Steam::getAccountCurrency($account);
$default = Amount::getDefaultCurrency();
$convertToNative = app('preferences')->get('convert_to_native', false)->data;
$convertToNative = Amount::convertToNative();
$useNative = $convertToNative && $default->id !== $currency->id;
$strings = [];
foreach ($info as $key => $balance) {

View File

@@ -136,7 +136,7 @@ class PiggyBankTransformer extends AbstractTransformer
$return[] = [
'id' => $account->id,
'name' => $account->name,
'current_amount' => $account->pivot->current_amount,
'current_amount' => (string) $account->pivot->current_amount,
// TODO add balance, add left to save.
];
}

View File

@@ -3,6 +3,41 @@
All notable changes to this project will be documented in this file.
This project adheres to [Semantic Versioning](http://semver.org/).
## 6.2.0 - 2025-01-xx
### Added
- Multi-currency support. If you set `ENABLE_EXCHANGE_RATES=true` and optionally `ENABLE_EXTERNAL_RATES=true` Firefly III will try to calculate all foreign currencies back to your native currency. This is a work in progress, not all fields and all places will support this yet. Please check out the [documentation](https://docs.firefly-iii.org/explanation/financial-concepts/exchange-rates/).
- Notifications support Nfty, Pushover, Slack and Discord.
- Many new security related notifications.
- [Issue 5523](https://github.com/firefly-iii/firefly-iii/issues/5523) (Add comment on a budget for a given month) reported by @n-serrette
- [Issue 8531](https://github.com/firefly-iii/firefly-iii/issues/8531) (Add `notes` to transaction audit report) reported by @clouserw
- [Issue 8307](https://github.com/firefly-iii/firefly-iii/issues/8307) (Notification support for Ntfy (and other push notification tools)) reported by @ragnarkarlsson
- [Issue 7945](https://github.com/firefly-iii/firefly-iii/issues/7945) ("Rules" that only trigger manually) reported by @SekoiaTree
- [Issue 6760](https://github.com/firefly-iii/firefly-iii/issues/6760) (Add a new trigger for automated rules) reported by @Gsyltc
- [Issue 6557](https://github.com/firefly-iii/firefly-iii/issues/6557) (Piggy Banks - Draw Funds from Multiple Accounts) reported by @BugPhobic
### Changed
- Firefly III requires PHP 8.4.
- [Issue 9501](https://github.com/firefly-iii/firefly-iii/issues/9501) (PHP8.4 support) reported by @JC5
- Docker container no longer runs under root.
- "Bills" are now called "subscriptions" to better reflect their purpose.
### Removed
- Removed support for PHP 8.3 and lower.
- Removed Docker support for linux/arm/v7, linux/arm/v8 and linux/386. Sorry.
### Fixed
- [Issue 9532](https://github.com/firefly-iii/firefly-iii/issues/9532) (ReportSum Integrity Check fails due to empty foreign_amount) reported by @SircasticFox
- [Issue 7288](https://github.com/firefly-iii/firefly-iii/issues/7288) (currentMonthStart/currentMonthEnd not working for no-budget view) reported by @bradsk88
### API
- API changes related to new features are [documented](#).
## 6.1.25 - 2024-12-19
### Fixed

View File

@@ -84,12 +84,9 @@
"bacon/bacon-qr-code": "2.*",
"diglactic/laravel-breadcrumbs": "^9",
"gdbots/query-parser": "^3.0",
"genealabs/laravel-model-caching": "^11.0",
"guzzlehttp/guzzle": "^7.8",
"jc5/google2fa-laravel": "^2.0",
"jc5/recovery": "^2",
"laravel-json-api/laravel": "^5.0",
"laravel-json-api/non-eloquent": "^4.0",
"laravel-notification-channels/pushover": "^4.0",
"laravel/framework": "^11",
"laravel/passport": "^12",

778
composer.lock generated
View File

@@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "232d05ea287cfd97dde3fb95805a9b5a",
"content-hash": "19d10c2c3cd2c2a8d4b12d9d01a34491",
"packages": [
{
"name": "bacon/bacon-qr-code",
@@ -1182,130 +1182,6 @@
},
"time": "2021-12-05T19:44:35+00:00"
},
{
"name": "genealabs/laravel-model-caching",
"version": "11.0.1",
"source": {
"type": "git",
"url": "https://github.com/mikebronner/laravel-model-caching.git",
"reference": "2a38f0f1ed3554cf2da272d66c4d08a7885f196b"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/mikebronner/laravel-model-caching/zipball/2a38f0f1ed3554cf2da272d66c4d08a7885f196b",
"reference": "2a38f0f1ed3554cf2da272d66c4d08a7885f196b",
"shasum": ""
},
"require": {
"genealabs/laravel-pivot-events": "^10.0|^11.0",
"illuminate/cache": "^10.0|^11.0",
"illuminate/config": "^10.0|^11.0",
"illuminate/console": "^10.0|^11.0",
"illuminate/container": "^10.0|^11.0",
"illuminate/database": "^10.0|^11.0",
"illuminate/http": "^10.0|^11.0",
"illuminate/support": "^10.0|^11.0",
"php": ">=8.1"
},
"require-dev": {
"doctrine/dbal": "^3.3",
"fakerphp/faker": "^1.11",
"laravel/legacy-factories": "^1.3",
"laravel/nova": "^4.0",
"orchestra/testbench": "^8.0|^9.0",
"orchestra/testbench-browser-kit": "^8.0",
"php-coveralls/php-coveralls": "^2.2",
"phpunit/phpunit": "^10.0",
"slevomat/coding-standard": "^7.0|^8.14",
"squizlabs/php_codesniffer": "^3.6",
"symfony/thanks": "^1.2"
},
"type": "library",
"extra": {
"laravel": {
"providers": [
"GeneaLabs\\LaravelModelCaching\\Providers\\Service"
]
}
},
"autoload": {
"psr-4": {
"GeneaLabs\\LaravelModelCaching\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Mike Bronner",
"email": "hello@genealabs.com"
}
],
"description": "Automatic caching for Eloquent models.",
"support": {
"issues": "https://github.com/mikebronner/laravel-model-caching/issues",
"source": "https://github.com/mikebronner/laravel-model-caching/tree/11.0.1"
},
"time": "2024-03-14T23:34:57+00:00"
},
{
"name": "genealabs/laravel-pivot-events",
"version": "11.0.0",
"source": {
"type": "git",
"url": "https://github.com/mikebronner/laravel-pivot-events.git",
"reference": "16e974d80160774641f4323f5ffb757b79f300d3"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/mikebronner/laravel-pivot-events/zipball/16e974d80160774641f4323f5ffb757b79f300d3",
"reference": "16e974d80160774641f4323f5ffb757b79f300d3",
"shasum": ""
},
"require": {
"illuminate/database": "^8.0|^9.0|^10.0|^11.0",
"illuminate/support": "^8.0|^9.0|^10.0|^11.0"
},
"require-dev": {
"orchestra/testbench": "^9.0",
"phpunit/phpunit": "^10.5",
"symfony/thanks": "^1.0"
},
"type": "library",
"autoload": {
"psr-4": {
"GeneaLabs\\LaravelPivotEvents\\": "src"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Mike Bronner",
"email": "hello@genealabs.com",
"homepage": "https://genealabs.com",
"role": "Developer"
}
],
"description": "This package introduces new eloquent events for sync(), attach(), detach() or updateExistingPivot() methods on BelongsToMany relation.",
"homepage": "https://github.com/mikebronner/laravel-pivot-events",
"keywords": [
"eloquent events",
"eloquent extra events",
"laravel BelongsToMany events",
"laravel pivot events",
"laravel sync events"
],
"support": {
"issues": "https://github.com/mikebronner/laravel-pivot-events/issues",
"source": "https://github.com/mikebronner/laravel-pivot-events"
},
"time": "2024-03-14T23:24:54+00:00"
},
{
"name": "graham-campbell/result-type",
"version": "v1.1.3",
@@ -1934,618 +1810,6 @@
},
"time": "2022-03-31T05:55:34+00:00"
},
{
"name": "laravel-json-api/core",
"version": "v5.0.1",
"source": {
"type": "git",
"url": "https://github.com/laravel-json-api/core.git",
"reference": "e2f6696580166f7b6384318e28168252c2bd20f4"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/laravel-json-api/core/zipball/e2f6696580166f7b6384318e28168252c2bd20f4",
"reference": "e2f6696580166f7b6384318e28168252c2bd20f4",
"shasum": ""
},
"require": {
"ext-json": "*",
"illuminate/auth": "^11.0",
"illuminate/contracts": "^11.0",
"illuminate/http": "^11.0",
"illuminate/support": "^11.0",
"php": "^8.2"
},
"require-dev": {
"phpunit/phpunit": "^10.5"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-develop": "5.x-dev"
}
},
"autoload": {
"psr-4": {
"LaravelJsonApi\\Core\\": "src/Core",
"LaravelJsonApi\\Contracts\\": "src/Contracts"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Cloud Creativity Ltd",
"email": "info@cloudcreativity.co.uk"
},
{
"name": "Christopher Gammie",
"email": "contact@gammie.co.uk"
}
],
"description": "Contracts and support classes for Laravel JSON:API packages.",
"homepage": "https://github.com/laravel-json-api/core",
"keywords": [
"JSON-API",
"jsonapi",
"jsonapi.org",
"laravel"
],
"support": {
"issues": "https://github.com/laravel-json-api/core/issues",
"source": "https://github.com/laravel-json-api/core/tree/v5.0.1"
},
"time": "2024-11-30T16:31:42+00:00"
},
{
"name": "laravel-json-api/eloquent",
"version": "v4.4.0",
"source": {
"type": "git",
"url": "https://github.com/laravel-json-api/eloquent.git",
"reference": "fd7ebf898fb6e78bcbb8cba1a3b0e0d2554df244"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/laravel-json-api/eloquent/zipball/fd7ebf898fb6e78bcbb8cba1a3b0e0d2554df244",
"reference": "fd7ebf898fb6e78bcbb8cba1a3b0e0d2554df244",
"shasum": ""
},
"require": {
"ext-json": "*",
"illuminate/database": "^11.0",
"illuminate/support": "^11.0",
"laravel-json-api/core": "^4.3.2|^5.0.1",
"php": "^8.2"
},
"require-dev": {
"orchestra/testbench": "^9.0",
"phpunit/phpunit": "^10.5"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-develop": "4.x-dev"
}
},
"autoload": {
"psr-4": {
"LaravelJsonApi\\Eloquent\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Cloud Creativity Ltd",
"email": "info@cloudcreativity.co.uk"
},
{
"name": "Christopher Gammie",
"email": "contact@gammie.co.uk"
}
],
"description": "Serialize Eloquent models as JSON:API resources.",
"homepage": "https://github.com/laravel-json-api/eloquent",
"keywords": [
"JSON-API",
"jsonapi",
"jsonapi.org",
"laravel"
],
"support": {
"issues": "https://github.com/laravel-json-api/eloquent/issues",
"source": "https://github.com/laravel-json-api/eloquent/tree/v4.4.0"
},
"time": "2024-11-30T17:36:37+00:00"
},
{
"name": "laravel-json-api/encoder-neomerx",
"version": "v4.1.0",
"source": {
"type": "git",
"url": "https://github.com/laravel-json-api/encoder-neomerx.git",
"reference": "077c745a0c3caca535d30cacbb36b8daee29b812"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/laravel-json-api/encoder-neomerx/zipball/077c745a0c3caca535d30cacbb36b8daee29b812",
"reference": "077c745a0c3caca535d30cacbb36b8daee29b812",
"shasum": ""
},
"require": {
"ext-json": "*",
"illuminate/contracts": "^11.0",
"illuminate/support": "^11.0",
"laravel-json-api/core": "^4.3.2|^5.0.1",
"laravel-json-api/neomerx-json-api": "^5.0.3",
"php": "^8.2"
},
"require-dev": {
"phpunit/phpunit": "^10.5"
},
"type": "library",
"extra": {
"laravel": {
"providers": [
"LaravelJsonApi\\Encoder\\Neomerx\\ServiceProvider"
]
},
"branch-alias": {
"dev-develop": "4.x-dev"
}
},
"autoload": {
"psr-4": {
"LaravelJsonApi\\Encoder\\Neomerx\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Cloud Creativity Ltd",
"email": "info@cloudcreativity.co.uk"
},
{
"name": "Christopher Gammie",
"email": "contact@gammie.co.uk"
}
],
"description": "Encode JSON:API resources using the neomerx/json-api package.",
"homepage": "https://github.com/laravel-json-api/encoder-neomerx",
"keywords": [
"JSON-API",
"jsonapi",
"jsonapi.org",
"laravel"
],
"support": {
"issues": "https://github.com/laravel-json-api/encoder-neomerx/issues",
"source": "https://github.com/laravel-json-api/encoder-neomerx/tree/v4.1.0"
},
"time": "2024-11-30T17:28:56+00:00"
},
{
"name": "laravel-json-api/exceptions",
"version": "v3.1.0",
"source": {
"type": "git",
"url": "https://github.com/laravel-json-api/exceptions.git",
"reference": "1969ba640de3ec288e0a447532c9b43e0941d475"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/laravel-json-api/exceptions/zipball/1969ba640de3ec288e0a447532c9b43e0941d475",
"reference": "1969ba640de3ec288e0a447532c9b43e0941d475",
"shasum": ""
},
"require": {
"ext-json": "*",
"illuminate/contracts": "^11.0",
"illuminate/pipeline": "^11.0",
"laravel-json-api/core": "^4.3.2|^5.0.1",
"laravel-json-api/validation": "^4.2",
"php": "^8.2"
},
"require-dev": {
"laravel-json-api/testing": "^3.0",
"orchestra/testbench": "^9.0",
"phpunit/phpunit": "^10.5"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-develop": "3.x-dev"
}
},
"autoload": {
"psr-4": {
"LaravelJsonApi\\Exceptions\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Cloud Creativity Ltd",
"email": "info@cloudcreativity.co.uk"
},
{
"name": "Christopher Gammie",
"email": "contact@gammie.co.uk"
}
],
"description": "JSON:API exception parsing for Laravel applications.",
"homepage": "https://github.com/laravel-json-api/exceptions",
"keywords": [
"JSON-API",
"jsonapi",
"jsonapi.org",
"laravel"
],
"support": {
"issues": "https://github.com/laravel-json-api/exceptions/issues",
"source": "https://github.com/laravel-json-api/exceptions/tree/v3.1.0"
},
"time": "2024-11-30T17:20:13+00:00"
},
{
"name": "laravel-json-api/laravel",
"version": "v5.0.2",
"source": {
"type": "git",
"url": "https://github.com/laravel-json-api/laravel.git",
"reference": "53045c6a55b4923e3cfadc085fd5f0b7c8c79cfc"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/laravel-json-api/laravel/zipball/53045c6a55b4923e3cfadc085fd5f0b7c8c79cfc",
"reference": "53045c6a55b4923e3cfadc085fd5f0b7c8c79cfc",
"shasum": ""
},
"require": {
"ext-json": "*",
"laravel-json-api/core": "^5.0.1",
"laravel-json-api/eloquent": "^4.4",
"laravel-json-api/encoder-neomerx": "^4.1",
"laravel-json-api/exceptions": "^3.1",
"laravel-json-api/spec": "^3.1",
"laravel-json-api/validation": "^4.2",
"laravel/framework": "^11.0",
"php": "^8.2"
},
"require-dev": {
"laravel-json-api/testing": "^3.0.2",
"orchestra/testbench": "^9.0",
"phpunit/phpunit": "^10.5"
},
"type": "library",
"extra": {
"laravel": {
"aliases": {
"JsonApi": "LaravelJsonApi\\Core\\Facades\\JsonApi",
"JsonApiRoute": "LaravelJsonApi\\Laravel\\Facades\\JsonApiRoute"
},
"providers": [
"LaravelJsonApi\\Laravel\\ServiceProvider"
]
},
"branch-alias": {
"dev-develop": "5.x-dev"
}
},
"autoload": {
"psr-4": {
"LaravelJsonApi\\Laravel\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Cloud Creativity Ltd",
"email": "info@cloudcreativity.co.uk"
},
{
"name": "Christopher Gammie",
"email": "contact@gammie.co.uk"
}
],
"description": "JSON:API for Laravel applications.",
"homepage": "https://github.com/laravel-json-api/laravel",
"keywords": [
"JSON-API",
"jsonapi",
"jsonapi.org",
"laravel"
],
"support": {
"issues": "https://github.com/laravel-json-api/laravel/issues",
"source": "https://github.com/laravel-json-api/laravel/tree/v5.0.2"
},
"time": "2024-12-03T20:43:07+00:00"
},
{
"name": "laravel-json-api/neomerx-json-api",
"version": "v5.0.3",
"source": {
"type": "git",
"url": "https://github.com/laravel-json-api/neomerx-json-api.git",
"reference": "836342be5eb4bcf6c3734c7f8e82c9ceec3be550"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/laravel-json-api/neomerx-json-api/zipball/836342be5eb4bcf6c3734c7f8e82c9ceec3be550",
"reference": "836342be5eb4bcf6c3734c7f8e82c9ceec3be550",
"shasum": ""
},
"require": {
"ext-json": "*",
"php": "^7.4|^8.0"
},
"require-dev": {
"friendsofphp/php-cs-fixer": "^3.17",
"mockery/mockery": "^1.4.4",
"phpmd/phpmd": "^2.11.1",
"phpunit/phpunit": "^9.5.10",
"scrutinizer/ocular": "^1.8",
"squizlabs/php_codesniffer": "^3.6.2"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-develop": "5.x-dev"
}
},
"autoload": {
"files": [
"src/I18n/format.php"
],
"psr-4": {
"Neomerx\\JsonApi\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"Apache-2.0"
],
"authors": [
{
"name": "neomerx",
"email": "info@neomerx.com"
},
{
"name": "Christopher Gammie",
"email": "contact@gammie.co.uk"
}
],
"description": "Framework agnostic JSON API (jsonapi.org) implementation",
"homepage": "https://github.com/neomerx/json-api",
"keywords": [
"JSON-API",
"api",
"json",
"jsonapi",
"jsonapi.org",
"neomerx"
],
"support": {
"issues": "https://github.com/neomerx/json-api/issues",
"source": "https://github.com/laravel-json-api/neomerx-json-api/tree/v5.0.3"
},
"time": "2024-11-29T17:49:31+00:00"
},
{
"name": "laravel-json-api/non-eloquent",
"version": "v4.1.0",
"source": {
"type": "git",
"url": "https://github.com/laravel-json-api/non-eloquent.git",
"reference": "3a28054ba3abd38323ec7c926419fc2a229766dd"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/laravel-json-api/non-eloquent/zipball/3a28054ba3abd38323ec7c926419fc2a229766dd",
"reference": "3a28054ba3abd38323ec7c926419fc2a229766dd",
"shasum": ""
},
"require": {
"laravel-json-api/core": "^4.3.2|^5.0.1",
"php": "^8.2"
},
"require-dev": {
"orchestra/testbench": "^9.0",
"phpunit/phpunit": "^10.5"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-develop": "4.x-dev"
}
},
"autoload": {
"psr-4": {
"LaravelJsonApi\\NonEloquent\\": "src"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Cloud Creativity Ltd",
"email": "info@cloudcreativity.co.uk"
},
{
"name": "Christopher Gammie",
"email": "contact@gammie.co.uk"
}
],
"description": "Construct JSON:API resources for non-Eloquent classes.",
"homepage": "https://github.com/laravel-json-api/non-eloquent",
"keywords": [
"JSON-API",
"jsonapi",
"jsonapi.org",
"laravel"
],
"support": {
"issues": "https://github.com/laravel-json-api/non-eloquent/issues",
"source": "https://github.com/laravel-json-api/non-eloquent/tree/v4.1.0"
},
"time": "2024-11-30T17:51:30+00:00"
},
{
"name": "laravel-json-api/spec",
"version": "v3.1.0",
"source": {
"type": "git",
"url": "https://github.com/laravel-json-api/spec.git",
"reference": "75a7a77de4421d58b0f38e2c94fae728b9d3690e"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/laravel-json-api/spec/zipball/75a7a77de4421d58b0f38e2c94fae728b9d3690e",
"reference": "75a7a77de4421d58b0f38e2c94fae728b9d3690e",
"shasum": ""
},
"require": {
"ext-json": "*",
"illuminate/contracts": "^11.0",
"illuminate/pipeline": "^11.0",
"illuminate/support": "^11.0",
"laravel-json-api/core": "^4.3.2|^5.0.1",
"php": "^8.2"
},
"require-dev": {
"orchestra/testbench": "^9.0",
"phpunit/phpunit": "^10.5"
},
"type": "library",
"extra": {
"laravel": {
"providers": [
"LaravelJsonApi\\Spec\\ServiceProvider"
]
},
"branch-alias": {
"dev-develop": "3.x-dev"
}
},
"autoload": {
"psr-4": {
"LaravelJsonApi\\Spec\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Cloud Creativity Ltd",
"email": "info@cloudcreativity.co.uk"
},
{
"name": "Christopher Gammie",
"email": "contact@gammie.co.uk"
}
],
"description": "Validate JSON documents for compliance with the JSON:API specification.",
"homepage": "https://github.com/laravel-json-api/spec",
"keywords": [
"JSON-API",
"jsonapi",
"jsonapi.org",
"laravel"
],
"support": {
"issues": "https://github.com/laravel-json-api/spec/issues",
"source": "https://github.com/laravel-json-api/spec/tree/v3.1.0"
},
"time": "2024-11-30T16:59:19+00:00"
},
{
"name": "laravel-json-api/validation",
"version": "v4.2.0",
"source": {
"type": "git",
"url": "https://github.com/laravel-json-api/validation.git",
"reference": "635e942e44f01186ddea1c7a29d8e2d0de2c1540"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/laravel-json-api/validation/zipball/635e942e44f01186ddea1c7a29d8e2d0de2c1540",
"reference": "635e942e44f01186ddea1c7a29d8e2d0de2c1540",
"shasum": ""
},
"require": {
"ext-json": "*",
"illuminate/contracts": "^11.0",
"illuminate/support": "^11.0",
"laravel-json-api/core": "^4.3.2|^5.0.1",
"php": "^8.2"
},
"require-dev": {
"orchestra/testbench": "^9.0",
"phpunit/phpunit": "^10.5"
},
"type": "library",
"extra": {
"laravel": {
"providers": [
"LaravelJsonApi\\Validation\\ServiceProvider"
]
},
"branch-alias": {
"dev-develop": "4.x-dev"
}
},
"autoload": {
"psr-4": {
"LaravelJsonApi\\Validation\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Cloud Creativity Ltd",
"email": "info@cloudcreativity.co.uk"
},
{
"name": "Christopher Gammie",
"email": "contact@gammie.co.uk"
}
],
"description": "Laravel validation for JSON:API resources.",
"homepage": "https://github.com/laravel-json-api/validation",
"keywords": [
"JSON-API",
"jsonapi",
"jsonapi.org",
"laravel"
],
"support": {
"issues": "https://github.com/laravel-json-api/validation/issues",
"source": "https://github.com/laravel-json-api/validation/tree/v4.2.0"
},
"time": "2024-11-30T17:11:17+00:00"
},
{
"name": "laravel-notification-channels/pushover",
"version": "4.0.0",
@@ -7330,12 +6594,12 @@
},
"type": "library",
"extra": {
"thanks": {
"url": "https://github.com/symfony/contracts",
"name": "symfony/contracts"
},
"branch-alias": {
"dev-main": "3.5-dev"
},
"thanks": {
"name": "symfony/contracts",
"url": "https://github.com/symfony/contracts"
}
},
"autoload": {
@@ -7637,12 +6901,12 @@
},
"type": "library",
"extra": {
"thanks": {
"url": "https://github.com/symfony/contracts",
"name": "symfony/contracts"
},
"branch-alias": {
"dev-main": "3.5-dev"
},
"thanks": {
"name": "symfony/contracts",
"url": "https://github.com/symfony/contracts"
}
},
"autoload": {
@@ -7860,12 +7124,12 @@
},
"type": "library",
"extra": {
"thanks": {
"url": "https://github.com/symfony/contracts",
"name": "symfony/contracts"
},
"branch-alias": {
"dev-main": "3.5-dev"
},
"thanks": {
"name": "symfony/contracts",
"url": "https://github.com/symfony/contracts"
}
},
"autoload": {
@@ -9670,12 +8934,12 @@
},
"type": "library",
"extra": {
"thanks": {
"url": "https://github.com/symfony/contracts",
"name": "symfony/contracts"
},
"branch-alias": {
"dev-main": "3.5-dev"
},
"thanks": {
"name": "symfony/contracts",
"url": "https://github.com/symfony/contracts"
}
},
"autoload": {
@@ -9930,12 +9194,12 @@
},
"type": "library",
"extra": {
"thanks": {
"url": "https://github.com/symfony/contracts",
"name": "symfony/contracts"
},
"branch-alias": {
"dev-main": "3.5-dev"
},
"thanks": {
"name": "symfony/contracts",
"url": "https://github.com/symfony/contracts"
}
},
"autoload": {

View File

@@ -81,7 +81,7 @@ return [
'running_balance_column' => env('USE_RUNNING_BALANCE', false),
// see cer.php for exchange rates feature flag.
],
'version' => 'branch-v6.2',
'version' => 'develop/2024-12-28',
'api_version' => '2.1.0', // field is no longer used.
'db_version' => 25,

View File

@@ -5,6 +5,7 @@ declare(strict_types=1);
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
use Illuminate\Support\Facades\Log;
return new class () extends Migration {
/**
@@ -13,10 +14,14 @@ return new class () extends Migration {
public function up(): void
{
// make account_id nullable and the relation also nullable.
Schema::table('piggy_banks', static function (Blueprint $table): void {
// 1. drop index
$table->dropForeign('piggy_banks_account_id_foreign');
});
try {
Schema::table('piggy_banks', static function (Blueprint $table): void {
// 1. drop index
$table->dropForeign('piggy_banks_account_id_foreign');
});
} catch (RuntimeException $e) {
Log::error('Could not drop foreign key "piggy_banks_account_id_foreign". Probably not an issue.');
}
Schema::table('piggy_banks', static function (Blueprint $table): void {
// 2. make column nullable.
$table->unsignedInteger('account_id')->nullable()->change();

2
package-lock.json generated
View File

@@ -12195,7 +12195,7 @@
"chartjs-adapter-date-fns": "^3.0.0",
"chartjs-chart-sankey": "^0.14.0",
"date-fns": "^4.0.0",
"i18next": "^24.0.0",
"i18next": "^24.2.0",
"i18next-chained-backend": "^4.6.2",
"i18next-http-backend": "^3.0.1",
"i18next-localstorage-backend": "^4.2.0",

View File

@@ -31,7 +31,7 @@
"chartjs-adapter-date-fns": "^3.0.0",
"chartjs-chart-sankey": "^0.14.0",
"date-fns": "^4.0.0",
"i18next": "^24.0.0",
"i18next": "^24.2.0",
"i18next-chained-backend": "^4.6.2",
"i18next-http-backend": "^3.0.1",
"i18next-localstorage-backend": "^4.2.0",

View File

@@ -1913,6 +1913,7 @@ return [
'extension_date_is' => 'Extension date is {date}',
// accounts:
'account_locked_currency' => 'The currency of this account must remain :name as long as piggy banks are linked to it.',
'i_am_owed_amount' => 'I am owed amount',
'i_owe_amount' => 'I owe amount',
'inactive_account_link' => 'You have :count inactive (archived) account, which you can view on this separate page.|You have :count inactive (archived) accounts, which you can view on this separate page.',

View File

@@ -27,9 +27,12 @@
</div>
<div class="box-body">
{{ ExpandedForm.text('name', account.name) }}
{% if account.accountType.type == 'Default account' or account.accountType.type == 'Asset account' or objectType == 'liabilities' %}
{% if canEditCurrency and (account.accountType.type == 'Default account' or account.accountType.type == 'Asset account' or objectType == 'liabilities') %}
{{ CurrencyForm.currencyList('currency_id', null, {helpText:'account_default_currency'|_}) }}
{% endif %}
{% if not canEditCurrency and (account.accountType.type == 'Default account' or account.accountType.type == 'Asset account' or objectType == 'liabilities') %}
<input type="hidden" name="currency_id" value="{{ currency.id }}"/>
{{ ExpandedForm.staticText('currency_id', trans('firefly.account_locked_currency', {name: currency.name})) }}
{% endif %}
{% if objectType == 'liabilities' %}

View File

@@ -116,6 +116,9 @@
<small>{{ 'budgeted'|_ }}:
<span class="text-success money-positive budgeted_amount" data-id="{{ budget.id }}" data-currency="{{ budget.transaction_currency.id }}">
{{ formatAmountBySymbol(budget.budgeted, budget.transaction_currency.symbol, budget.transaction_currency.decimal_places, false) }}
{% if null != budget.native_budgeted %}
({{ formatAmountBySymbol(budget.native_budgeted, defaultCurrency.symbol, defaultCurrency.decimal_places, false) }})
{% endif %}
</span>
</small>
</div>
@@ -125,7 +128,13 @@
data-id="{{ budget.id }}">{{ trans('firefly.available_between', {start: budget.start_date.isoFormat(monthAndDayFormat), end: budget.end_date.isoFormat(monthAndDayFormat) }) }}
:
<span class="available_amount" data-id="{{ budget.id }}" data-currency="{{ budget.transaction_currency.id }}"
data-value="{{ budget.amount }}">{{ formatAmountBySymbol(budget.amount, budget.transaction_currency.symbol, budget.transaction_currency.decimal_places, true) }}</span>
data-value="{{ budget.amount }}">
{{ formatAmountBySymbol(budget.amount, budget.transaction_currency.symbol, budget.transaction_currency.decimal_places, true) }}
{% if(convertToNative and 0 != budget.native_amount) %}
({{ formatAmountBySymbol(budget.native_amount, defaultCurrency.symbol, defaultCurrency.decimal_places, true) }})
{% endif %}
</span>
</small>
</div>
</div>

View File

@@ -158,12 +158,19 @@
<td style="width:33%;">{{ 'amount'|_ }}</td>
<td>
{{ formatAmountBySymbol(limit.amount, limit.transactionCurrency.symbol, limit.transactionCurrency.decimal_places) }}
{% if convertToNative and 0 != limit.native_amount %}
({{ formatAmountBySymbol(limit.native_amount, defaultCurrency.symbol, defaultCurrency.decimal_places) }})
{% endif %}
</td>
</tr>
<tr>
<td style="width:33%;">{{ 'spent'|_ }}</td>
<td>
{{ formatAmountBySymbol(limit.spent, limit.transactionCurrency.symbol, limit.transactionCurrency.decimal_places) }}
{% if convertToNative %}
{{ formatAmountBySymbol(limit.spent, defaultCurrency.symbol, defaultCurrency.decimal_places) }}
{% else %}
{{ formatAmountBySymbol(limit.spent, limit.transactionCurrency.symbol, limit.transactionCurrency.decimal_places) }}
{% endif %}
</td>
</tr>
{% if limit.spent > 0 %}

View File

@@ -219,12 +219,14 @@
<span>{{ 'currencies'|_ }}</span>
</a>
</li>
{% if config('cer.enabled') %}
<li class="{{ activeRoutePartial('exchange-rates') }}">
<a class="{{ activeRoutePartial('exchange-rates') }}" href="{{ route('exchange-rates.index') }}">
<span class="fa fa-angle-right fa-fw"></span>
<span>{{ 'menu_exchange_rates_index'|_ }}</span>
</a>
</li>
{% endif %}
{% if hasRole('owner') %}
<li class="{{ activeRoutePartial('admin') }}">
<a class="{{ activeRoutePartial('admin') }}" href="{{ route('admin.index') }}">

View File

@@ -100,7 +100,8 @@
{{ ExpandedForm.date('fiscalYearStart',fiscalYearStart,{ 'label' : 'pref_fiscal_year_start_label'|_ }) }}
</div>
{# conversion back to natiev #}
{# conversion back to native #}
{% if config('cer.enabled') %}
<div class="preferences-box">
<h3>{{ 'pref_convert_to_native'|_ }}</h3>
<p class="text-info">
@@ -108,6 +109,7 @@
</p>
{{ ExpandedForm.checkbox('convertToNative','1',convertToNative,{ 'label' : 'pref_convert_native_help'|_ }) }}
</div>
{% endif %}
</div>
{# general settings column B #}