Merge branch 'develop'

This commit is contained in:
github-actions 2024-12-30 16:07:29 +00:00
commit 31722477d4
832 changed files with 19816 additions and 14645 deletions

View File

@ -29,7 +29,7 @@ $paths = [
$current . '/../../database', $current . '/../../database',
$current . '/../../routes', $current . '/../../routes',
$current . '/../../tests', $current . '/../../tests',
$current . '/../../resources/lang', $current . '/../../resources/lang/en_US',
]; ];
$finder = PhpCsFixer\Finder::create() $finder = PhpCsFixer\Finder::create()

View File

@ -97,13 +97,13 @@
}, },
"type": "library", "type": "library",
"extra": { "extra": {
"branch-alias": {
"dev-main": "3.x-dev"
},
"phpstan": { "phpstan": {
"includes": [ "includes": [
"extension.neon" "extension.neon"
] ]
},
"branch-alias": {
"dev-main": "3.x-dev"
} }
}, },
"autoload": { "autoload": {
@ -406,16 +406,16 @@
}, },
{ {
"name": "friendsofphp/php-cs-fixer", "name": "friendsofphp/php-cs-fixer",
"version": "v3.65.0", "version": "v3.66.0",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer.git", "url": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer.git",
"reference": "79d4f3e77b250a7d8043d76c6af8f0695e8a469f" "reference": "5f5f2a142ff36b93c41885bca29cc5f861c013e6"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/PHP-CS-Fixer/PHP-CS-Fixer/zipball/79d4f3e77b250a7d8043d76c6af8f0695e8a469f", "url": "https://api.github.com/repos/PHP-CS-Fixer/PHP-CS-Fixer/zipball/5f5f2a142ff36b93c41885bca29cc5f861c013e6",
"reference": "79d4f3e77b250a7d8043d76c6af8f0695e8a469f", "reference": "5f5f2a142ff36b93c41885bca29cc5f861c013e6",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -441,7 +441,7 @@
"symfony/polyfill-mbstring": "^1.28", "symfony/polyfill-mbstring": "^1.28",
"symfony/polyfill-php80": "^1.28", "symfony/polyfill-php80": "^1.28",
"symfony/polyfill-php81": "^1.28", "symfony/polyfill-php81": "^1.28",
"symfony/process": "^5.4 || ^6.0 || ^7.0", "symfony/process": "^5.4 || ^6.0 || ^7.0 <7.2",
"symfony/stopwatch": "^5.4 || ^6.0 || ^7.0" "symfony/stopwatch": "^5.4 || ^6.0 || ^7.0"
}, },
"require-dev": { "require-dev": {
@ -497,7 +497,7 @@
], ],
"support": { "support": {
"issues": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/issues", "issues": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/issues",
"source": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/tree/v3.65.0" "source": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/tree/v3.66.0"
}, },
"funding": [ "funding": [
{ {
@ -505,7 +505,7 @@
"type": "github" "type": "github"
} }
], ],
"time": "2024-11-25T00:39:24+00:00" "time": "2024-12-29T13:46:23+00:00"
}, },
{ {
"name": "psr/container", "name": "psr/container",
@ -1369,12 +1369,12 @@
}, },
"type": "library", "type": "library",
"extra": { "extra": {
"thanks": {
"url": "https://github.com/symfony/contracts",
"name": "symfony/contracts"
},
"branch-alias": { "branch-alias": {
"dev-main": "3.5-dev" "dev-main": "3.5-dev"
},
"thanks": {
"name": "symfony/contracts",
"url": "https://github.com/symfony/contracts"
} }
}, },
"autoload": { "autoload": {
@ -1517,12 +1517,12 @@
}, },
"type": "library", "type": "library",
"extra": { "extra": {
"thanks": {
"url": "https://github.com/symfony/contracts",
"name": "symfony/contracts"
},
"branch-alias": { "branch-alias": {
"dev-main": "3.5-dev" "dev-main": "3.5-dev"
},
"thanks": {
"name": "symfony/contracts",
"url": "https://github.com/symfony/contracts"
} }
}, },
"autoload": { "autoload": {
@ -2246,16 +2246,16 @@
}, },
{ {
"name": "symfony/process", "name": "symfony/process",
"version": "v7.2.0", "version": "v7.1.8",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/symfony/process.git", "url": "https://github.com/symfony/process.git",
"reference": "d34b22ba9390ec19d2dd966c40aa9e8462f27a7e" "reference": "42783370fda6e538771f7c7a36e9fa2ee3a84892"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/symfony/process/zipball/d34b22ba9390ec19d2dd966c40aa9e8462f27a7e", "url": "https://api.github.com/repos/symfony/process/zipball/42783370fda6e538771f7c7a36e9fa2ee3a84892",
"reference": "d34b22ba9390ec19d2dd966c40aa9e8462f27a7e", "reference": "42783370fda6e538771f7c7a36e9fa2ee3a84892",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -2287,7 +2287,7 @@
"description": "Executes commands in sub-processes", "description": "Executes commands in sub-processes",
"homepage": "https://symfony.com", "homepage": "https://symfony.com",
"support": { "support": {
"source": "https://github.com/symfony/process/tree/v7.2.0" "source": "https://github.com/symfony/process/tree/v7.1.8"
}, },
"funding": [ "funding": [
{ {
@ -2303,7 +2303,7 @@
"type": "tidelift" "type": "tidelift"
} }
], ],
"time": "2024-11-06T14:24:19+00:00" "time": "2024-11-06T14:23:19+00:00"
}, },
{ {
"name": "symfony/service-contracts", "name": "symfony/service-contracts",
@ -2329,12 +2329,12 @@
}, },
"type": "library", "type": "library",
"extra": { "extra": {
"thanks": {
"url": "https://github.com/symfony/contracts",
"name": "symfony/contracts"
},
"branch-alias": { "branch-alias": {
"dev-main": "3.5-dev" "dev-main": "3.5-dev"
},
"thanks": {
"name": "symfony/contracts",
"url": "https://github.com/symfony/contracts"
} }
}, },
"autoload": { "autoload": {

View File

@ -26,8 +26,7 @@ SCRIPT_DIR="$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )
cd $SCRIPT_DIR/php-cs-fixer cd $SCRIPT_DIR/php-cs-fixer
composer update --quiet composer update --quiet
rm -f .php-cs-fixer.cache rm -f .php-cs-fixer.cache
PHP_CS_FIXER_IGNORE_ENV=true PHP_CS_FIXER_IGNORE_ENV=true ./vendor/bin/php-cs-fixer fix \
./vendor/bin/php-cs-fixer fix \
--config $SCRIPT_DIR/php-cs-fixer/.php-cs-fixer.php \ --config $SCRIPT_DIR/php-cs-fixer/.php-cs-fixer.php \
--format=txt \ --format=txt \
--allow-risky=yes --allow-risky=yes

View File

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

View File

@ -4,6 +4,7 @@ Over time, many people have contributed to Firefly III. Their efforts are not al
Please find below all the people who contributed to the Firefly III code. Their names are mentioned in the year of their first contribution. Please find below all the people who contributed to the Firefly III code. Their names are mentioned in the year of their first contribution.
## 2024 ## 2024
- TasneemTantawy
- Antônio Franco - Antônio Franco
- yparitcher - yparitcher
- Jhon Pedroza - Jhon Pedroza

View File

@ -26,10 +26,11 @@ namespace FireflyIII\Api\V1\Controllers\Autocomplete;
use FireflyIII\Api\V1\Controllers\Controller; use FireflyIII\Api\V1\Controllers\Controller;
use FireflyIII\Api\V1\Requests\Autocomplete\AutocompleteRequest; use FireflyIII\Api\V1\Requests\Autocomplete\AutocompleteRequest;
use FireflyIII\Enums\AccountTypeEnum;
use FireflyIII\Exceptions\FireflyException; use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Models\Account; use FireflyIII\Models\Account;
use FireflyIII\Models\AccountType;
use FireflyIII\Repositories\Account\AccountRepositoryInterface; use FireflyIII\Repositories\Account\AccountRepositoryInterface;
use FireflyIII\Support\Facades\Steam;
use FireflyIII\Support\Http\Api\AccountFilter; use FireflyIII\Support\Http\Api\AccountFilter;
use FireflyIII\User; use FireflyIII\User;
use Illuminate\Http\JsonResponse; use Illuminate\Http\JsonResponse;
@ -61,7 +62,7 @@ class AccountController extends Controller
return $next($request); return $next($request);
} }
); );
$this->balanceTypes = [AccountType::ASSET, AccountType::LOAN, AccountType::DEBT, AccountType::MORTGAGE]; $this->balanceTypes = [AccountTypeEnum::ASSET->value, AccountTypeEnum::LOAN->value, AccountTypeEnum::DEBT->value, AccountTypeEnum::MORTGAGE->value];
} }
/** /**
@ -80,20 +81,20 @@ class AccountController extends Controller
$return = []; $return = [];
$result = $this->repository->searchAccount((string) $query, $types, $this->parameters->get('limit')); $result = $this->repository->searchAccount((string) $query, $types, $this->parameters->get('limit'));
// TODO this code is duplicated in the V2 Autocomplete controller, which means this code is due to be deprecated.
$defaultCurrency = app('amount')->getDefaultCurrency();
/** @var Account $account */ /** @var Account $account */
foreach ($result as $account) { foreach ($result as $account) {
$nameWithBalance = $account->name; $nameWithBalance = $account->name;
$currency = $this->repository->getAccountCurrency($account) ?? $defaultCurrency; $currency = $this->repository->getAccountCurrency($account) ?? $this->defaultCurrency;
$useCurrency = $currency;
if (in_array($account->accountType->type, $this->balanceTypes, true)) { if (in_array($account->accountType->type, $this->balanceTypes, true)) {
$balance = app('steam')->balance($account, $date); $balance = Steam::finalAccountBalance($account, $date);
$key = $this->convertToNative && $currency->id !== $this->defaultCurrency->id ? 'native_balance' : 'balance';
$useCurrency = $this->convertToNative && $currency->id !== $this->defaultCurrency->id ? $this->defaultCurrency : $currency;
$amount = $balance[$key] ?? '0';
$nameWithBalance = sprintf( $nameWithBalance = sprintf(
'%s (%s)', '%s (%s)',
$account->name, $account->name,
app('amount')->formatAnything($currency, $balance, false) app('amount')->formatAnything($useCurrency, $amount, false)
); );
} }
@ -102,11 +103,11 @@ class AccountController extends Controller
'name' => $account->name, 'name' => $account->name,
'name_with_balance' => $nameWithBalance, 'name_with_balance' => $nameWithBalance,
'type' => $account->accountType->type, 'type' => $account->accountType->type,
'currency_id' => (string)$currency->id, 'currency_id' => (string) $useCurrency->id,
'currency_name' => $currency->name, 'currency_name' => $useCurrency->name,
'currency_code' => $currency->code, 'currency_code' => $useCurrency->code,
'currency_symbol' => $currency->symbol, 'currency_symbol' => $useCurrency->symbol,
'currency_decimal_places' => $currency->decimal_places, 'currency_decimal_places' => $useCurrency->decimal_places,
]; ];
} }
@ -114,7 +115,7 @@ class AccountController extends Controller
usort( usort(
$return, $return,
static function (array $left, array $right) { static function (array $left, array $right) {
$order = [AccountType::ASSET, AccountType::REVENUE, AccountType::EXPENSE]; $order = [AccountTypeEnum::ASSET->value, AccountTypeEnum::REVENUE->value, AccountTypeEnum::EXPENSE->value];
$posA = (int) array_search($left['type'], $order, true); $posA = (int) array_search($left['type'], $order, true);
$posB = (int) array_search($right['type'], $order, true); $posB = (int) array_search($right['type'], $order, true);

View File

@ -68,12 +68,11 @@ class PiggyBankController extends Controller
{ {
$data = $request->getData(); $data = $request->getData();
$piggies = $this->piggyRepository->searchPiggyBank($data['query'], $this->parameters->get('limit')); $piggies = $this->piggyRepository->searchPiggyBank($data['query'], $this->parameters->get('limit'));
$defaultCurrency = app('amount')->getDefaultCurrency();
$response = []; $response = [];
/** @var PiggyBank $piggy */ /** @var PiggyBank $piggy */
foreach ($piggies as $piggy) { foreach ($piggies as $piggy) {
$currency = $this->accountRepository->getAccountCurrency($piggy->account) ?? $defaultCurrency; $currency = $piggy->transactionCurrency;
$objectGroup = $piggy->objectGroups()->first(); $objectGroup = $piggy->objectGroups()->first();
$response[] = [ $response[] = [
'id' => (string) $piggy->id, 'id' => (string) $piggy->id,
@ -99,13 +98,12 @@ class PiggyBankController extends Controller
{ {
$data = $request->getData(); $data = $request->getData();
$piggies = $this->piggyRepository->searchPiggyBank($data['query'], $this->parameters->get('limit')); $piggies = $this->piggyRepository->searchPiggyBank($data['query'], $this->parameters->get('limit'));
$defaultCurrency = app('amount')->getDefaultCurrency();
$response = []; $response = [];
/** @var PiggyBank $piggy */ /** @var PiggyBank $piggy */
foreach ($piggies as $piggy) { foreach ($piggies as $piggy) {
$currency = $this->accountRepository->getAccountCurrency($piggy->account) ?? $defaultCurrency; $currency = $piggy->transactionCurrency;
$currentAmount = $this->piggyRepository->getRepetition($piggy)->currentamount ?? '0'; $currentAmount = $this->piggyRepository->getCurrentAmount($piggy);
$objectGroup = $piggy->objectGroups()->first(); $objectGroup = $piggy->objectGroups()->first();
$response[] = [ $response[] = [
'id' => (string) $piggy->id, 'id' => (string) $piggy->id,
@ -114,7 +112,7 @@ class PiggyBankController extends Controller
'%s (%s / %s)', '%s (%s / %s)',
$piggy->name, $piggy->name,
app('amount')->formatAnything($currency, $currentAmount, false), app('amount')->formatAnything($currency, $currentAmount, false),
app('amount')->formatAnything($currency, $piggy->targetamount, false), app('amount')->formatAnything($currency, $piggy->target_amount, false),
), ),
'currency_id' => (string) $currency->id, 'currency_id' => (string) $currency->id,
'currency_name' => $currency->name, 'currency_name' => $currency->name,

View File

@ -27,9 +27,9 @@ namespace FireflyIII\Api\V1\Controllers\Chart;
use Carbon\Carbon; use Carbon\Carbon;
use FireflyIII\Api\V1\Controllers\Controller; use FireflyIII\Api\V1\Controllers\Controller;
use FireflyIII\Api\V1\Requests\Data\DateRequest; use FireflyIII\Api\V1\Requests\Data\DateRequest;
use FireflyIII\Enums\AccountTypeEnum;
use FireflyIII\Exceptions\FireflyException; use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Models\Account; use FireflyIII\Models\Account;
use FireflyIII\Models\AccountType;
use FireflyIII\Models\Preference; use FireflyIII\Models\Preference;
use FireflyIII\Repositories\Account\AccountRepositoryInterface; use FireflyIII\Repositories\Account\AccountRepositoryInterface;
use FireflyIII\Support\Http\Api\ApiSupport; use FireflyIII\Support\Http\Api\ApiSupport;
@ -81,11 +81,10 @@ class AccountController extends Controller
$end = $dates['end']; $end = $dates['end'];
// user's preferences // user's preferences
$defaultSet = $this->repository->getAccountsByType([AccountType::ASSET])->pluck('id')->toArray(); $defaultSet = $this->repository->getAccountsByType([AccountTypeEnum::ASSET->value])->pluck('id')->toArray();
/** @var Preference $frontpage */ /** @var Preference $frontpage */
$frontpage = app('preferences')->get('frontpageAccounts', $defaultSet); $frontpage = app('preferences')->get('frontpageAccounts', $defaultSet);
$default = app('amount')->getDefaultCurrency();
if (!(is_array($frontpage->data) && count($frontpage->data) > 0)) { if (!(is_array($frontpage->data) && count($frontpage->data) > 0)) {
$frontpage->data = $defaultSet; $frontpage->data = $defaultSet;
@ -98,10 +97,8 @@ class AccountController extends Controller
/** @var Account $account */ /** @var Account $account */
foreach ($accounts as $account) { foreach ($accounts as $account) {
$currency = $this->repository->getAccountCurrency($account); $currency = $this->repository->getAccountCurrency($account) ?? $this->defaultCurrency;
if (null === $currency) { $field = $this->convertToNative && $currency->id !== $this->defaultCurrency->id ? 'native_balance' : 'balance';
$currency = $default;
}
$currentSet = [ $currentSet = [
'label' => $account->name, 'label' => $account->name,
'currency_id' => (string) $currency->id, 'currency_id' => (string) $currency->id,
@ -116,13 +113,12 @@ class AccountController extends Controller
]; ];
// TODO this code is also present in the V2 chart account controller so this method is due to be deprecated. // TODO this code is also present in the V2 chart account controller so this method is due to be deprecated.
$currentStart = clone $start; $currentStart = clone $start;
$range = app('steam')->balanceInRange($account, $start, clone $end); $range = app('steam')->finalAccountBalanceInRange($account, $start, clone $end, $this->convertToNative);
// 2022-10-11 this method no longer converts to float. $previous = array_values($range)[0][$field];
$previous = array_values($range)[0];
while ($currentStart <= $end) { while ($currentStart <= $end) {
$format = $currentStart->format('Y-m-d'); $format = $currentStart->format('Y-m-d');
$label = $currentStart->toAtomString(); $label = $currentStart->toAtomString();
$balance = array_key_exists($format, $range) ? $range[$format] : $previous; $balance = array_key_exists($format, $range) ? $range[$format][$field] : $previous;
$previous = $balance; $previous = $balance;
$currentStart->addDay(); $currentStart->addDay();
$currentSet['entries'][$label] = $balance; $currentSet['entries'][$label] = $balance;

View File

@ -28,6 +28,9 @@ use Carbon\Carbon;
use Carbon\Exceptions\InvalidDateException; use Carbon\Exceptions\InvalidDateException;
use Carbon\Exceptions\InvalidFormatException; use Carbon\Exceptions\InvalidFormatException;
use FireflyIII\Models\Preference; use FireflyIII\Models\Preference;
use FireflyIII\Models\TransactionCurrency;
use FireflyIII\Support\Facades\Amount;
use FireflyIII\Support\Facades\Steam;
use FireflyIII\User; use FireflyIII\User;
use Illuminate\Foundation\Auth\Access\AuthorizesRequests; use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
use Illuminate\Foundation\Bus\DispatchesJobs; use Illuminate\Foundation\Bus\DispatchesJobs;
@ -55,6 +58,8 @@ abstract class Controller extends BaseController
/** @var array<int, string> */ /** @var array<int, string> */
protected array $allowedSort; protected array $allowedSort;
protected ParameterBag $parameters; protected ParameterBag $parameters;
protected bool $convertToNative = false;
protected TransactionCurrency $defaultCurrency;
/** /**
* Controller constructor. * Controller constructor.
@ -67,8 +72,11 @@ abstract class Controller extends BaseController
function ($request, $next) { function ($request, $next) {
$this->parameters = $this->getParameters(); $this->parameters = $this->getParameters();
if (auth()->check()) { if (auth()->check()) {
$language = app('steam')->getLanguage(); $language = Steam::getLanguage();
$this->convertToNative = Amount::convertToNative();
$this->defaultCurrency = Amount::getDefaultCurrency();
app()->setLocale($language); app()->setLocale($language);
} }
return $next($request); return $next($request);

View File

@ -26,10 +26,10 @@ namespace FireflyIII\Api\V1\Controllers\Data;
use FireflyIII\Api\V1\Controllers\Controller; use FireflyIII\Api\V1\Controllers\Controller;
use FireflyIII\Api\V1\Requests\Data\DestroyRequest; use FireflyIII\Api\V1\Requests\Data\DestroyRequest;
use FireflyIII\Enums\AccountTypeEnum;
use FireflyIII\Enums\TransactionTypeEnum; use FireflyIII\Enums\TransactionTypeEnum;
use FireflyIII\Exceptions\FireflyException; use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Models\Account; use FireflyIII\Models\Account;
use FireflyIII\Models\AccountType;
use FireflyIII\Models\TransactionJournal; use FireflyIII\Models\TransactionJournal;
use FireflyIII\Repositories\Account\AccountRepositoryInterface; use FireflyIII\Repositories\Account\AccountRepositoryInterface;
use FireflyIII\Repositories\Bill\BillRepositoryInterface; use FireflyIII\Repositories\Bill\BillRepositoryInterface;
@ -66,9 +66,9 @@ class DestroyController extends Controller
$objects = $request->getObjects(); $objects = $request->getObjects();
$this->unused = $request->boolean('unused', false); $this->unused = $request->boolean('unused', false);
$allExceptAssets = [AccountType::BENEFICIARY, AccountType::CASH, AccountType::CREDITCARD, AccountType::DEFAULT, AccountType::EXPENSE, AccountType::IMPORT, AccountType::INITIAL_BALANCE, AccountType::LIABILITY_CREDIT, AccountType::RECONCILIATION, AccountType::REVENUE]; $allExceptAssets = [AccountTypeEnum::BENEFICIARY->value, AccountTypeEnum::CASH->value, AccountTypeEnum::CREDITCARD->value, AccountTypeEnum::DEFAULT->value, AccountTypeEnum::EXPENSE->value, AccountTypeEnum::IMPORT->value, AccountTypeEnum::INITIAL_BALANCE->value, AccountTypeEnum::LIABILITY_CREDIT->value, AccountTypeEnum::RECONCILIATION->value, AccountTypeEnum::REVENUE->value];
$all = [AccountType::ASSET, AccountType::BENEFICIARY, AccountType::CASH, AccountType::CREDITCARD, AccountType::DEBT, AccountType::DEFAULT, AccountType::EXPENSE, AccountType::IMPORT, AccountType::INITIAL_BALANCE, AccountType::LIABILITY_CREDIT, AccountType::LOAN, AccountType::MORTGAGE, AccountType::RECONCILIATION]; $all = [AccountTypeEnum::ASSET->value, AccountTypeEnum::BENEFICIARY->value, AccountTypeEnum::CASH->value, AccountTypeEnum::CREDITCARD->value, AccountTypeEnum::DEBT->value, AccountTypeEnum::DEFAULT->value, AccountTypeEnum::EXPENSE->value, AccountTypeEnum::IMPORT->value, AccountTypeEnum::INITIAL_BALANCE->value, AccountTypeEnum::LIABILITY_CREDIT->value, AccountTypeEnum::LOAN->value, AccountTypeEnum::MORTGAGE->value, AccountTypeEnum::RECONCILIATION->value];
$liabilities = [AccountType::DEBT, AccountType::LOAN, AccountType::MORTGAGE, AccountType::CREDITCARD]; $liabilities = [AccountTypeEnum::DEBT->value, AccountTypeEnum::LOAN->value, AccountTypeEnum::MORTGAGE->value, AccountTypeEnum::CREDITCARD->value];
$transactions = [TransactionTypeEnum::WITHDRAWAL->value, TransactionTypeEnum::DEPOSIT->value, TransactionTypeEnum::TRANSFER->value, TransactionTypeEnum::RECONCILIATION->value]; $transactions = [TransactionTypeEnum::WITHDRAWAL->value, TransactionTypeEnum::DEPOSIT->value, TransactionTypeEnum::TRANSFER->value, TransactionTypeEnum::RECONCILIATION->value];
match ($objects) { match ($objects) {
@ -82,9 +82,9 @@ class DestroyController extends Controller
'object_groups' => $this->destroyObjectGroups(), 'object_groups' => $this->destroyObjectGroups(),
'not_assets_liabilities' => $this->destroyAccounts($allExceptAssets), 'not_assets_liabilities' => $this->destroyAccounts($allExceptAssets),
'accounts' => $this->destroyAccounts($all), 'accounts' => $this->destroyAccounts($all),
'asset_accounts' => $this->destroyAccounts([AccountType::ASSET, AccountType::DEFAULT]), 'asset_accounts' => $this->destroyAccounts([AccountTypeEnum::ASSET->value, AccountTypeEnum::DEFAULT->value]),
'expense_accounts' => $this->destroyAccounts([AccountType::BENEFICIARY, AccountType::EXPENSE]), 'expense_accounts' => $this->destroyAccounts([AccountTypeEnum::BENEFICIARY->value, AccountTypeEnum::EXPENSE->value]),
'revenue_accounts' => $this->destroyAccounts([AccountType::REVENUE]), 'revenue_accounts' => $this->destroyAccounts([AccountTypeEnum::REVENUE->value]),
'liabilities' => $this->destroyAccounts($liabilities), 'liabilities' => $this->destroyAccounts($liabilities),
'transactions' => $this->destroyTransactions($transactions), 'transactions' => $this->destroyTransactions($transactions),
'withdrawals' => $this->destroyTransactions([TransactionTypeEnum::WITHDRAWAL->value]), 'withdrawals' => $this->destroyTransactions([TransactionTypeEnum::WITHDRAWAL->value]),

View File

@ -29,13 +29,13 @@ use FireflyIII\Models\Account;
use FireflyIII\Models\Bill; use FireflyIII\Models\Bill;
use FireflyIII\Models\Budget; use FireflyIII\Models\Budget;
use FireflyIII\Models\Category; use FireflyIII\Models\Category;
use FireflyIII\Models\PiggyBank;
use FireflyIII\Models\Recurrence; use FireflyIII\Models\Recurrence;
use FireflyIII\Models\Rule; use FireflyIII\Models\Rule;
use FireflyIII\Models\RuleGroup; use FireflyIII\Models\RuleGroup;
use FireflyIII\Models\Tag; use FireflyIII\Models\Tag;
use FireflyIII\Models\TransactionGroup; use FireflyIII\Models\TransactionGroup;
use FireflyIII\Models\TransactionJournal; use FireflyIII\Models\TransactionJournal;
use FireflyIII\Repositories\PiggyBank\PiggyBankRepositoryInterface;
use FireflyIII\User; use FireflyIII\User;
use Illuminate\Http\JsonResponse; use Illuminate\Http\JsonResponse;
@ -63,14 +63,17 @@ class PurgeController extends Controller
Bill::whereUserId($user->id)->onlyTrashed()->forceDelete(); Bill::whereUserId($user->id)->onlyTrashed()->forceDelete();
// piggies // piggies
$set = PiggyBank::leftJoin('accounts', 'accounts.id', 'piggy_banks.account_id') $repository = app(PiggyBankRepositoryInterface::class);
->where('accounts.user_id', $user->id)->onlyTrashed()->get(['piggy_banks.*']) $repository->setUser($user);
; $repository->purgeAll();
// $set = PiggyBank::leftJoin('accounts', 'accounts.id', 'piggy_banks.account_id')
/** @var PiggyBank $piggy */ // ->where('accounts.user_id', $user->id)->onlyTrashed()->get(['piggy_banks.*'])
foreach ($set as $piggy) { // ;
$piggy->forceDelete(); //
} // /** @var PiggyBank $piggy */
// foreach ($set as $piggy) {
// $piggy->forceDelete();
// }
// rule group // rule group
RuleGroup::whereUserId($user->id)->onlyTrashed()->forceDelete(); RuleGroup::whereUserId($user->id)->onlyTrashed()->forceDelete();

View File

@ -29,7 +29,9 @@ use FireflyIII\Api\V1\Requests\Insight\GenericRequest;
use FireflyIII\Enums\TransactionTypeEnum; use FireflyIII\Enums\TransactionTypeEnum;
use FireflyIII\Helpers\Collector\GroupCollectorInterface; use FireflyIII\Helpers\Collector\GroupCollectorInterface;
use FireflyIII\Repositories\Bill\BillRepositoryInterface; use FireflyIII\Repositories\Bill\BillRepositoryInterface;
use FireflyIII\Support\Facades\Amount;
use Illuminate\Http\JsonResponse; use Illuminate\Http\JsonResponse;
use Illuminate\Support\Facades\Log;
/** /**
* Class BillController * Class BillController
@ -67,6 +69,8 @@ class BillController extends Controller
$bills = $request->getBills(); $bills = $request->getBills();
$start = $request->getStart(); $start = $request->getStart();
$end = $request->getEnd(); $end = $request->getEnd();
$convertToNative = Amount::convertToNative();
$default = Amount::getDefaultCurrency();
$response = []; $response = [];
// get all bills: // get all bills:
@ -83,9 +87,22 @@ class BillController extends Controller
foreach ($genericSet as $journal) { foreach ($genericSet as $journal) {
$billId = (int) $journal['bill_id']; $billId = (int) $journal['bill_id'];
$currencyId = (int) $journal['currency_id']; $currencyId = (int) $journal['currency_id'];
$foreignCurrencyId = (int)$journal['foreign_currency_id']; $currencyCode = $journal['currency_code'];
$field = 'amount';
// use the native amount if the user wants to convert to native currency
if ($convertToNative && $currencyId !== $default->id) {
$currencyId = $default->id;
$currencyCode = $default->code;
$field = 'native_amount';
}
// use foreign amount when the foreign currency IS the default currency.
if ($convertToNative && $journal['currency_id'] !== $default->id && $default->id === $journal['foreign_currency_id']) {
$field = 'foreign_amount';
}
Log::debug(sprintf('Journal #%d in bill #%d will use %s (%s %s)', $journal['transaction_group_id'], $billId, $field, $currencyCode, $journal[$field] ?? '0'));
$key = sprintf('%d-%d', $billId, $currencyId); $key = sprintf('%d-%d', $billId, $currencyId);
$foreignKey = sprintf('%d-%d', $billId, $foreignCurrencyId);
if (0 !== $currencyId) { if (0 !== $currencyId) {
$response[$key] ??= [ $response[$key] ??= [
@ -94,21 +111,11 @@ class BillController extends Controller
'difference' => '0', 'difference' => '0',
'difference_float' => 0, 'difference_float' => 0,
'currency_id' => (string) $currencyId, 'currency_id' => (string) $currencyId,
'currency_code' => $journal['currency_code'], 'currency_code' => $currencyCode,
]; ];
$response[$key]['difference'] = bcadd($response[$key]['difference'], $journal['amount']); $response[$key]['difference'] = bcadd($response[$key]['difference'], (string) ($journal[$field] ?? '0'));
$response[$key]['difference_float'] = (float) $response[$key]['difference']; // intentional float $response[$key]['difference_float'] = (float) $response[$key]['difference']; // intentional float
} }
if (0 !== $foreignCurrencyId) {
$response[$foreignKey] ??= [
'difference' => '0',
'difference_float' => 0,
'currency_id' => (string)$foreignCurrencyId,
'currency_code' => $journal['foreign_currency_code'],
];
$response[$foreignKey]['difference'] = bcadd($response[$foreignKey]['difference'], $journal['foreign_amount']);
$response[$foreignKey]['difference_float'] = (float)$response[$foreignKey]['difference']; // intentional float
}
} }
return response()->json(array_values($response)); return response()->json(array_values($response));
@ -125,6 +132,8 @@ class BillController extends Controller
$accounts = $request->getAssetAccounts(); $accounts = $request->getAssetAccounts();
$start = $request->getStart(); $start = $request->getStart();
$end = $request->getEnd(); $end = $request->getEnd();
$convertToNative = Amount::convertToNative();
$default = Amount::getDefaultCurrency();
$response = []; $response = [];
// collect all expenses in this period (regardless of type) by the given bills and accounts. // collect all expenses in this period (regardless of type) by the given bills and accounts.
@ -136,28 +145,31 @@ class BillController extends Controller
foreach ($genericSet as $journal) { foreach ($genericSet as $journal) {
$currencyId = (int) $journal['currency_id']; $currencyId = (int) $journal['currency_id'];
$foreignCurrencyId = (int)$journal['foreign_currency_id']; $currencyCode = $journal['currency_code'];
$field = 'amount';
// use the native amount if the user wants to convert to native currency
if ($convertToNative && $currencyId !== $default->id) {
$currencyId = $default->id;
$currencyCode = $default->code;
$field = 'native_amount';
}
// use foreign amount when the foreign currency IS the default currency.
if ($convertToNative && $journal['currency_id'] !== $default->id && $default->id === $journal['foreign_currency_id']) {
$field = 'foreign_amount';
}
Log::debug(sprintf('Journal #%d will use %s (%s %s)', $journal['transaction_group_id'], $field, $currencyCode, $journal[$field] ?? '0'));
if (0 !== $currencyId) { if (0 !== $currencyId) {
$response[$currencyId] ??= [ $response[$currencyId] ??= [
'difference' => '0', 'difference' => '0',
'difference_float' => 0, 'difference_float' => 0,
'currency_id' => (string) $currencyId, 'currency_id' => (string) $currencyId,
'currency_code' => $journal['currency_code'], 'currency_code' => $currencyCode,
]; ];
$response[$currencyId]['difference'] = bcadd($response[$currencyId]['difference'], $journal['amount']); $response[$currencyId]['difference'] = bcadd($response[$currencyId]['difference'], (string) ($journal[$field] ?? '0'));
$response[$currencyId]['difference_float'] = (float) $response[$currencyId]['difference']; // intentional float $response[$currencyId]['difference_float'] = (float) $response[$currencyId]['difference']; // intentional float
} }
if (0 !== $foreignCurrencyId) {
$response[$foreignCurrencyId] ??= [
'difference' => '0',
'difference_float' => 0,
'currency_id' => (string)$foreignCurrencyId,
'currency_code' => $journal['foreign_currency_code'],
];
$response[$foreignCurrencyId]['difference'] = bcadd($response[$foreignCurrencyId]['difference'], $journal['foreign_amount']);
$response[$foreignCurrencyId]['difference_float'] = (float)$response[$foreignCurrencyId]['difference']; // intentional float
}
} }
return response()->json(array_values($response)); return response()->json(array_values($response));

View File

@ -28,7 +28,9 @@ use FireflyIII\Api\V1\Controllers\Controller;
use FireflyIII\Api\V1\Requests\Insight\GenericRequest; use FireflyIII\Api\V1\Requests\Insight\GenericRequest;
use FireflyIII\Enums\TransactionTypeEnum; use FireflyIII\Enums\TransactionTypeEnum;
use FireflyIII\Helpers\Collector\GroupCollectorInterface; use FireflyIII\Helpers\Collector\GroupCollectorInterface;
use FireflyIII\Support\Facades\Amount;
use Illuminate\Http\JsonResponse; use Illuminate\Http\JsonResponse;
use Illuminate\Support\Facades\Log;
/** /**
* Class PeriodController * Class PeriodController
@ -45,36 +47,46 @@ class PeriodController extends Controller
$start = $request->getStart(); $start = $request->getStart();
$end = $request->getEnd(); $end = $request->getEnd();
$response = []; $response = [];
$convertToNative = Amount::convertToNative();
$default = Amount::getDefaultCurrency();
// collect all expenses in this period (regardless of type) // collect all expenses in this period (regardless of type)
$collector = app(GroupCollectorInterface::class); $collector = app(GroupCollectorInterface::class);
$collector->setTypes([TransactionTypeEnum::WITHDRAWAL->value])->setRange($start, $end)->setSourceAccounts($accounts); $collector->setTypes([TransactionTypeEnum::WITHDRAWAL->value])->setRange($start, $end)->setSourceAccounts($accounts);
$genericSet = $collector->getExtractedJournals(); $genericSet = $collector->getExtractedJournals();
foreach ($genericSet as $journal) { foreach ($genericSet as $journal) {
// same code as many other sumExpense methods. I think this needs some kind of generic method.
$amount = '0';
$currencyId = (int) $journal['currency_id']; $currencyId = (int) $journal['currency_id'];
$foreignCurrencyId = (int)$journal['foreign_currency_id']; $currencyCode = $journal['currency_code'];
if ($convertToNative) {
$amount = Amount::getAmountFromJournal($journal);
if ($default->id !== (int) $journal['currency_id'] && $default->id !== (int) $journal['foreign_currency_id']) {
$currencyId = $default->id;
$currencyCode = $default->code;
}
if ($default->id !== (int) $journal['currency_id'] && $default->id === (int) $journal['foreign_currency_id']) {
$currencyId = $journal['foreign_currency_id'];
$currencyCode = $journal['foreign_currency_code'];
}
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'];
}
if (0 !== $currencyId) {
$response[$currencyId] ??= [ $response[$currencyId] ??= [
'difference' => '0', 'difference' => '0',
'difference_float' => 0, 'difference_float' => 0,
'currency_id' => (string) $currencyId, 'currency_id' => (string) $currencyId,
'currency_code' => $journal['currency_code'], 'currency_code' => $currencyCode,
]; ];
$response[$currencyId]['difference'] = bcadd($response[$currencyId]['difference'], $journal['amount']); $response[$currencyId]['difference'] = bcadd($response[$currencyId]['difference'], $amount);
$response[$currencyId]['difference_float'] = (float) $response[$currencyId]['difference']; // intentional float $response[$currencyId]['difference_float'] = (float) $response[$currencyId]['difference']; // intentional float
} }
if (0 !== $foreignCurrencyId) {
$response[$foreignCurrencyId] ??= [
'difference' => '0',
'difference_float' => 0,
'currency_id' => (string)$foreignCurrencyId,
'currency_code' => $journal['foreign_currency_code'],
];
$response[$foreignCurrencyId]['difference'] = bcadd($response[$foreignCurrencyId]['difference'], $journal['foreign_amount']);
$response[$foreignCurrencyId]['difference_float'] = (float)$response[$foreignCurrencyId]['difference']; // intentional float
}
}
return response()->json(array_values($response)); return response()->json(array_values($response));
} }

View File

@ -29,7 +29,9 @@ use FireflyIII\Api\V1\Requests\Insight\GenericRequest;
use FireflyIII\Enums\TransactionTypeEnum; use FireflyIII\Enums\TransactionTypeEnum;
use FireflyIII\Helpers\Collector\GroupCollectorInterface; use FireflyIII\Helpers\Collector\GroupCollectorInterface;
use FireflyIII\Repositories\Tag\TagRepositoryInterface; use FireflyIII\Repositories\Tag\TagRepositoryInterface;
use FireflyIII\Support\Facades\Amount;
use Illuminate\Http\JsonResponse; use Illuminate\Http\JsonResponse;
use Illuminate\Support\Facades\Log;
/** /**
* Class TagController * Class TagController
@ -66,6 +68,8 @@ class TagController extends Controller
$start = $request->getStart(); $start = $request->getStart();
$end = $request->getEnd(); $end = $request->getEnd();
$response = []; $response = [];
$convertToNative = Amount::convertToNative();
$default = Amount::getDefaultCurrency();
// collect all expenses in this period (regardless of type) by the given bills and accounts. // collect all expenses in this period (regardless of type) by the given bills and accounts.
$collector = app(GroupCollectorInterface::class); $collector = app(GroupCollectorInterface::class);
@ -75,30 +79,37 @@ class TagController extends Controller
$genericSet = $collector->getExtractedJournals(); $genericSet = $collector->getExtractedJournals();
foreach ($genericSet as $journal) { foreach ($genericSet as $journal) {
// same code as many other sumExpense methods. I think this needs some kind of generic method.
$amount = '0';
$currencyId = (int) $journal['currency_id']; $currencyId = (int) $journal['currency_id'];
$foreignCurrencyId = (int)$journal['foreign_currency_id']; $currencyCode = $journal['currency_code'];
if ($convertToNative) {
$amount = Amount::getAmountFromJournal($journal);
if ($default->id !== (int) $journal['currency_id'] && $default->id !== (int) $journal['foreign_currency_id']) {
$currencyId = $default->id;
$currencyCode = $default->code;
}
if ($default->id !== (int) $journal['currency_id'] && $default->id === (int) $journal['foreign_currency_id']) {
$currencyId = $journal['foreign_currency_id'];
$currencyCode = $journal['foreign_currency_code'];
}
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'];
}
if (0 !== $currencyId) {
$response[$currencyId] ??= [ $response[$currencyId] ??= [
'difference' => '0', 'difference' => '0',
'difference_float' => 0, 'difference_float' => 0,
'currency_id' => (string) $currencyId, 'currency_id' => (string) $currencyId,
'currency_code' => $journal['currency_code'], 'currency_code' => $currencyCode,
]; ];
$response[$currencyId]['difference'] = bcadd($response[$currencyId]['difference'], $journal['amount']); $response[$currencyId]['difference'] = bcadd($response[$currencyId]['difference'], $amount);
$response[$currencyId]['difference_float'] = (float) $response[$currencyId]['difference']; // float but on purpose. $response[$currencyId]['difference_float'] = (float) $response[$currencyId]['difference']; // float but on purpose.
} }
if (0 !== $foreignCurrencyId) {
$response[$foreignCurrencyId] ??= [
'difference' => '0',
'difference_float' => 0,
'currency_id' => (string)$foreignCurrencyId,
'currency_code' => $journal['foreign_currency_code'],
];
$response[$foreignCurrencyId]['difference'] = bcadd($response[$foreignCurrencyId]['difference'], $journal['foreign_amount']);
$response[$foreignCurrencyId]['difference_float'] = (float)$response[$foreignCurrencyId]['difference']; // float but on purpose.
}
}
return response()->json(array_values($response)); return response()->json(array_values($response));
} }

View File

@ -73,6 +73,7 @@ class AccountController extends Controller
$start = $request->getStart(); $start = $request->getStart();
$end = $request->getEnd(); $end = $request->getEnd();
$assetAccounts = $request->getAssetAccounts(); $assetAccounts = $request->getAssetAccounts();
$income = $this->opsRepository->sumIncomeByDestination($start, $end, $assetAccounts); $income = $this->opsRepository->sumIncomeByDestination($start, $end, $assetAccounts);
$result = []; $result = [];

View File

@ -28,6 +28,7 @@ use FireflyIII\Api\V1\Controllers\Controller;
use FireflyIII\Api\V1\Requests\Insight\GenericRequest; use FireflyIII\Api\V1\Requests\Insight\GenericRequest;
use FireflyIII\Enums\TransactionTypeEnum; use FireflyIII\Enums\TransactionTypeEnum;
use FireflyIII\Helpers\Collector\GroupCollectorInterface; use FireflyIII\Helpers\Collector\GroupCollectorInterface;
use FireflyIII\Support\Facades\Amount;
use Illuminate\Http\JsonResponse; use Illuminate\Http\JsonResponse;
/** /**
@ -45,39 +46,38 @@ class PeriodController extends Controller
$start = $request->getStart(); $start = $request->getStart();
$end = $request->getEnd(); $end = $request->getEnd();
$response = []; $response = [];
$convertToNative = Amount::convertToNative();
$default = Amount::getDefaultCurrency();
// collect all expenses in this period (regardless of type) // collect all expenses in this period (regardless of type)
$collector = app(GroupCollectorInterface::class); $collector = app(GroupCollectorInterface::class);
$collector->setTypes([TransactionTypeEnum::DEPOSIT->value])->setRange($start, $end)->setDestinationAccounts($accounts); $collector->setTypes([TransactionTypeEnum::DEPOSIT->value])->setRange($start, $end)->setDestinationAccounts($accounts);
$genericSet = $collector->getExtractedJournals(); $genericSet = $collector->getExtractedJournals();
foreach ($genericSet as $journal) { foreach ($genericSet as $journal) {
$currencyId = (int)$journal['currency_id']; // currency
$foreignCurrencyId = (int)$journal['foreign_currency_id']; $currencyId = $journal['currency_id'];
$currencyCode = $journal['currency_code'];
$field = $convertToNative && $currencyId !== $default->id ? 'native_amount' : 'amount';
// perhaps use default currency instead?
if ($convertToNative && $journal['currency_id'] !== $default->id) {
$currencyId = $default->id;
$currencyCode = $default->code;
}
// use foreign amount when the foreign currency IS the default currency.
if ($convertToNative && $journal['currency_id'] !== $default->id && $default->id === $journal['foreign_currency_id']) {
$field = 'foreign_amount';
}
if (0 !== $currencyId) {
$response[$currencyId] ??= [ $response[$currencyId] ??= [
'difference' => '0', 'difference' => '0',
'difference_float' => 0, 'difference_float' => 0,
'currency_id' => (string) $currencyId, 'currency_id' => (string) $currencyId,
'currency_code' => $journal['currency_code'], 'currency_code' => $currencyCode,
]; ];
$response[$currencyId]['difference'] = bcadd($response[$currencyId]['difference'], app('steam')->positive($journal['amount'])); $response[$currencyId]['difference'] = bcadd($response[$currencyId]['difference'], app('steam')->positive($journal[$field]));
$response[$currencyId]['difference_float'] = (float) $response[$currencyId]['difference']; // float but on purpose. $response[$currencyId]['difference_float'] = (float) $response[$currencyId]['difference']; // float but on purpose.
} }
if (0 !== $foreignCurrencyId) {
$response[$foreignCurrencyId] ??= [
'difference' => '0',
'difference_float' => 0,
'currency_id' => (string)$foreignCurrencyId,
'currency_code' => $journal['foreign_currency_code'],
];
$response[$foreignCurrencyId]['difference'] = bcadd(
$response[$foreignCurrencyId]['difference'],
app('steam')->positive($journal['foreign_amount'])
);
$response[$foreignCurrencyId]['difference_float'] = (float)$response[$foreignCurrencyId]['difference']; // float but on purpose.
}
}
return response()->json(array_values($response)); return response()->json(array_values($response));
} }

View File

@ -29,6 +29,7 @@ use FireflyIII\Api\V1\Requests\Insight\GenericRequest;
use FireflyIII\Enums\TransactionTypeEnum; use FireflyIII\Enums\TransactionTypeEnum;
use FireflyIII\Helpers\Collector\GroupCollectorInterface; use FireflyIII\Helpers\Collector\GroupCollectorInterface;
use FireflyIII\Repositories\Tag\TagRepositoryInterface; use FireflyIII\Repositories\Tag\TagRepositoryInterface;
use FireflyIII\Support\Facades\Amount;
use Illuminate\Http\JsonResponse; use Illuminate\Http\JsonResponse;
/** /**
@ -67,6 +68,8 @@ class TagController extends Controller
$start = $request->getStart(); $start = $request->getStart();
$end = $request->getEnd(); $end = $request->getEnd();
$response = []; $response = [];
$convertToNative = Amount::convertToNative();
$default = Amount::getDefaultCurrency();
// collect all expenses in this period (regardless of type) by the given bills and accounts. // collect all expenses in this period (regardless of type) by the given bills and accounts.
$collector = app(GroupCollectorInterface::class); $collector = app(GroupCollectorInterface::class);
@ -76,32 +79,30 @@ class TagController extends Controller
$genericSet = $collector->getExtractedJournals(); $genericSet = $collector->getExtractedJournals();
foreach ($genericSet as $journal) { foreach ($genericSet as $journal) {
$currencyId = (int)$journal['currency_id']; // currency
$foreignCurrencyId = (int)$journal['foreign_currency_id']; $currencyId = $journal['currency_id'];
$currencyCode = $journal['currency_code'];
$field = $convertToNative && $currencyId !== $default->id ? 'native_amount' : 'amount';
// perhaps use default currency instead?
if ($convertToNative && $journal['currency_id'] !== $default->id) {
$currencyId = $default->id;
$currencyCode = $default->code;
}
// use foreign amount when the foreign currency IS the default currency.
if ($convertToNative && $journal['currency_id'] !== $default->id && $default->id === $journal['foreign_currency_id']) {
$field = 'foreign_amount';
}
if (0 !== $currencyId) {
$response[$currencyId] ??= [ $response[$currencyId] ??= [
'difference' => '0', 'difference' => '0',
'difference_float' => 0, 'difference_float' => 0,
'currency_id' => (string) $currencyId, 'currency_id' => (string) $currencyId,
'currency_code' => $journal['currency_code'], 'currency_code' => $currencyCode,
]; ];
$response[$currencyId]['difference'] = bcadd($response[$currencyId]['difference'], app('steam')->positive($journal['amount'])); $response[$currencyId]['difference'] = bcadd($response[$currencyId]['difference'], app('steam')->positive($journal[$field]));
$response[$currencyId]['difference_float'] = (float) $response[$currencyId]['difference']; $response[$currencyId]['difference_float'] = (float) $response[$currencyId]['difference'];
}
if (0 !== $foreignCurrencyId) {
$response[$foreignCurrencyId] ??= [
'difference' => '0',
'difference_float' => 0,
'currency_id' => (string)$foreignCurrencyId,
'currency_code' => $journal['foreign_currency_code'],
];
$response[$foreignCurrencyId]['difference'] = bcadd(
$response[$foreignCurrencyId]['difference'],
app('steam')->positive($journal['foreign_amount'])
);
$response[$foreignCurrencyId]['difference_float'] = (float)$response[$foreignCurrencyId]['difference'];
}
} }
return response()->json(array_values($response)); return response()->json(array_values($response));

View File

@ -28,6 +28,7 @@ use FireflyIII\Api\V1\Controllers\Controller;
use FireflyIII\Api\V1\Requests\Insight\GenericRequest; use FireflyIII\Api\V1\Requests\Insight\GenericRequest;
use FireflyIII\Helpers\Collector\GroupCollectorInterface; use FireflyIII\Helpers\Collector\GroupCollectorInterface;
use FireflyIII\Models\TransactionType; use FireflyIII\Models\TransactionType;
use FireflyIII\Support\Facades\Amount;
use Illuminate\Http\JsonResponse; use Illuminate\Http\JsonResponse;
/** /**
@ -45,38 +46,38 @@ class PeriodController extends Controller
$start = $request->getStart(); $start = $request->getStart();
$end = $request->getEnd(); $end = $request->getEnd();
$response = []; $response = [];
$convertToNative = Amount::convertToNative();
$default = Amount::getDefaultCurrency();
// collect all expenses in this period (regardless of type) // collect all expenses in this period (regardless of type)
$collector = app(GroupCollectorInterface::class); $collector = app(GroupCollectorInterface::class);
$collector->setTypes([TransactionType::TRANSFER])->setRange($start, $end)->setDestinationAccounts($accounts); $collector->setTypes([TransactionType::TRANSFER])->setRange($start, $end)->setDestinationAccounts($accounts);
$genericSet = $collector->getExtractedJournals(); $genericSet = $collector->getExtractedJournals();
foreach ($genericSet as $journal) { foreach ($genericSet as $journal) {
$currencyId = (int)$journal['currency_id']; // currency
$foreignCurrencyId = (int)$journal['foreign_currency_id']; $currencyId = $journal['currency_id'];
$currencyCode = $journal['currency_code'];
$field = $convertToNative && $currencyId !== $default->id ? 'native_amount' : 'amount';
// perhaps use default currency instead?
if ($convertToNative && $journal['currency_id'] !== $default->id) {
$currencyId = $default->id;
$currencyCode = $default->code;
}
// use foreign amount when the foreign currency IS the default currency.
if ($convertToNative && $journal['currency_id'] !== $default->id && $default->id === $journal['foreign_currency_id']) {
$field = 'foreign_amount';
}
if (0 !== $currencyId) {
$response[$currencyId] ??= [ $response[$currencyId] ??= [
'difference' => '0', 'difference' => '0',
'difference_float' => 0, 'difference_float' => 0,
'currency_id' => (string) $currencyId, 'currency_id' => (string) $currencyId,
'currency_code' => $journal['currency_code'], 'currency_code' => $currencyCode,
]; ];
$response[$currencyId]['difference'] = bcadd($response[$currencyId]['difference'], app('steam')->positive($journal['amount'])); $response[$currencyId]['difference'] = bcadd($response[$currencyId]['difference'], app('steam')->positive($journal[$field]));
$response[$currencyId]['difference_float'] = (float) $response[$currencyId]['difference']; $response[$currencyId]['difference_float'] = (float) $response[$currencyId]['difference'];
}
if (0 !== $foreignCurrencyId) {
$response[$foreignCurrencyId] ??= [
'difference' => '0',
'difference_float' => 0,
'currency_id' => (string)$foreignCurrencyId,
'currency_code' => $journal['foreign_currency_code'],
];
$response[$foreignCurrencyId]['difference'] = bcadd(
$response[$foreignCurrencyId]['difference'],
app('steam')->positive($journal['foreign_amount'])
);
$response[$foreignCurrencyId]['difference_float'] = (float)$response[$foreignCurrencyId]['difference'];
}
} }
return response()->json(array_values($response)); return response()->json(array_values($response));

View File

@ -29,6 +29,7 @@ use FireflyIII\Api\V1\Requests\Insight\GenericRequest;
use FireflyIII\Helpers\Collector\GroupCollectorInterface; use FireflyIII\Helpers\Collector\GroupCollectorInterface;
use FireflyIII\Models\TransactionType; use FireflyIII\Models\TransactionType;
use FireflyIII\Repositories\Tag\TagRepositoryInterface; use FireflyIII\Repositories\Tag\TagRepositoryInterface;
use FireflyIII\Support\Facades\Amount;
use Illuminate\Http\JsonResponse; use Illuminate\Http\JsonResponse;
/** /**
@ -65,6 +66,9 @@ class TagController extends Controller
$start = $request->getStart(); $start = $request->getStart();
$end = $request->getEnd(); $end = $request->getEnd();
$response = []; $response = [];
$convertToNative = Amount::convertToNative();
$default = Amount::getDefaultCurrency();
// collect all expenses in this period (regardless of type) by the given bills and accounts. // collect all expenses in this period (regardless of type) by the given bills and accounts.
$collector = app(GroupCollectorInterface::class); $collector = app(GroupCollectorInterface::class);
@ -74,32 +78,30 @@ class TagController extends Controller
$genericSet = $collector->getExtractedJournals(); $genericSet = $collector->getExtractedJournals();
foreach ($genericSet as $journal) { foreach ($genericSet as $journal) {
$currencyId = (int)$journal['currency_id']; // currency
$foreignCurrencyId = (int)$journal['foreign_currency_id']; $currencyId = $journal['currency_id'];
$currencyCode = $journal['currency_code'];
$field = $convertToNative && $currencyId !== $default->id ? 'native_amount' : 'amount';
// perhaps use default currency instead?
if ($convertToNative && $journal['currency_id'] !== $default->id) {
$currencyId = $default->id;
$currencyCode = $default->code;
}
// use foreign amount when the foreign currency IS the default currency.
if ($convertToNative && $journal['currency_id'] !== $default->id && $default->id === $journal['foreign_currency_id']) {
$field = 'foreign_amount';
}
if (0 !== $currencyId) {
$response[$currencyId] ??= [ $response[$currencyId] ??= [
'difference' => '0', 'difference' => '0',
'difference_float' => 0, 'difference_float' => 0,
'currency_id' => (string) $currencyId, 'currency_id' => (string) $currencyId,
'currency_code' => $journal['currency_code'], 'currency_code' => $currencyCode,
]; ];
$response[$currencyId]['difference'] = bcadd($response[$currencyId]['difference'], app('steam')->positive($journal['amount'])); $response[$currencyId]['difference'] = bcadd($response[$currencyId]['difference'], app('steam')->positive($journal[$field]));
$response[$currencyId]['difference_float'] = (float) $response[$currencyId]['difference']; $response[$currencyId]['difference_float'] = (float) $response[$currencyId]['difference'];
}
if (0 !== $foreignCurrencyId) {
$response[$foreignCurrencyId] ??= [
'difference' => '0',
'difference_float' => 0,
'currency_id' => (string)$foreignCurrencyId,
'currency_code' => $journal['foreign_currency_code'],
];
$response[$foreignCurrencyId]['difference'] = bcadd(
$response[$foreignCurrencyId]['difference'],
app('steam')->positive($journal['foreign_amount'])
);
$response[$foreignCurrencyId]['difference_float'] = (float)$response[$foreignCurrencyId]['difference'];
}
} }
return response()->json(array_values($response)); return response()->json(array_values($response));

View File

@ -111,7 +111,7 @@ class ListController extends Controller
// types to get, page size: // types to get, page size:
$pageSize = $this->parameters->get('limit'); $pageSize = $this->parameters->get('limit');
// get list of budgets. Count it and split it. // get list of piggy banks. Count it and split it.
$collection = $this->repository->getPiggyBanks($account); $collection = $this->repository->getPiggyBanks($account);
$count = $collection->count(); $count = $collection->count();
$piggyBanks = $collection->slice(($this->parameters->get('page') - 1) * $pageSize, $pageSize); $piggyBanks = $collection->slice(($this->parameters->get('page') - 1) * $pageSize, $pageSize);

View File

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

View File

@ -28,6 +28,7 @@ use FireflyIII\Api\V1\Controllers\Controller;
use FireflyIII\Exceptions\FireflyException; use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Models\PiggyBank; use FireflyIII\Models\PiggyBank;
use FireflyIII\Repositories\PiggyBank\PiggyBankRepositoryInterface; use FireflyIII\Repositories\PiggyBank\PiggyBankRepositoryInterface;
use FireflyIII\Transformers\AccountTransformer;
use FireflyIII\Transformers\AttachmentTransformer; use FireflyIII\Transformers\AttachmentTransformer;
use FireflyIII\Transformers\PiggyBankEventTransformer; use FireflyIII\Transformers\PiggyBankEventTransformer;
use Illuminate\Http\JsonResponse; use Illuminate\Http\JsonResponse;
@ -58,6 +59,38 @@ class ListController extends Controller
); );
} }
/**
* This endpoint is documented at:
* https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/piggy_banks/listAccountByPiggyBank
*
* List single resource.
*
* @throws FireflyException
*/
public function accounts(PiggyBank $piggyBank): JsonResponse
{
// types to get, page size:
$pageSize = $this->parameters->get('limit');
$manager = $this->getManager();
$collection = $piggyBank->accounts;
$count = $collection->count();
$events = $collection->slice(($this->parameters->get('page') - 1) * $pageSize, $pageSize);
// make paginator:
$paginator = new LengthAwarePaginator($events, $count, $pageSize, $this->parameters->get('page'));
$paginator->setPath(route('api.v1.piggy-banks.accounts', [$piggyBank->id]).$this->buildParams());
/** @var AccountTransformer $transformer */
$transformer = app(AccountTransformer::class);
$transformer->setParameters($this->parameters);
$resource = new FractalCollection($events, $transformer, 'accounts');
$resource->setPaginator(new IlluminatePaginatorAdapter($paginator));
return response()->json($manager->createData($resource)->toArray())->header('Content-Type', self::CONTENT_TYPE);
}
/** /**
* This endpoint is documented at: * This endpoint is documented at:
* https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/piggy_banks/listAttachmentByPiggyBank * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/piggy_banks/listAttachmentByPiggyBank

View File

@ -72,7 +72,7 @@ class ShowController extends Controller
// types to get, page size: // types to get, page size:
$pageSize = $this->parameters->get('limit'); $pageSize = $this->parameters->get('limit');
// get list of budgets. Count it and split it. // get list of piggy banks. Count it and split it.
$collection = $this->repository->getPiggyBanks(); $collection = $this->repository->getPiggyBanks();
$count = $collection->count(); $count = $collection->count();
$piggyBanks = $collection->slice(($this->parameters->get('page') - 1) * $pageSize, $pageSize); $piggyBanks = $collection->slice(($this->parameters->get('page') - 1) * $pageSize, $pageSize);

View File

@ -107,8 +107,7 @@ class ShowController extends Controller
/** @var User $user */ /** @var User $user */
$user = auth()->user(); $user = auth()->user();
$manager = $this->getManager(); $manager = $this->getManager();
$defaultCurrency = app('amount')->getDefaultCurrencyByUserGroup($user->userGroup); $this->parameters->set('defaultCurrency', $this->defaultCurrency);
$this->parameters->set('defaultCurrency', $defaultCurrency);
// update fields with user info. // update fields with user info.
$currency->refreshForUser($user); $currency->refreshForUser($user);
@ -135,7 +134,7 @@ class ShowController extends Controller
/** @var User $user */ /** @var User $user */
$user = auth()->user(); $user = auth()->user();
$manager = $this->getManager(); $manager = $this->getManager();
$currency = app('amount')->getDefaultCurrencyByUserGroup($user->userGroup); $currency = $this->defaultCurrency;
// update fields with user info. // update fields with user info.
$currency->refreshForUser($user); $currency->refreshForUser($user);

View File

@ -62,7 +62,6 @@ class AccountController extends Controller
*/ */
public function search(Request $request): JsonResponse|Response public function search(Request $request): JsonResponse|Response
{ {
app('log')->debug('Now in account search()');
$manager = $this->getManager(); $manager = $this->getManager();
$query = trim((string) $request->get('query')); $query = trim((string) $request->get('query'));
$field = trim((string) $request->get('field')); $field = trim((string) $request->get('field'));
@ -70,6 +69,7 @@ class AccountController extends Controller
if ('' === $query || !in_array($field, $this->validFields, true)) { if ('' === $query || !in_array($field, $this->validFields, true)) {
return response(null, 422); return response(null, 422);
} }
app('log')->debug(sprintf('Now in account search("%s", "%s")', $field, $query));
$types = $this->mapAccountTypes($type); $types = $this->mapAccountTypes($type);
/** @var AccountSearch $search */ /** @var AccountSearch $search */

View File

@ -27,19 +27,21 @@ namespace FireflyIII\Api\V1\Controllers\Summary;
use Carbon\Carbon; use Carbon\Carbon;
use FireflyIII\Api\V1\Controllers\Controller; use FireflyIII\Api\V1\Controllers\Controller;
use FireflyIII\Api\V1\Requests\Data\DateRequest; use FireflyIII\Api\V1\Requests\Data\DateRequest;
use FireflyIII\Enums\AccountTypeEnum;
use FireflyIII\Enums\TransactionTypeEnum; use FireflyIII\Enums\TransactionTypeEnum;
use FireflyIII\Helpers\Collector\GroupCollectorInterface; use FireflyIII\Helpers\Collector\GroupCollectorInterface;
use FireflyIII\Helpers\Report\NetWorthInterface; use FireflyIII\Helpers\Report\NetWorthInterface;
use FireflyIII\Models\Account; use FireflyIII\Models\Account;
use FireflyIII\Models\AccountType;
use FireflyIII\Repositories\Account\AccountRepositoryInterface; use FireflyIII\Repositories\Account\AccountRepositoryInterface;
use FireflyIII\Repositories\Bill\BillRepositoryInterface; use FireflyIII\Repositories\Bill\BillRepositoryInterface;
use FireflyIII\Repositories\Budget\AvailableBudgetRepositoryInterface; use FireflyIII\Repositories\Budget\AvailableBudgetRepositoryInterface;
use FireflyIII\Repositories\Budget\BudgetRepositoryInterface; use FireflyIII\Repositories\Budget\BudgetRepositoryInterface;
use FireflyIII\Repositories\Budget\OperationsRepositoryInterface; use FireflyIII\Repositories\Budget\OperationsRepositoryInterface;
use FireflyIII\Repositories\UserGroups\Currency\CurrencyRepositoryInterface; use FireflyIII\Repositories\UserGroups\Currency\CurrencyRepositoryInterface;
use FireflyIII\Support\Facades\Amount;
use FireflyIII\User; use FireflyIII\User;
use Illuminate\Http\JsonResponse; use Illuminate\Http\JsonResponse;
use Illuminate\Support\Facades\Log;
/** /**
* Class BasicController * Class BasicController
@ -120,6 +122,9 @@ class BasicController extends Controller
private function getBalanceInformation(Carbon $start, Carbon $end): array private function getBalanceInformation(Carbon $start, Carbon $end): array
{ {
// some config settings
$convertToNative = Amount::convertToNative();
$default = Amount::getDefaultCurrency();
// prep some arrays: // prep some arrays:
$incomes = []; $incomes = [];
$expenses = []; $expenses = [];
@ -133,16 +138,17 @@ class BasicController extends Controller
$set = $collector->getExtractedJournals(); $set = $collector->getExtractedJournals();
/** @var array $transactionJournal */ /** @var array $journal */
foreach ($set as $transactionJournal) { foreach ($set as $journal) {
$currencyId = (int)$transactionJournal['currency_id']; $currencyId = $convertToNative ? $default->id : (int) $journal['currency_id'];
$amount = Amount::getAmountFromJournal($journal);
$incomes[$currencyId] ??= '0'; $incomes[$currencyId] ??= '0';
$incomes[$currencyId] = bcadd( $incomes[$currencyId] = bcadd(
$incomes[$currencyId], $incomes[$currencyId],
bcmul($transactionJournal['amount'], '-1') bcmul($amount, '-1')
); );
$sums[$currencyId] ??= '0'; $sums[$currencyId] ??= '0';
$sums[$currencyId] = bcadd($sums[$currencyId], bcmul($transactionJournal['amount'], '-1')); $sums[$currencyId] = bcadd($sums[$currencyId], bcmul($amount, '-1'));
} }
// collect expenses of user using the new group collector. // collect expenses of user using the new group collector.
@ -151,13 +157,14 @@ class BasicController extends Controller
$collector->setRange($start, $end)->setPage($this->parameters->get('page'))->setTypes([TransactionTypeEnum::WITHDRAWAL->value]); $collector->setRange($start, $end)->setPage($this->parameters->get('page'))->setTypes([TransactionTypeEnum::WITHDRAWAL->value]);
$set = $collector->getExtractedJournals(); $set = $collector->getExtractedJournals();
/** @var array $transactionJournal */ /** @var array $journal */
foreach ($set as $transactionJournal) { foreach ($set as $journal) {
$currencyId = (int)$transactionJournal['currency_id']; $currencyId = $convertToNative ? $default->id : (int) $journal['currency_id'];
$amount = Amount::getAmountFromJournal($journal);
$expenses[$currencyId] ??= '0'; $expenses[$currencyId] ??= '0';
$expenses[$currencyId] = bcadd($expenses[$currencyId], $transactionJournal['amount']); $expenses[$currencyId] = bcadd($expenses[$currencyId], $amount);
$sums[$currencyId] ??= '0'; $sums[$currencyId] ??= '0';
$sums[$currencyId] = bcadd($sums[$currencyId], $transactionJournal['amount']); $sums[$currencyId] = bcadd($sums[$currencyId], $amount);
} }
// format amounts: // format amounts:
@ -269,24 +276,27 @@ class BasicController extends Controller
*/ */
private function getLeftToSpendInfo(Carbon $start, Carbon $end): array private function getLeftToSpendInfo(Carbon $start, Carbon $end): array
{ {
Log::debug(sprintf('Now in getLeftToSpendInfo("%s", "%s")', $start->format('Y-m-d H:i:s'), $end->format('Y-m-d H:i:s')));
$return = []; $return = [];
$today = today(config('app.timezone')); $today = today(config('app.timezone'));
$available = $this->abRepository->getAvailableBudgetWithCurrency($start, $end); $available = $this->abRepository->getAvailableBudgetWithCurrency($start, $end);
$budgets = $this->budgetRepository->getActiveBudgets(); $budgets = $this->budgetRepository->getActiveBudgets();
$spent = $this->opsRepository->sumExpenses($start, $end, null, $budgets); $spent = $this->opsRepository->sumExpenses($start, $end, null, $budgets);
$days = (int) $today->diffInDays($end, true) + 1;
foreach ($spent as $row) { foreach ($spent as $row) {
// either an amount was budgeted or 0 is available. // either an amount was budgeted or 0 is available.
$amount = (string)($available[$row['currency_id']] ?? '0'); $currencyId = $row['currency_id'];
$amount = (string) ($available[$currencyId] ?? '0');
$spentInCurrency = $row['sum']; $spentInCurrency = $row['sum'];
$leftToSpend = bcadd($amount, $spentInCurrency); $leftToSpend = bcadd($amount, $spentInCurrency);
$days = (int)$today->diffInDays($end, true) + 1;
$perDay = '0'; $perDay = '0';
if (0 !== $days && bccomp($leftToSpend, '0') > -1) { if (0 !== $days && bccomp($leftToSpend, '0') > -1) {
$perDay = bcdiv($leftToSpend, (string) $days); $perDay = bcdiv($leftToSpend, (string) $days);
} }
Log::debug(sprintf('Spent %s %s', $row['currency_code'], $row['sum']));
$return[] = [ $return[] = [
'key' => sprintf('left-to-spend-in-%s', $row['currency_code']), 'key' => sprintf('left-to-spend-in-%s', $row['currency_code']),
'title' => trans('firefly.box_left_to_spend_in_currency', ['currency' => $row['currency_symbol']]), 'title' => trans('firefly.box_left_to_spend_in_currency', ['currency' => $row['currency_symbol']]),
@ -311,9 +321,11 @@ class BasicController extends Controller
private function getNetWorthInfo(Carbon $start, Carbon $end): array private function getNetWorthInfo(Carbon $start, Carbon $end): array
{ {
Log::debug('getNetWorthInfo');
/** @var User $user */ /** @var User $user */
$user = auth()->user(); $user = auth()->user();
$date = today(config('app.timezone'))->startOfDay(); $date = now(config('app.timezone'));
// start and end in the future? use $end // start and end in the future? use $end
if ($this->notInDateRange($date, $start, $end)) { if ($this->notInDateRange($date, $start, $end)) {
/** @var Carbon $date */ /** @var Carbon $date */
@ -323,9 +335,7 @@ class BasicController extends Controller
/** @var NetWorthInterface $netWorthHelper */ /** @var NetWorthInterface $netWorthHelper */
$netWorthHelper = app(NetWorthInterface::class); $netWorthHelper = app(NetWorthInterface::class);
$netWorthHelper->setUser($user); $netWorthHelper->setUser($user);
$allAccounts = $this->accountRepository->getActiveAccountsByType( $allAccounts = $this->accountRepository->getActiveAccountsByType([AccountTypeEnum::ASSET->value, AccountTypeEnum::DEFAULT->value, AccountTypeEnum::LOAN->value, AccountTypeEnum::MORTGAGE->value, AccountTypeEnum::DEBT->value]);
[AccountType::ASSET, AccountType::DEFAULT, AccountType::LOAN, AccountType::MORTGAGE, AccountType::DEBT]
);
// filter list on preference of being included. // filter list on preference of being included.
$filtered = $allAccounts->filter( $filtered = $allAccounts->filter(
@ -360,6 +370,7 @@ class BasicController extends Controller
'sub_title' => '', 'sub_title' => '',
]; ];
} }
Log::debug('End of getNetWorthInfo');
return $return; return $return;
} }

View File

@ -48,6 +48,7 @@ class StoreRequest extends FormRequest
'amount' => $this->convertString('amount'), 'amount' => $this->convertString('amount'),
'currency_id' => $this->convertInteger('currency_id'), 'currency_id' => $this->convertInteger('currency_id'),
'currency_code' => $this->convertString('currency_code'), 'currency_code' => $this->convertString('currency_code'),
'notes' => $this->stringWithNewlines('notes'),
]; ];
} }
@ -62,6 +63,7 @@ class StoreRequest extends FormRequest
'amount' => ['required', new IsValidPositiveAmount()], 'amount' => ['required', new IsValidPositiveAmount()],
'currency_id' => 'numeric|exists:transaction_currencies,id', 'currency_id' => 'numeric|exists:transaction_currencies,id',
'currency_code' => 'min:3|max:51|exists:transaction_currencies,code', 'currency_code' => 'min:3|max:51|exists:transaction_currencies,code',
'notes' => 'nullable|min:0|max:32768',
]; ];
} }
} }

View File

@ -51,7 +51,12 @@ class UpdateRequest extends FormRequest
'amount' => ['amount', 'convertString'], 'amount' => ['amount', 'convertString'],
'currency_id' => ['currency_id', 'convertInteger'], 'currency_id' => ['currency_id', 'convertInteger'],
'currency_code' => ['currency_code', 'convertString'], 'currency_code' => ['currency_code', 'convertString'],
'notes' => ['notes', 'stringWithNewlines'],
]; ];
if (false === $this->has('notes')) {
// ignore notes, not submitted.
unset($fields['notes']);
}
return $this->getAllData($fields); return $this->getAllData($fields);
} }
@ -67,6 +72,7 @@ class UpdateRequest extends FormRequest
'amount' => ['nullable', new IsValidPositiveAmount()], 'amount' => ['nullable', new IsValidPositiveAmount()],
'currency_id' => 'numeric|exists:transaction_currencies,id', 'currency_id' => 'numeric|exists:transaction_currencies,id',
'currency_code' => 'min:3|max:51|exists:transaction_currencies,code', 'currency_code' => 'min:3|max:51|exists:transaction_currencies,code',
'notes' => 'nullable|min:0|max:32768',
]; ];
} }

View File

@ -24,10 +24,15 @@ declare(strict_types=1);
namespace FireflyIII\Api\V1\Requests\Models\PiggyBank; namespace FireflyIII\Api\V1\Requests\Models\PiggyBank;
use FireflyIII\Rules\IsValidPositiveAmount; use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Models\TransactionCurrency;
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
use FireflyIII\Rules\IsValidZeroOrMoreAmount;
use FireflyIII\Support\Request\ChecksLogin; use FireflyIII\Support\Request\ChecksLogin;
use FireflyIII\Support\Request\ConvertsDataTypes; use FireflyIII\Support\Request\ConvertsDataTypes;
use Illuminate\Foundation\Http\FormRequest; use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Support\Facades\Log;
use Illuminate\Validation\Validator;
/** /**
* Class StoreRequest * Class StoreRequest
@ -47,18 +52,38 @@ class StoreRequest extends FormRequest
]; ];
$data = $this->getAllData($fields); $data = $this->getAllData($fields);
$data['name'] = $this->convertString('name'); $data['name'] = $this->convertString('name');
$data['account_id'] = $this->convertInteger('account_id'); $data['accounts'] = $this->parseAccounts($this->get('accounts'));
$data['targetamount'] = $this->convertString('target_amount'); $data['target_amount'] = $this->convertString('target_amount');
$data['current_amount'] = $this->convertString('current_amount'); $data['start_date'] = $this->getCarbonDate('start_date');
$data['startdate'] = $this->getCarbonDate('start_date'); $data['target_date'] = $this->getCarbonDate('target_date');
$data['targetdate'] = $this->getCarbonDate('target_date');
$data['notes'] = $this->stringWithNewlines('notes'); $data['notes'] = $this->stringWithNewlines('notes');
$data['object_group_id'] = $this->convertInteger('object_group_id'); $data['object_group_id'] = $this->convertInteger('object_group_id');
$data['transaction_currency_id'] = $this->convertInteger('transaction_currency_id');
$data['transaction_currency_code'] = $this->convertString('transaction_currency_code');
$data['object_group_title'] = $this->convertString('object_group_title'); $data['object_group_title'] = $this->convertString('object_group_title');
return $data; return $data;
} }
private function parseAccounts(mixed $array): array
{
if (!is_array($array)) {
return [];
}
$return = [];
foreach ($array as $entry) {
if (!is_array($entry)) {
continue;
}
$return[] = [
'account_id' => $this->integerFromValue((string) ($entry['account_id'] ?? '0')),
'current_amount' => $this->clearString((string) ($entry['current_amount'] ?? '0')),
];
}
return $return;
}
/** /**
* The rules that the incoming request must be matched against. * The rules that the incoming request must be matched against.
*/ */
@ -66,14 +91,79 @@ class StoreRequest extends FormRequest
{ {
return [ return [
'name' => 'required|min:1|max:255|uniquePiggyBankForUser', 'name' => 'required|min:1|max:255|uniquePiggyBankForUser',
'current_amount' => ['nullable', new IsValidPositiveAmount()], 'accounts' => 'required',
'account_id' => 'required|numeric|belongsToUser:accounts,id', 'accounts.*' => 'array|required',
'accounts.*.account_id' => 'required|numeric|belongsToUser:accounts,id',
'accounts.*.current_amount' => ['numeric', new IsValidZeroOrMoreAmount()],
'object_group_id' => 'numeric|belongsToUser:object_groups,id', 'object_group_id' => 'numeric|belongsToUser:object_groups,id',
'object_group_title' => ['min:1', 'max:255'], 'object_group_title' => ['min:1', 'max:255'],
'target_amount' => ['required', new IsValidPositiveAmount()], 'target_amount' => ['required', new IsValidZeroOrMoreAmount()],
'start_date' => 'date|nullable', 'start_date' => 'date|nullable',
'transaction_currency_id' => 'exists:transaction_currencies,id|required_without:transaction_currency_code',
'transaction_currency_code' => 'exists:transaction_currencies,code|required_without:transaction_currency_id',
'target_date' => 'date|nullable|after:start_date', 'target_date' => 'date|nullable|after:start_date',
'notes' => 'max:65000', 'notes' => 'max:65000',
]; ];
} }
/**
* Can only store money on liabilities and asset accounts.
*/
public function withValidator(Validator $validator): void
{
$validator->after(
function (Validator $validator): void {
// validate start before end only if both are there.
$data = $validator->getData();
$currency = $this->getCurrencyFromData($data);
$targetAmount = (string) ($data['target_amount'] ?? '0');
$currentAmount = '0';
if (array_key_exists('accounts', $data) && is_array($data['accounts'])) {
$repository = app(AccountRepositoryInterface::class);
$types = config('firefly.piggy_bank_account_types');
foreach ($data['accounts'] as $index => $array) {
$accountId = (int) ($array['account_id'] ?? 0);
$account = $repository->find($accountId);
if (null !== $account) {
// check currency here.
$accountCurrency = $repository->getAccountCurrency($account);
$isMultiCurrency = $repository->getMetaValue($account, 'is_multi_currency');
$currentAmount = bcadd($currentAmount, (string) ($array['current_amount'] ?? '0'));
if ($accountCurrency->id !== $currency->id && 'true' !== $isMultiCurrency) {
$validator->errors()->add(sprintf('accounts.%d', $index), trans('validation.invalid_account_currency'));
}
$type = $account->accountType->type;
if (!in_array($type, $types, true)) {
$validator->errors()->add(sprintf('accounts.%d', $index), trans('validation.invalid_account_type'));
}
}
}
}
if (-1 === bccomp($targetAmount, $currentAmount) && 1 === bccomp($targetAmount, '0')) {
$validator->errors()->add('target_amount', trans('validation.current_amount_too_much'));
}
}
);
if ($validator->fails()) {
Log::channel('audit')->error(sprintf('Validation errors in %s', __CLASS__), $validator->errors()->toArray());
}
}
private function getCurrencyFromData(array $data): TransactionCurrency
{
if (array_key_exists('transaction_currency_code', $data) && '' !== (string) $data['transaction_currency_code']) {
$currency = TransactionCurrency::whereCode($data['transaction_currency_code'])->first();
if (null !== $currency) {
return $currency;
}
}
if (array_key_exists('transaction_currency_id', $data) && '' !== (string) $data['transaction_currency_id']) {
$currency = TransactionCurrency::find((int) $data['transaction_currency_id']);
if (null !== $currency) {
return $currency;
}
}
throw new FireflyException('Unexpected empty currency.');
}
} }

View File

@ -119,7 +119,7 @@ class StoreRequest extends FormRequest
'description' => 'min:1|max:32768|nullable', 'description' => 'min:1|max:32768|nullable',
'rule_group_id' => 'belongsToUser:rule_groups|required_without:rule_group_title', 'rule_group_id' => 'belongsToUser:rule_groups|required_without:rule_group_title',
'rule_group_title' => 'nullable|min:1|max:255|required_without:rule_group_id|belongsToUser:rule_groups,title', 'rule_group_title' => 'nullable|min:1|max:255|required_without:rule_group_id|belongsToUser:rule_groups,title',
'trigger' => 'required|in:store-journal,update-journal', 'trigger' => 'required|in:store-journal,update-journal,manual-activation',
'triggers.*.type' => 'required|in:'.implode(',', $validTriggers), 'triggers.*.type' => 'required|in:'.implode(',', $validTriggers),
'triggers.*.value' => 'required_if:actions.*.type,'.$contextTriggers.'|min:1|ruleTriggerValue|max:1024', 'triggers.*.value' => 'required_if:actions.*.type,'.$contextTriggers.'|min:1|ruleTriggerValue|max:1024',
'triggers.*.stop_processing' => [new IsBoolean()], 'triggers.*.stop_processing' => [new IsBoolean()],

View File

@ -138,7 +138,7 @@ class UpdateRequest extends FormRequest
'description' => 'min:1|max:32768|nullable', 'description' => 'min:1|max:32768|nullable',
'rule_group_id' => 'belongsToUser:rule_groups', 'rule_group_id' => 'belongsToUser:rule_groups',
'rule_group_title' => 'nullable|min:1|max:255|belongsToUser:rule_groups,title', 'rule_group_title' => 'nullable|min:1|max:255|belongsToUser:rule_groups,title',
'trigger' => 'in:store-journal,update-journal', 'trigger' => 'in:store-journal,update-journal.manual-activation',
'triggers.*.type' => 'required|in:'.implode(',', $validTriggers), 'triggers.*.type' => 'required|in:'.implode(',', $validTriggers),
'triggers.*.value' => 'required_if:actions.*.type,'.$contextTriggers.'|min:1|ruleTriggerValue|max:1024', 'triggers.*.value' => 'required_if:actions.*.type,'.$contextTriggers.'|min:1|ruleTriggerValue|max:1024',
'triggers.*.stop_processing' => [new IsBoolean()], 'triggers.*.stop_processing' => [new IsBoolean()],

View File

@ -40,9 +40,9 @@ use Illuminate\Support\Facades\Log;
*/ */
class AccountController extends Controller class AccountController extends Controller
{ {
private AccountRepositoryInterface $repository;
private TransactionCurrency $default;
private ExchangeRateConverter $converter; private ExchangeRateConverter $converter;
private TransactionCurrency $default;
private AccountRepositoryInterface $repository;
/** /**
* AccountController constructor. * AccountController constructor.

View File

@ -45,9 +45,9 @@ class AccountController extends Controller
use CollectsAccountsFromFilter; use CollectsAccountsFromFilter;
use ValidatesUserGroupTrait; use ValidatesUserGroupTrait;
private AccountRepositoryInterface $repository;
private ChartData $chartData; private ChartData $chartData;
private TransactionCurrency $default; private TransactionCurrency $default;
private AccountRepositoryInterface $repository;
public function __construct() public function __construct()
{ {
@ -118,22 +118,21 @@ class AccountController extends Controller
'native_entries' => [], 'native_entries' => [],
]; ];
$currentStart = clone $params['start']; $currentStart = clone $params['start'];
$range = app('steam')->balanceInRange($account, $params['start'], clone $params['end'], $currency); $range = app('steam')->finalAccountBalanceInRange($account, $params['start'], clone $params['end'], $this->convertToNative);
$rangeConverted = app('steam')->balanceInRangeConverted($account, $params['start'], clone $params['end'], $this->default);
$previous = array_values($range)[0]; $previous = array_values($range)[0]['balance'];
$previousConverted = array_values($rangeConverted)[0]; $previousNative = array_values($range)[0]['native_balance'];
while ($currentStart <= $params['end']) { while ($currentStart <= $params['end']) {
$format = $currentStart->format('Y-m-d'); $format = $currentStart->format('Y-m-d');
$label = $currentStart->toAtomString(); $label = $currentStart->toAtomString();
$balance = array_key_exists($format, $range) ? $range[$format] : $previous; $balance = array_key_exists($format, $range) ? $range[$format]['balance'] : $previous;
$balanceConverted = array_key_exists($format, $rangeConverted) ? $rangeConverted[$format] : $previousConverted; $balanceNative = array_key_exists($format, $range) ? $range[$format]['balance_native'] : $previousNative;
$previous = $balance; $previous = $balance;
$previousConverted = $balanceConverted; $previousNative = $balanceNative;
$currentStart->addDay(); $currentStart->addDay();
$currentSet['entries'][$label] = $balance; $currentSet['entries'][$label] = $balance;
$currentSet['native_entries'][$label] = $balanceConverted; $currentSet['native_entries'][$label] = $balanceNative;
} }
$this->chartData->add($currentSet); $this->chartData->add($currentSet);
} }

View File

@ -45,9 +45,10 @@ class BalanceController extends Controller
use CleansChartData; use CleansChartData;
use CollectsAccountsFromFilter; use CollectsAccountsFromFilter;
private AccountRepositoryInterface $repository;
private GroupCollectorInterface $collector;
private ChartData $chartData; private ChartData $chartData;
private GroupCollectorInterface $collector;
private AccountRepositoryInterface $repository;
// private TransactionCurrency $default; // private TransactionCurrency $default;
public function __construct() public function __construct()

View File

@ -55,8 +55,9 @@ class Controller extends BaseController
use ValidatesUserGroupTrait; use ValidatesUserGroupTrait;
protected const string CONTENT_TYPE = 'application/vnd.api+json'; protected const string CONTENT_TYPE = 'application/vnd.api+json';
protected ParameterBag $parameters;
protected array $acceptedRoles = [UserRoleEnum::READ_ONLY]; protected array $acceptedRoles = [UserRoleEnum::READ_ONLY];
protected ParameterBag $parameters;
protected bool $convertToNative = false;
public function __construct() public function __construct()
{ {
@ -92,8 +93,8 @@ class Controller extends BaseController
if ($page < 1) { if ($page < 1) {
$page = 1; $page = 1;
} }
if ($page > (2 ^ 16)) { if ($page > 2 ** 16) {
$page = (2 ^ 16); $page = 2 ** 16;
} }
$bag->set('page', $page); $bag->set('page', $page);

View File

@ -1,112 +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

@ -36,9 +36,8 @@ use Illuminate\Support\Facades\Log;
class IndexController extends Controller class IndexController extends Controller
{ {
public const string RESOURCE_KEY = 'accounts'; public const string RESOURCE_KEY = 'accounts';
private AccountRepositoryInterface $repository;
protected array $acceptedRoles = [UserRoleEnum::READ_ONLY, UserRoleEnum::MANAGE_TRANSACTIONS]; protected array $acceptedRoles = [UserRoleEnum::READ_ONLY, UserRoleEnum::MANAGE_TRANSACTIONS];
private AccountRepositoryInterface $repository;
/** /**
* AccountController constructor. * AccountController constructor.

View File

@ -39,9 +39,8 @@ use Illuminate\Http\JsonResponse;
class ShowController extends Controller class ShowController extends Controller
{ {
public const string RESOURCE_KEY = 'accounts'; public const string RESOURCE_KEY = 'accounts';
private AccountRepositoryInterface $repository;
protected array $acceptedRoles = [UserRoleEnum::READ_ONLY, UserRoleEnum::MANAGE_TRANSACTIONS]; protected array $acceptedRoles = [UserRoleEnum::READ_ONLY, UserRoleEnum::MANAGE_TRANSACTIONS];
private AccountRepositoryInterface $repository;
/** /**
* AccountController constructor. * AccountController constructor.

View File

@ -0,0 +1,67 @@
<?php
/*
* DestroyController.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\Model\ExchangeRate;
use FireflyIII\Api\V2\Controllers\Controller;
use FireflyIII\Api\V2\Request\Model\ExchangeRate\DestroyRequest;
use FireflyIII\Models\TransactionCurrency;
use FireflyIII\Repositories\UserGroups\ExchangeRate\ExchangeRateRepositoryInterface;
use FireflyIII\Support\Http\Api\ValidatesUserGroupTrait;
use Illuminate\Http\JsonResponse;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
class DestroyController extends Controller
{
use ValidatesUserGroupTrait;
public const string RESOURCE_KEY = 'exchange-rates';
private ExchangeRateRepositoryInterface $repository;
public function __construct()
{
parent::__construct();
$this->middleware(
function ($request, $next) {
$this->repository = app(ExchangeRateRepositoryInterface::class);
$this->repository->setUserGroup($this->validateUserGroup($request));
return $next($request);
}
);
}
public function destroy(DestroyRequest $request, TransactionCurrency $from, TransactionCurrency $to): JsonResponse
{
$date = $request->getDate();
$rate = $this->repository->getSpecificRateOnDate($from, $to, $date);
if (null === $rate) {
throw new NotFoundHttpException();
}
$this->repository->deleteRate($rate);
return response()->json([], 204);
}
}

View File

@ -0,0 +1,75 @@
<?php
/*
* ShowController.php
* Copyright (c) 2023 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\Model\ExchangeRate;
use FireflyIII\Api\V2\Controllers\Controller;
use FireflyIII\Repositories\UserGroups\ExchangeRate\ExchangeRateRepositoryInterface;
use FireflyIII\Support\Http\Api\ValidatesUserGroupTrait;
use Illuminate\Http\JsonResponse;
use Illuminate\Pagination\LengthAwarePaginator;
/**
* Class ShowController
*/
class IndexController extends Controller
{
use ValidatesUserGroupTrait;
public const string RESOURCE_KEY = 'exchange-rates';
private ExchangeRateRepositoryInterface $repository;
public function __construct()
{
parent::__construct();
$this->middleware(
function ($request, $next) {
$this->repository = app(ExchangeRateRepositoryInterface::class);
$this->repository->setUserGroup($this->validateUserGroup($request));
return $next($request);
}
);
}
public function index(): JsonResponse
{
$piggies = $this->repository->getAll();
$pageSize = $this->parameters->get('limit');
$count = $piggies->count();
$piggies = $piggies->slice(($this->parameters->get('page') - 1) * $pageSize, $pageSize);
$paginator = new LengthAwarePaginator($piggies, $count, $pageSize, $this->parameters->get('page'));
var_dump('here we are');
$transformer = new ExchangeRateTransformer();
$transformer->setParameters($this->parameters); // give params to transformer
return response()
->json($this->jsonApiList(self::RESOURCE_KEY, $paginator, $transformer))
->header('Content-Type', self::CONTENT_TYPE)
;
}
}

View File

@ -0,0 +1,76 @@
<?php
/*
* ShowController.php
* Copyright (c) 2023 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\Model\ExchangeRate;
use FireflyIII\Api\V2\Controllers\Controller;
use FireflyIII\Models\TransactionCurrency;
use FireflyIII\Repositories\UserGroups\ExchangeRate\ExchangeRateRepositoryInterface;
use FireflyIII\Support\Http\Api\ValidatesUserGroupTrait;
use FireflyIII\Transformers\V2\ExchangeRateTransformer;
use Illuminate\Http\JsonResponse;
use Illuminate\Pagination\LengthAwarePaginator;
/**
* Class ShowController
*/
class ShowController extends Controller
{
use ValidatesUserGroupTrait;
public const string RESOURCE_KEY = 'exchange-rates';
private ExchangeRateRepositoryInterface $repository;
public function __construct()
{
parent::__construct();
$this->middleware(
function ($request, $next) {
$this->repository = app(ExchangeRateRepositoryInterface::class);
$this->repository->setUserGroup($this->validateUserGroup($request));
return $next($request);
}
);
}
public function show(TransactionCurrency $from, TransactionCurrency $to): JsonResponse
{
$pageSize = $this->parameters->get('limit');
$page = $this->parameters->get('page');
$rates = $this->repository->getRates($from, $to);
$count = $rates->count();
$rates = $rates->slice(($page - 1) * $pageSize, $pageSize);
$paginator = new LengthAwarePaginator($rates, $count, $pageSize, $page);
$transformer = new ExchangeRateTransformer();
$transformer->setParameters($this->parameters); // give params to transformer
return response()
->json($this->jsonApiList(self::RESOURCE_KEY, $paginator, $transformer))
->header('Content-Type', self::CONTENT_TYPE)
;
}
}

View File

@ -0,0 +1,81 @@
<?php
/*
* DestroyController.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\Model\ExchangeRate;
use FireflyIII\Api\V2\Controllers\Controller;
use FireflyIII\Api\V2\Request\Model\ExchangeRate\StoreRequest;
use FireflyIII\Repositories\UserGroups\ExchangeRate\ExchangeRateRepositoryInterface;
use FireflyIII\Support\Http\Api\ValidatesUserGroupTrait;
use FireflyIII\Transformers\V2\ExchangeRateTransformer;
use Illuminate\Http\JsonResponse;
class StoreController extends Controller
{
use ValidatesUserGroupTrait;
public const string RESOURCE_KEY = 'exchange-rates';
private ExchangeRateRepositoryInterface $repository;
public function __construct()
{
parent::__construct();
$this->middleware(
function ($request, $next) {
$this->repository = app(ExchangeRateRepositoryInterface::class);
$this->repository->setUserGroup($this->validateUserGroup($request));
return $next($request);
}
);
}
public function store(StoreRequest $request): JsonResponse
{
$date = $request->getDate();
$rate = $request->getRate();
$from = $request->getFromCurrency();
$to = $request->getToCurrency();
// already has rate?
$object = $this->repository->getSpecificRateOnDate($from, $to, $date);
if (null !== $object) {
// just update it, no matter.
$rate = $this->repository->updateExchangeRate($object, $rate, $date);
}
if (null === $object) {
// store new
$rate = $this->repository->storeExchangeRate($from, $to, $rate, $date);
}
$transformer = new ExchangeRateTransformer();
$transformer->setParameters($this->parameters);
return response()
->api($this->jsonApiObject(self::RESOURCE_KEY, $rate, $transformer))
->header('Content-Type', self::CONTENT_TYPE)
;
}
}

View File

@ -0,0 +1,69 @@
<?php
/*
* DestroyController.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\Model\ExchangeRate;
use FireflyIII\Api\V2\Controllers\Controller;
use FireflyIII\Api\V2\Request\Model\ExchangeRate\UpdateRequest;
use FireflyIII\Models\CurrencyExchangeRate;
use FireflyIII\Repositories\UserGroups\ExchangeRate\ExchangeRateRepositoryInterface;
use FireflyIII\Support\Http\Api\ValidatesUserGroupTrait;
use FireflyIII\Transformers\V2\ExchangeRateTransformer;
use Illuminate\Http\JsonResponse;
class UpdateController extends Controller
{
use ValidatesUserGroupTrait;
public const string RESOURCE_KEY = 'exchange-rates';
private ExchangeRateRepositoryInterface $repository;
public function __construct()
{
parent::__construct();
$this->middleware(
function ($request, $next) {
$this->repository = app(ExchangeRateRepositoryInterface::class);
$this->repository->setUserGroup($this->validateUserGroup($request));
return $next($request);
}
);
}
public function update(UpdateRequest $request, CurrencyExchangeRate $exchangeRate): JsonResponse
{
$date = $request->getDate();
$rate = $request->getRate();
$exchangeRate = $this->repository->updateExchangeRate($exchangeRate, $rate, $date);
$transformer = new ExchangeRateTransformer();
$transformer->setParameters($this->parameters);
return response()
->api($this->jsonApiObject(self::RESOURCE_KEY, $exchangeRate, $transformer))
->header('Content-Type', self::CONTENT_TYPE)
;
}
}

View File

@ -0,0 +1,83 @@
<?php
/*
* IndexController.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\Model\TransactionCurrency;
use FireflyIII\Api\V2\Controllers\Controller;
use FireflyIII\Api\V2\Request\Model\TransactionCurrency\IndexRequest;
use FireflyIII\Enums\UserRoleEnum;
use FireflyIII\Repositories\UserGroups\Currency\CurrencyRepositoryInterface;
use FireflyIII\Transformers\V2\CurrencyTransformer;
use Illuminate\Http\JsonResponse;
use Illuminate\Pagination\LengthAwarePaginator;
class IndexController extends Controller
{
public const string RESOURCE_KEY = 'transaction-currencies';
protected array $acceptedRoles = [UserRoleEnum::READ_ONLY];
private CurrencyRepositoryInterface $repository;
public function __construct()
{
parent::__construct();
$this->middleware(
function ($request, $next) {
$this->repository = app(CurrencyRepositoryInterface::class);
// new way of user group validation
$userGroup = $this->validateUserGroup($request);
$this->repository->setUserGroup($userGroup);
return $next($request);
}
);
}
public function index(IndexRequest $request): JsonResponse
{
$settings = $request->getAll();
if (true === $settings['enabled']) {
$currencies = $this->repository->get();
}
if (true !== $settings['enabled']) {
$currencies = $this->repository->getAll();
}
$pageSize = $this->parameters->get('limit');
$count = $currencies->count();
// depending on the sort parameters, this list must not be split, because the
// order is calculated in the account transformer and by that time it's too late.
$accounts = $currencies->slice(($this->parameters->get('page') - 1) * $pageSize, $pageSize);
$paginator = new LengthAwarePaginator($accounts, $count, $pageSize, $this->parameters->get('page'));
$transformer = new CurrencyTransformer();
$this->parameters->set('pageSize', $pageSize);
$transformer->setParameters($this->parameters); // give params to transformer
return response()
->json($this->jsonApiList(self::RESOURCE_KEY, $paginator, $transformer))
->header('Content-Type', self::CONTENT_TYPE)
;
}
}

View File

@ -0,0 +1,80 @@
<?php
/*
* ShowController.php
* Copyright (c) 2021 james@firefly-iii.org
*
* This file is part of Firefly III (https://github.com/firefly-iii).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace FireflyIII\Api\V2\Controllers\Model\TransactionCurrency;
use FireflyIII\Api\V2\Controllers\Controller;
use FireflyIII\Models\TransactionCurrency;
use FireflyIII\Models\UserGroup;
use FireflyIII\Repositories\UserGroups\Currency\CurrencyRepositoryInterface;
use FireflyIII\Transformers\V2\CurrencyTransformer;
use Illuminate\Http\JsonResponse;
/**
* Class ShowController
*/
class ShowController extends Controller
{
public const string RESOURCE_KEY = 'transaction-currencies';
private CurrencyRepositoryInterface $repository;
public function __construct()
{
parent::__construct();
$this->middleware(
function ($request, $next) {
$this->repository = app(CurrencyRepositoryInterface::class);
// new way of user group validation
$userGroup = $this->validateUserGroup($request);
$this->repository->setUserGroup($userGroup);
return $next($request);
}
);
}
public function show(TransactionCurrency $currency): JsonResponse
{
$groups = $currency->userGroups()->where('user_groups.id', $this->repository->getUserGroup()->id)->get();
$enabled = $groups->count() > 0;
$default = false;
/** @var UserGroup $group */
foreach ($groups as $group) {
$default = 1 === $group->pivot->group_default;
}
$currency->userGroupEnabled = $enabled;
$currency->userGroupDefault = $default;
$transformer = new CurrencyTransformer();
$transformer->setParameters($this->parameters);
return response()
->api($this->jsonApiObject(self::RESOURCE_KEY, $currency, $transformer))
->header('Content-Type', self::CONTENT_TYPE)
;
}
}

View File

@ -73,6 +73,16 @@ class AutocompleteRequest extends FormRequest
return $array; return $array;
} }
private function getAccountTypeParameter(array $types): array
{
$return = [];
foreach ($types as $type) {
$return = array_merge($return, $this->mapAccountTypes($type));
}
return array_unique($return);
}
public function rules(): array public function rules(): array
{ {
$valid = array_keys($this->types); $valid = array_keys($this->types);
@ -86,14 +96,4 @@ class AutocompleteRequest extends FormRequest
'transaction_types' => 'nullable|in:todo', 'transaction_types' => 'nullable|in:todo',
]; ];
} }
private function getAccountTypeParameter(array $types): array
{
$return = [];
foreach ($types as $type) {
$return = array_merge($return, $this->mapAccountTypes($type));
}
return array_unique($return);
}
} }

View File

@ -40,6 +40,7 @@ class BalanceChartRequest extends FormRequest
use ChecksLogin; use ChecksLogin;
use ConvertsDataTypes; use ConvertsDataTypes;
use ValidatesUserGroupTrait; use ValidatesUserGroupTrait;
protected array $acceptedRoles = [UserRoleEnum::READ_ONLY]; protected array $acceptedRoles = [UserRoleEnum::READ_ONLY];
/** /**

View File

@ -1,7 +1,7 @@
<?php <?php
/* /*
* AccountBalanceRepository.php * DestroyRequest.php
* Copyright (c) 2024 james@firefly-iii.org. * Copyright (c) 2024 james@firefly-iii.org.
* *
* This file is part of Firefly III (https://github.com/firefly-iii). * This file is part of Firefly III (https://github.com/firefly-iii).
@ -22,25 +22,30 @@
declare(strict_types=1); declare(strict_types=1);
namespace FireflyIII\JsonApi\V2\AccountBalances; namespace FireflyIII\Api\V2\Request\Model\ExchangeRate;
use FireflyIII\Entities\AccountBalance; use Carbon\Carbon;
use LaravelJsonApi\Contracts\Store\QueriesAll; use FireflyIII\Support\Request\ChecksLogin;
use LaravelJsonApi\NonEloquent\AbstractRepository; use FireflyIII\Support\Request\ConvertsDataTypes;
use Illuminate\Foundation\Http\FormRequest;
class AccountBalanceRepository extends AbstractRepository implements QueriesAll class DestroyRequest extends FormRequest
{ {
#[\Override] use ChecksLogin;
public function find(string $resourceId): ?object use ConvertsDataTypes;
public function getDate(): Carbon
{ {
return AccountBalance::fromArray(); return $this->getCarbonDate('date');
} }
public function queryAll(): Capabilities\AccountBalanceQuery /**
* The rules that the incoming request must be matched against.
*/
public function rules(): array
{ {
return Capabilities\AccountBalanceQuery::make() return [
->withServer($this->server) 'date' => 'required|date|after:1900-01-01|before:2099-12-31',
->withSchema($this->schema) ];
;
} }
} }

View File

@ -0,0 +1,70 @@
<?php
/*
* DestroyRequest.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\Request\Model\ExchangeRate;
use Carbon\Carbon;
use FireflyIII\Models\TransactionCurrency;
use FireflyIII\Support\Request\ChecksLogin;
use FireflyIII\Support\Request\ConvertsDataTypes;
use Illuminate\Foundation\Http\FormRequest;
class StoreRequest extends FormRequest
{
use ChecksLogin;
use ConvertsDataTypes;
public function getDate(): ?Carbon
{
return $this->getCarbonDate('date');
}
public function getRate(): string
{
return (string) $this->get('rate');
}
public function getFromCurrency(): TransactionCurrency
{
return TransactionCurrency::where('code', $this->get('from'))->first();
}
public function getToCurrency(): TransactionCurrency
{
return TransactionCurrency::where('code', $this->get('to'))->first();
}
/**
* The rules that the incoming request must be matched against.
*/
public function rules(): array
{
return [
'date' => 'required|date|after:1900-01-01|before:2099-12-31',
'rate' => 'required|numeric|gt:0',
'from' => 'required|exists:transaction_currencies,code',
'to' => 'required|exists:transaction_currencies,code',
];
}
}

View File

@ -1,7 +1,7 @@
<?php <?php
/* /*
* AccountBalanceQuery.php * DestroyRequest.php
* Copyright (c) 2024 james@firefly-iii.org. * Copyright (c) 2024 james@firefly-iii.org.
* *
* This file is part of Firefly III (https://github.com/firefly-iii). * This file is part of Firefly III (https://github.com/firefly-iii).
@ -22,38 +22,36 @@
declare(strict_types=1); declare(strict_types=1);
namespace FireflyIII\JsonApi\V2\AccountBalances\Capabilities; namespace FireflyIII\Api\V2\Request\Model\ExchangeRate;
use FireflyIII\Entities\AccountBalance; use Carbon\Carbon;
use FireflyIII\Models\Account; use FireflyIII\Support\Request\ChecksLogin;
use LaravelJsonApi\NonEloquent\Capabilities\QueryAll; use FireflyIII\Support\Request\ConvertsDataTypes;
use Illuminate\Foundation\Http\FormRequest;
class AccountBalanceQuery extends QueryAll class UpdateRequest extends FormRequest
{ {
private Account $account; use ChecksLogin;
use ConvertsDataTypes;
public function getDate(): ?Carbon
{
return $this->getCarbonDate('date');
}
public function getRate(): string
{
return (string) $this->get('rate');
}
/** /**
* QuerySites constructor. * The rules that the incoming request must be matched against.
*/ */
public function __construct() public function rules(): array
{
parent::__construct();
}
public function get(): iterable
{ {
return [ return [
AccountBalance::fromArray(), 'date' => 'date|after:1900-01-01|before:2099-12-31',
AccountBalance::fromArray(), 'rate' => 'required|numeric|gt:0',
AccountBalance::fromArray(),
AccountBalance::fromArray(),
]; ];
} }
public function withAccount(Account $account): self
{
$this->account = $account;
return $this;
}
} }

View File

@ -0,0 +1,64 @@
<?php
/*
* IndexRequest.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\Request\Model\TransactionCurrency;
use FireflyIII\Support\Http\Api\AccountFilter;
use FireflyIII\Support\Request\ChecksLogin;
use FireflyIII\Support\Request\ConvertsDataTypes;
use FireflyIII\Support\Request\GetFilterInstructions;
use FireflyIII\Support\Request\GetSortInstructions;
use Illuminate\Foundation\Http\FormRequest;
/**
* Class IndexRequest
*
* Lots of code stolen from the SingleDateRequest.
*/
class IndexRequest extends FormRequest
{
use AccountFilter;
use ChecksLogin;
use ConvertsDataTypes;
use GetFilterInstructions;
use GetSortInstructions;
public function getAll(): array
{
return [
'enabled' => $this->convertBoolean($this->get('enabled')),
];
}
/**
* The rules that the incoming request must be matched against.
*/
public function rules(): array
{
return [
'enabled' => 'nullable|boolean',
];
}
}

View File

@ -1,5 +1,25 @@
<?php <?php
/*
* ConvertsDatesToUTC.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); declare(strict_types=1);
/* /*
* ConvertDatesToUTC.php * ConvertDatesToUTC.php
@ -21,7 +41,7 @@ declare(strict_types=1);
* along with this program. If not, see https://www.gnu.org/licenses/. * along with this program. If not, see https://www.gnu.org/licenses/.
*/ */
namespace FireflyIII\Console\Commands\Integrity; namespace FireflyIII\Console\Commands\Correction;
use Carbon\Carbon; use Carbon\Carbon;
use FireflyIII\Console\Commands\ShowsFriendlyMessages; use FireflyIII\Console\Commands\ShowsFriendlyMessages;
@ -31,17 +51,10 @@ use Illuminate\Database\QueryException;
use Illuminate\Support\Collection; use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Log; use Illuminate\Support\Facades\Log;
class ConvertDatesToUTC extends Command class ConvertsDatesToUTC extends Command
{ {
use ShowsFriendlyMessages; use ShowsFriendlyMessages;
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'firefly-iii:migrate-to-utc';
/** /**
* The console command description. * The console command description.
* *
@ -49,11 +62,22 @@ class ConvertDatesToUTC extends Command
*/ */
protected $description = 'Convert stored dates to UTC.'; protected $description = 'Convert stored dates to UTC.';
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'correction:convert-to-utc';
/** /**
* Execute the console command. * Execute the console command.
*/ */
public function handle(): int public function handle(): int
{ {
$this->friendlyWarning('Please do not use this command.');
return 0;
/** /**
* @var string $model * @var string $model
* @var array $fields * @var array $fields

View File

@ -1,253 +0,0 @@
<?php
/*
* CorrectAmounts.php
* Copyright (c) 2023 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\Correction;
use FireflyIII\Console\Commands\ShowsFriendlyMessages;
use FireflyIII\Models\AutoBudget;
use FireflyIII\Models\AvailableBudget;
use FireflyIII\Models\Bill;
use FireflyIII\Models\BudgetLimit;
use FireflyIII\Models\CurrencyExchangeRate;
use FireflyIII\Models\PiggyBank;
use FireflyIII\Models\PiggyBankRepetition;
use FireflyIII\Models\RecurrenceTransaction;
use FireflyIII\Models\RuleTrigger;
use Illuminate\Console\Command;
/**
* Class ReportSkeleton
*/
class CorrectAmounts extends Command
{
use ShowsFriendlyMessages;
protected $description = 'This command makes sure positive and negative amounts are recorded correctly.';
protected $signature = 'firefly-iii:fix-amount-pos-neg';
public function handle(): int
{
// auto budgets must be positive
$this->fixAutoBudgets();
// available budgets must be positive
$this->fixAvailableBudgets();
// bills must be positive (both amounts)
$this->fixBills();
// budget limits must be positive
$this->fixBudgetLimits();
// currency_exchange_rates must be positive
$this->fixExchangeRates();
// piggy_bank_repetitions must be positive
$this->fixRepetitions();
// piggy_banks must be positive
$this->fixPiggyBanks();
// recurrences_transactions amount must be positive
$this->fixRecurrences();
// rule_triggers must be positive or zero (amount_less, amount_more, amount_is)
$this->fixRuleTriggers();
return 0;
}
private function fixAutoBudgets(): void
{
$set = AutoBudget::where('amount', '<', 0)->get();
$count = $set->count();
if (0 === $count) {
$this->friendlyPositive('All auto budget amounts are positive.');
return;
}
/** @var AutoBudget $item */
foreach ($set as $item) {
$item->amount = app('steam')->positive($item->amount);
$item->save();
}
$this->friendlyInfo(sprintf('Corrected %d auto budget amount(s).', $count));
}
private function fixAvailableBudgets(): void
{
$set = AvailableBudget::where('amount', '<', 0)->get();
$count = $set->count();
if (0 === $count) {
$this->friendlyPositive('All available budget amounts are positive.');
return;
}
/** @var AvailableBudget $item */
foreach ($set as $item) {
$item->amount = app('steam')->positive($item->amount);
$item->save();
}
$this->friendlyInfo(sprintf('Corrected %d available budget amount(s).', $count));
}
private function fixBills(): void
{
$set = Bill::where('amount_min', '<', 0)->orWhere('amount_max', '<', 0)->get();
$count = $set->count();
if (0 === $count) {
$this->friendlyPositive('All bill amounts are positive.');
return;
}
/** @var Bill $item */
foreach ($set as $item) {
$item->amount_min = app('steam')->positive($item->amount_min);
$item->amount_max = app('steam')->positive($item->amount_max);
$item->save();
}
$this->friendlyInfo(sprintf('Corrected %d bill amount(s).', $count));
}
private function fixBudgetLimits(): void
{
$set = BudgetLimit::where('amount', '<', 0)->get();
$count = $set->count();
if (0 === $count) {
$this->friendlyPositive('All budget limit amounts are positive.');
return;
}
/** @var BudgetLimit $item */
foreach ($set as $item) {
$item->amount = app('steam')->positive($item->amount);
$item->save();
}
$this->friendlyInfo(sprintf('Corrected %d budget limit amount(s).', $count));
}
private function fixExchangeRates(): void
{
$set = CurrencyExchangeRate::where('rate', '<', 0)->get();
$count = $set->count();
if (0 === $count) {
$this->friendlyPositive('All currency exchange rates are positive.');
return;
}
/** @var CurrencyExchangeRate $item */
foreach ($set as $item) {
$item->rate = app('steam')->positive($item->rate);
$item->save();
}
$this->friendlyInfo(sprintf('Corrected %d currency exchange rate(s).', $count));
}
private function fixRepetitions(): void
{
$set = PiggyBankRepetition::where('currentamount', '<', 0)->get();
$count = $set->count();
if (0 === $count) {
$this->friendlyPositive('All piggy bank repetition amounts are positive.');
return;
}
/** @var PiggyBankRepetition $item */
foreach ($set as $item) {
$item->currentamount = app('steam')->positive($item->currentamount);
$item->save();
}
$this->friendlyInfo(sprintf('Corrected %d piggy bank repetition amount(s).', $count));
}
private function fixPiggyBanks(): void
{
$set = PiggyBank::where('targetamount', '<', 0)->get();
$count = $set->count();
if (0 === $count) {
$this->friendlyPositive('All piggy bank amounts are positive.');
return;
}
/** @var PiggyBank $item */
foreach ($set as $item) {
$item->targetamount = app('steam')->positive($item->targetamount);
$item->save();
}
$this->friendlyInfo(sprintf('Corrected %d piggy bank amount(s).', $count));
}
private function fixRecurrences(): void
{
$set = RecurrenceTransaction::where('amount', '<', 0)
->orWhere('foreign_amount', '<', 0)
->get()
;
$count = $set->count();
if (0 === $count) {
$this->friendlyPositive('All recurring transaction amounts are positive.');
return;
}
/** @var RecurrenceTransaction $item */
foreach ($set as $item) {
$item->amount = app('steam')->positive($item->amount);
$item->foreign_amount = app('steam')->positive($item->foreign_amount);
$item->save();
}
$this->friendlyInfo(sprintf('Corrected %d recurring transaction amount(s).', $count));
}
private function fixRuleTriggers(): void
{
$set = RuleTrigger::whereIn('trigger_type', ['amount_less', 'amount_more', 'amount_is'])->get();
$fixed = 0;
/** @var RuleTrigger $item */
foreach ($set as $item) {
// basic check:
$check = 0;
try {
$check = bccomp((string)$item->trigger_value, '0');
} catch (\ValueError $e) {
$this->friendlyError(sprintf('Rule #%d contained invalid %s-trigger "%s". The trigger has been removed, and the rule is disabled.', $item->rule_id, $item->trigger_type, $item->trigger_value));
$item->rule->active = false;
$item->rule->save();
$item->forceDelete();
}
if (-1 === $check) {
++$fixed;
$item->trigger_value = app('steam')->positive($item->trigger_value);
$item->save();
}
}
if (0 === $fixed) {
$this->friendlyPositive('All rule trigger amounts are positive.');
return;
}
$this->friendlyInfo(sprintf('Corrected %d rule trigger amount(s).', $fixed));
}
}

View File

@ -4,12 +4,9 @@ namespace FireflyIII\Console\Commands\Correction;
use Illuminate\Console\Command; use Illuminate\Console\Command;
/**
* Class CorrectionSkeleton
* TODO DONT FORGET TO ADD THIS TO THE DOCKER BUILD
*/
class CorrectionSkeleton extends Command class CorrectionSkeleton extends Command
{ {
use ShowsFriendlyMessages;
protected $description = 'DESCRIPTION HERE'; protected $description = 'DESCRIPTION HERE';
protected $signature = 'firefly-iii:CORR_COMMAND'; protected $signature = 'firefly-iii:CORR_COMMAND';

View File

@ -29,15 +29,12 @@ use FireflyIII\Repositories\Account\AccountRepositoryInterface;
use FireflyIII\User; use FireflyIII\User;
use Illuminate\Console\Command; use Illuminate\Console\Command;
/** class CorrectsAccountOrder extends Command
* Class FixAccountOrder
*/
class FixAccountOrder extends Command
{ {
use ShowsFriendlyMessages; use ShowsFriendlyMessages;
protected $description = 'Make sure account order is correct.'; protected $description = 'Make sure account order is correct.';
protected $signature = 'firefly-iii:fix-account-order'; protected $signature = 'correction:account-order';
private AccountRepositoryInterface $repository; private AccountRepositoryInterface $repository;
@ -54,8 +51,6 @@ class FixAccountOrder extends Command
$this->repository->resetAccountOrder(); $this->repository->resetAccountOrder();
} }
$this->friendlyPositive('All accounts are ordered correctly');
return 0; return 0;
} }

View File

@ -36,15 +36,12 @@ use Illuminate\Console\Command;
use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Query\JoinClause; use Illuminate\Database\Query\JoinClause;
/** class CorrectsAccountTypes extends Command
* Class FixAccountTypes
*/
class FixAccountTypes extends Command
{ {
use ShowsFriendlyMessages; use ShowsFriendlyMessages;
protected $description = 'Make sure all journals have the correct from/to account types.'; protected $description = 'Make sure all journals have the correct from/to account types.';
protected $signature = 'firefly-iii:fix-account-types'; protected $signature = 'correction:account-types';
private int $count; private int $count;
private array $expected; private array $expected;
private AccountFactory $factory; private AccountFactory $factory;
@ -120,9 +117,6 @@ class FixAccountTypes extends Command
} }
} }
} }
if (0 === $this->count) {
$this->friendlyPositive('All account types are OK');
}
if (0 !== $this->count) { if (0 !== $this->count) {
app('log')->debug(sprintf('%d journals had to be fixed.', $this->count)); app('log')->debug(sprintf('%d journals had to be fixed.', $this->count));
$this->friendlyInfo(sprintf('Acted on %d transaction(s)', $this->count)); $this->friendlyInfo(sprintf('Acted on %d transaction(s)', $this->count));

View File

@ -0,0 +1,185 @@
<?php
/*
* CorrectAmounts.php
* Copyright (c) 2023 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\Correction;
use FireflyIII\Console\Commands\ShowsFriendlyMessages;
use FireflyIII\Models\AutoBudget;
use FireflyIII\Models\AvailableBudget;
use FireflyIII\Models\Bill;
use FireflyIII\Models\BudgetLimit;
use FireflyIII\Models\CurrencyExchangeRate;
use FireflyIII\Models\PiggyBank;
use FireflyIII\Models\RecurrenceTransaction;
use FireflyIII\Models\RuleTrigger;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\DB;
class CorrectsAmounts extends Command
{
use ShowsFriendlyMessages;
protected $description = 'This command makes sure positive and negative amounts are recorded correctly.';
protected $signature = 'correction:amounts';
public function handle(): int
{
// auto budgets must be positive
$this->fixAutoBudgets();
// available budgets must be positive
$this->fixAvailableBudgets();
// bills must be positive (both amounts)
$this->fixBills();
// budget limits must be positive
$this->fixBudgetLimits();
// currency_exchange_rates must be positive
$this->fixExchangeRates();
// piggy_banks must be positive
$this->fixPiggyBanks();
// recurrences_transactions amount must be positive
$this->fixRecurrences();
// rule_triggers must be positive or zero (amount_less, amount_more, amount_is)
$this->fixRuleTriggers();
return 0;
}
private function fixAutoBudgets(): void
{
$count = AutoBudget::where('amount', '<', 0)->update(['amount' => DB::raw('amount * -1')]);
if (0 === $count) {
return;
}
$this->friendlyInfo(sprintf('Corrected %d auto budget amount(s).', $count));
}
private function fixAvailableBudgets(): void
{
$count = AvailableBudget::where('amount', '<', 0)->update(['amount' => DB::raw('amount * -1')]);
if (0 === $count) {
return;
}
$this->friendlyInfo(sprintf('Corrected %d available budget amount(s).', $count));
}
private function fixBills(): void
{
$count = 0;
$count += Bill::where('amount_max', '<', 0)->update(['amount_max' => DB::raw('amount_max * -1')]);
$count += Bill::where('amount_min', '<', 0)->update(['amount_min' => DB::raw('amount_min * -1')]);
if (0 === $count) {
return;
}
$this->friendlyInfo(sprintf('Corrected %d bill amount(s).', $count));
}
private function fixBudgetLimits(): void
{
$count = BudgetLimit::where('amount', '<', 0)->update(['amount' => DB::raw('amount * -1')]);
if (0 === $count) {
return;
}
$this->friendlyInfo(sprintf('Corrected %d budget limit amount(s).', $count));
}
private function fixExchangeRates(): void
{
$count = CurrencyExchangeRate::where('rate', '<', 0)->update(['rate' => DB::raw('rate * -1')]);
if (0 === $count) {
return;
}
$this->friendlyInfo(sprintf('Corrected %d currency exchange rate(s).', $count));
}
private function fixPiggyBanks(): void
{
$count = PiggyBank::where('target_amount', '<', 0)->update(['target_amount' => DB::raw('target_amount * -1')]);
if (0 === $count) {
return;
}
$this->friendlyInfo(sprintf('Corrected %d piggy bank amount(s).', $count));
}
private function fixRecurrences(): void
{
$count = 0;
$count += RecurrenceTransaction::where('amount', '<', 0)->update(['amount' => DB::raw('amount * -1')]);
$count += RecurrenceTransaction::where('foreign_amount', '<', 0)->update(['foreign_amount' => DB::raw('foreign_amount * -1')]);
if (0 === $count) {
return;
}
$this->friendlyInfo(sprintf('Corrected %d recurring transaction amount(s).', $count));
}
/**
* Foreach loop is unavoidable here.
*/
private function fixRuleTriggers(): void
{
$set = RuleTrigger::whereIn('trigger_type', ['amount_less', 'amount_more', 'amount_is'])->get();
$fixed = 0;
/** @var RuleTrigger $item */
foreach ($set as $item) {
$result = $this->fixRuleTrigger($item);
if (true === $result) {
++$fixed;
}
}
if (0 === $fixed) {
return;
}
$this->friendlyInfo(sprintf('Corrected %d rule trigger amount(s).', $fixed));
}
private function fixRuleTrigger(RuleTrigger $item): bool
{
try {
$check = bccomp((string) $item->trigger_value, '0');
} catch (\ValueError $e) {
$this->friendlyError(sprintf('Rule #%d contained invalid %s-trigger "%s". The trigger has been removed, and the rule is disabled.', $item->rule_id, $item->trigger_type, $item->trigger_value));
$item->rule->active = false;
$item->rule->save();
$item->forceDelete();
return false;
}
if (-1 === $check) {
$item->trigger_value = app('steam')->positive($item->trigger_value);
$item->save();
return true;
}
return false;
}
}

View File

@ -37,15 +37,12 @@ use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Log; use Illuminate\Support\Facades\Log;
use Symfony\Component\Console\Command\Command as CommandAlias; use Symfony\Component\Console\Command\Command as CommandAlias;
/** class CorrectsCurrencies extends Command
* Class EnableCurrencies
*/
class EnableCurrencies extends Command
{ {
use ShowsFriendlyMessages; use ShowsFriendlyMessages;
protected $description = 'Enables all currencies in use.'; protected $description = 'Enables all currencies in use.';
protected $signature = 'firefly-iii:enable-currencies'; protected $signature = 'correction:currencies';
/** /**
* Execute the console command. * Execute the console command.

View File

@ -27,14 +27,11 @@ namespace FireflyIII\Console\Commands\Correction;
use FireflyIII\Console\Commands\ShowsFriendlyMessages; use FireflyIII\Console\Commands\ShowsFriendlyMessages;
use Illuminate\Console\Command; use Illuminate\Console\Command;
/** class CorrectsDatabase extends Command
* Class CorrectDatabase
*/
class CorrectDatabase extends Command
{ {
use ShowsFriendlyMessages; use ShowsFriendlyMessages;
protected $description = 'Will correct the integrity of your database, if necessary.'; protected $description = 'Will validate and correct the integrity of your database, if necessary.';
protected $signature = 'firefly-iii:correct-database'; protected $signature = 'firefly-iii:correct-database';
/** /**
@ -49,32 +46,36 @@ class CorrectDatabase extends Command
return 1; return 1;
} }
$commands = [ $commands = [
'firefly-iii:fix-piggies', 'correction:restore-oauth-keys',
'firefly-iii:create-link-types', 'correction:timezones',
'firefly-iii:create-access-tokens', 'correction:create-group-memberships',
'firefly-iii:remove-bills', 'correction:group-information',
'firefly-iii:fix-amount-pos-neg', 'correction:piggy-banks',
'firefly-iii:enable-currencies', 'correction:link-types',
'firefly-iii:fix-transfer-budgets', 'correction:access-tokens',
'firefly-iii:fix-uneven-amount', 'correction:bills',
'firefly-iii:delete-zero-amount', 'correction:amounts',
'firefly-iii:delete-orphaned-transactions', 'correction:currencies',
'firefly-iii:delete-empty-journals', 'correction:transfer-budgets',
'firefly-iii:delete-empty-groups', 'correction:uneven-amounts',
'firefly-iii:fix-account-types', 'correction:zero-amounts',
'firefly-iii:fix-ibans', 'correction:orphaned-transactions',
'firefly-iii:fix-account-order', 'correction:empty-journals',
'firefly-iii:rename-meta-fields', 'correction:empty-groups',
'firefly-iii:fix-ob-currencies', 'correction:account-types',
'firefly-iii:fix-long-descriptions', 'correction:ibans',
'firefly-iii:fix-recurring-transactions', 'correction:account-order',
'firefly-iii:upgrade-group-information', 'correction:meta-fields',
'firefly-iii:fix-transaction-types', 'correction:opening-balance-currencies',
'firefly-iii:fix-frontpage-accounts', 'correction:long-descriptions',
// new! 'correction:recurring-transactions',
'firefly-iii:unify-group-accounts', 'correction:frontpage-accounts',
'firefly-iii:trigger-credit-recalculation', 'correction:group-accounts',
'firefly-iii:migrate-preferences', 'correction:recalculates-liabilities',
'correction:preferences',
// 'correction:transaction-types', // resource heavy, disabled.
'correction:recalculate-native-amounts', // not necessary, disabled.
'firefly-iii:report-integrity',
]; ];
foreach ($commands as $command) { foreach ($commands as $command) {
$this->friendlyLine(sprintf('Now executing command "%s"', $command)); $this->friendlyLine(sprintf('Now executing command "%s"', $command));

View File

@ -31,15 +31,12 @@ use FireflyIII\Repositories\Account\AccountRepositoryInterface;
use FireflyIII\User; use FireflyIII\User;
use Illuminate\Console\Command; use Illuminate\Console\Command;
/** class CorrectsFrontpageAccounts extends Command
* Class FixFrontpageAccounts
*/
class FixFrontpageAccounts extends Command
{ {
use ShowsFriendlyMessages; use ShowsFriendlyMessages;
protected $description = 'Fixes a preference that may include deleted accounts or accounts of another type.'; protected $description = 'Fixes a preference that may include deleted accounts or accounts of another type.';
protected $signature = 'firefly-iii:fix-frontpage-accounts'; protected $signature = 'correction:frontpage-accounts';
/** /**
* Execute the console command. * Execute the console command.
@ -55,7 +52,6 @@ class FixFrontpageAccounts extends Command
$this->fixPreference($preference); $this->fixPreference($preference);
} }
} }
$this->friendlyPositive('Account preferences are OK');
return 0; return 0;
} }

View File

@ -31,15 +31,12 @@ use FireflyIII\Models\TransactionGroup;
use FireflyIII\Models\TransactionJournal; use FireflyIII\Models\TransactionJournal;
use Illuminate\Console\Command; use Illuminate\Console\Command;
/** class CorrectsGroupAccounts extends Command
* Class FixGroupAccounts
*/
class FixGroupAccounts extends Command
{ {
use ShowsFriendlyMessages; use ShowsFriendlyMessages;
protected $description = 'Unify the source / destination accounts of split groups.'; protected $description = 'Unify the source / destination accounts of split groups.';
protected $signature = 'firefly-iii:unify-group-accounts'; protected $signature = 'correction:group-accounts';
/** /**
* Execute the console command. * Execute the console command.
@ -64,8 +61,6 @@ class FixGroupAccounts extends Command
$handler->unifyAccounts($event); $handler->unifyAccounts($event);
} }
$this->friendlyPositive('Updated possible inconsistent transaction groups.');
return 0; return 0;
} }
} }

View File

@ -1,8 +1,8 @@
<?php <?php
/* /*
* UpdateGroupInformation.php * CorrectsGroupInformation.php
* Copyright (c) 2022 james@firefly-iii.org * Copyright (c) 2024 james@firefly-iii.org.
* *
* This file is part of Firefly III (https://github.com/firefly-iii). * This file is part of Firefly III (https://github.com/firefly-iii).
* *
@ -17,12 +17,12 @@
* GNU Affero General Public License for more details. * GNU Affero General Public License for more details.
* *
* You should have received a copy of the GNU Affero General Public License * 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/>. * along with this program. If not, see https://www.gnu.org/licenses/.
*/ */
declare(strict_types=1); declare(strict_types=1);
namespace FireflyIII\Console\Commands\Integrity; namespace FireflyIII\Console\Commands\Correction;
use FireflyIII\Console\Commands\ShowsFriendlyMessages; use FireflyIII\Console\Commands\ShowsFriendlyMessages;
use FireflyIII\Models\Account; use FireflyIII\Models\Account;
@ -45,15 +45,12 @@ use FireflyIII\User;
use Illuminate\Console\Command; use Illuminate\Console\Command;
use Illuminate\Database\QueryException; use Illuminate\Database\QueryException;
/** class CorrectsGroupInformation extends Command
* Class UpdateGroupInformation
*/
class UpdateGroupInformation extends Command
{ {
use ShowsFriendlyMessages; use ShowsFriendlyMessages;
protected $description = 'Makes sure that every object is linked to a group'; protected $description = 'Makes sure that every object is linked to a group';
protected $signature = 'firefly-iii:upgrade-group-information'; protected $signature = 'correction:group-information';
/** /**
* Execute the console command. * Execute the console command.
@ -79,7 +76,7 @@ class UpdateGroupInformation extends Command
{ {
$group = $user->userGroup; $group = $user->userGroup;
if (null === $group) { if (null === $group) {
$this->friendlyWarning(sprintf('User "%s" has no group.', $user->email)); $this->friendlyWarning(sprintf('User "%s" has no group. Please run "php artisan firefly-iii:create-group-memberships"', $user->email));
return; return;
} }

View File

@ -30,15 +30,12 @@ use FireflyIII\Models\AccountType;
use Illuminate\Console\Command; use Illuminate\Console\Command;
use Illuminate\Support\Collection; use Illuminate\Support\Collection;
/** class CorrectsIbans extends Command
* Class FixIbans
*/
class FixIbans extends Command
{ {
use ShowsFriendlyMessages; use ShowsFriendlyMessages;
protected $description = 'Removes spaces from IBANs'; protected $description = 'Removes spaces from IBANs';
protected $signature = 'firefly-iii:fix-ibans'; protected $signature = 'correction:ibans';
private int $count = 0; private int $count = 0;
/** /**
@ -49,9 +46,6 @@ class FixIbans extends Command
$accounts = Account::whereNotNull('iban')->get(); $accounts = Account::whereNotNull('iban')->get();
$this->filterIbans($accounts); $this->filterIbans($accounts);
$this->countAndCorrectIbans($accounts); $this->countAndCorrectIbans($accounts);
if (0 === $this->count) {
$this->friendlyPositive('All IBANs are valid.');
}
return 0; return 0;
} }

View File

@ -28,24 +28,22 @@ use FireflyIII\Console\Commands\ShowsFriendlyMessages;
use FireflyIII\Models\TransactionGroup; use FireflyIII\Models\TransactionGroup;
use FireflyIII\Models\TransactionJournal; use FireflyIII\Models\TransactionJournal;
use Illuminate\Console\Command; use Illuminate\Console\Command;
use Illuminate\Support\Facades\DB;
/** class CorrectsLongDescriptions extends Command
* Class FixLongDescriptions
*/
class FixLongDescriptions extends Command
{ {
use ShowsFriendlyMessages; use ShowsFriendlyMessages;
private const int MAX_LENGTH = 1000; private const int MAX_LENGTH = 1000;
protected $description = 'Fixes long descriptions in journals and groups.'; protected $description = 'Fixes long descriptions in journals and groups.';
protected $signature = 'firefly-iii:fix-long-descriptions'; protected $signature = 'correction:long-descriptions';
/** /**
* Execute the console command. * Execute the console command.
*/ */
public function handle(): int public function handle(): int
{ {
$journals = TransactionJournal::get(['id', 'description']); $journals = TransactionJournal::where(DB::raw('LENGTH(description)'), '>', self::MAX_LENGTH)->get(['id', 'description']);
$count = 0; $count = 0;
/** @var TransactionJournal $journal */ /** @var TransactionJournal $journal */
@ -58,7 +56,7 @@ class FixLongDescriptions extends Command
} }
} }
$groups = TransactionGroup::get(['id', 'title']); $groups = TransactionGroup::where(DB::raw('LENGTH(title)'), '>', self::MAX_LENGTH)->get(['id', 'title']);
/** @var TransactionGroup $group */ /** @var TransactionGroup $group */
foreach ($groups as $group) { foreach ($groups as $group) {
@ -69,9 +67,6 @@ class FixLongDescriptions extends Command
++$count; ++$count;
} }
} }
if (0 === $count) {
$this->friendlyPositive('All transaction group and journal title lengths are within bounds.');
}
return 0; return 0;
} }

View File

@ -26,16 +26,14 @@ namespace FireflyIII\Console\Commands\Correction;
use FireflyIII\Console\Commands\ShowsFriendlyMessages; use FireflyIII\Console\Commands\ShowsFriendlyMessages;
use Illuminate\Console\Command; use Illuminate\Console\Command;
use Illuminate\Support\Facades\DB;
/** class CorrectsMetaDataFields extends Command
* Class RenameMetaFields
*/
class RenameMetaFields extends Command
{ {
use ShowsFriendlyMessages; use ShowsFriendlyMessages;
protected $description = 'Rename changed meta fields.'; protected $description = 'Rename changed meta fields.';
protected $signature = 'firefly-iii:rename-meta-fields'; protected $signature = 'correction:meta-fields';
private int $count = 0; private int $count = 0;
@ -61,9 +59,6 @@ class RenameMetaFields extends Command
foreach ($changes as $original => $update) { foreach ($changes as $original => $update) {
$this->rename($original, $update); $this->rename($original, $update);
} }
if (0 === $this->count) {
$this->friendlyPositive('All meta fields are correct.');
}
if (0 !== $this->count) { if (0 !== $this->count) {
$this->friendlyInfo(sprintf('Renamed %d meta field(s).', $this->count)); $this->friendlyInfo(sprintf('Renamed %d meta field(s).', $this->count));
} }
@ -73,7 +68,7 @@ class RenameMetaFields extends Command
private function rename(string $original, string $update): void private function rename(string $original, string $update): void
{ {
$total = \DB::table('journal_meta') $total = DB::table('journal_meta')
->where('name', '=', $original) ->where('name', '=', $original)
->update(['name' => $update]) ->update(['name' => $update])
; ;

View File

@ -0,0 +1,243 @@
<?php
declare(strict_types=1);
/*
* RecalculateNativeAmounts.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/.
*/
namespace FireflyIII\Console\Commands\Correction;
use FireflyIII\Console\Commands\ShowsFriendlyMessages;
use FireflyIII\Handlers\Observer\TransactionObserver;
use FireflyIII\Models\Account;
use FireflyIII\Models\AutoBudget;
use FireflyIII\Models\AvailableBudget;
use FireflyIII\Models\Bill;
use FireflyIII\Models\Budget;
use FireflyIII\Models\BudgetLimit;
use FireflyIII\Models\PiggyBank;
use FireflyIII\Models\PiggyBankEvent;
use FireflyIII\Models\Transaction;
use FireflyIII\Models\TransactionCurrency;
use FireflyIII\Models\UserGroup;
use FireflyIII\Repositories\UserGroup\UserGroupRepositoryInterface;
use FireflyIII\Repositories\UserGroups\PiggyBank\PiggyBankRepositoryInterface;
use FireflyIII\Support\Facades\Preferences;
use FireflyIII\Support\Http\Api\ExchangeRateConverter;
use Illuminate\Console\Command;
use Illuminate\Database\Eloquent\Builder as EloquentBuilder;
use Illuminate\Database\Query\Builder as DatabaseBuilder;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;
class CorrectsNativeAmounts extends Command
{
use ShowsFriendlyMessages;
protected $description = 'Recalculate native amounts for all objects.';
protected $signature = 'correction:recalculate-native-amounts';
/**
* Execute the console 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!');
/** @var UserGroupRepositoryInterface $repository */
$repository = app(UserGroupRepositoryInterface::class);
/** @var UserGroup $userGroup */
foreach ($repository->getAll() as $userGroup) {
$this->recalculateForGroup($userGroup);
}
$this->friendlyInfo('Recalculated all native amounts.');
return 0;
}
private function recalculateForGroup(UserGroup $userGroup): void
{
Log::debug(sprintf('Now recalculating for user group #%d', $userGroup->id));
$this->recalculateAccounts($userGroup);
// do a check with the group's currency so we can skip some stuff.
Preferences::mark();
$currency = app('amount')->getDefaultCurrencyByUserGroup($userGroup);
$this->recalculatePiggyBanks($userGroup, $currency);
$this->recalculateBudgets($userGroup, $currency);
$this->recalculateAvailableBudgets($userGroup, $currency);
$this->recalculateBills($userGroup, $currency);
$this->calculateTransactions($userGroup, $currency);
}
private function recalculateAccounts(UserGroup $userGroup): void
{
$set = $userGroup->accounts()->where(function (EloquentBuilder $q): void {
$q->whereNotNull('virtual_balance');
$q->orWhere('virtual_balance', '!=', '');
})->get();
/** @var Account $account */
foreach ($set as $account) {
$account->touch();
}
Log::debug(sprintf('Recalculated %d accounts for user group #%d.', $set->count(), $userGroup->id));
}
private function recalculatePiggyBanks(UserGroup $userGroup, TransactionCurrency $currency): void
{
$converter = new ExchangeRateConverter();
$converter->setUserGroup($userGroup);
$converter->setIgnoreSettings(true);
$repository = app(PiggyBankRepositoryInterface::class);
$repository->setUserGroup($userGroup);
$set = $repository->getPiggyBanks();
$set = $set->filter(
static function (PiggyBank $piggyBank) use ($currency) {
return $currency->id !== $piggyBank->transaction_currency_id;
}
);
foreach ($set as $piggyBank) {
$piggyBank->encrypted = false;
$piggyBank->save();
foreach ($piggyBank->accounts as $account) {
$account->pivot->native_current_amount = null;
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();
}
$this->recalculatePiggyBankEvents($piggyBank);
}
Log::debug(sprintf('Recalculated %d piggy banks for user group #%d.', $set->count(), $userGroup->id));
}
private function recalculatePiggyBankEvents(PiggyBank $piggyBank): void
{
$set = $piggyBank->piggyBankEvents()->get();
$set->each(
static function (PiggyBankEvent $event): void {
$event->touch();
}
);
Log::debug(sprintf('Recalculated %d piggy bank events.', $set->count()));
}
private function recalculateBudgets(UserGroup $userGroup, TransactionCurrency $currency): void
{
$set = $userGroup->budgets()->get();
/** @var Budget $budget */
foreach ($set as $budget) {
$this->recalculateBudgetLimits($budget, $currency);
$this->recalculateAutoBudgets($budget, $currency);
}
Log::debug(sprintf('Recalculated %d budgets.', $set->count()));
}
private function recalculateBudgetLimits(Budget $budget, TransactionCurrency $currency): void
{
$set = $budget->budgetlimits()->where('transaction_currency_id', '!=', $currency->id)->get();
/** @var BudgetLimit $limit */
foreach ($set as $limit) {
Log::debug(sprintf('Will now touch BL #%d', $limit->id));
$limit->touch();
Log::debug(sprintf('Done with touch BL #%d', $limit->id));
}
Log::debug(sprintf('Recalculated %d budget limits for budget #%d.', $set->count(), $budget->id));
}
private function recalculateAutoBudgets(Budget $budget, TransactionCurrency $currency): void
{
$set = $budget->autoBudgets()->where('transaction_currency_id', '!=', $currency->id)->get();
/** @var AutoBudget $autoBudget */
foreach ($set as $autoBudget) {
$autoBudget->touch();
}
Log::debug(sprintf('Recalculated %d auto budgets for budget #%d.', $set->count(), $budget->id));
}
private function recalculateAvailableBudgets(UserGroup $userGroup, TransactionCurrency $currency): void
{
Log::debug('Start with available budgets.');
$set = $userGroup->availableBudgets()->where('transaction_currency_id', '!=', $currency->id)->get();
/** @var AvailableBudget $budget */
foreach ($set as $budget) {
$budget->touch();
}
Log::debug(sprintf('Recalculated %d available budgets.', $set->count()));
}
private function recalculateBills(UserGroup $userGroup, TransactionCurrency $currency): void
{
$set = $userGroup->bills()->where('transaction_currency_id', '!=', $currency->id)->get();
/** @var Bill $bill */
foreach ($set as $bill) {
$bill->touch();
}
Log::debug(sprintf('Recalculated %d bills.', $set->count()));
}
private function calculateTransactions(UserGroup $userGroup, TransactionCurrency $currency): void
{
// custom query because of the potential size of this update.
$set = DB::table('transactions')
->join('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id')
->where('transaction_journals.user_group_id', $userGroup->id)
->where(function (DatabaseBuilder $q1) use ($currency): void {
$q1->where(function (DatabaseBuilder $q2) use ($currency): void {
$q2->whereNot('transactions.transaction_currency_id', $currency->id)->whereNull('transactions.foreign_currency_id');
})->orWhere(function (DatabaseBuilder $q3) use ($currency): void {
$q3->whereNot('transactions.transaction_currency_id', $currency->id)->whereNot('transactions.foreign_currency_id', $currency->id);
});
})
// ->where(static function (DatabaseBuilder $q) use ($currency): void {
// $q->whereNot('transactions.transaction_currency_id', $currency->id)
// ->whereNot('transactions.foreign_currency_id', $currency->id)
// ;
// })
->get(['transactions.id'])
;
TransactionObserver::$recalculate = false;
foreach ($set as $item) {
// here we are.
$transaction = Transaction::find($item->id);
$transaction->touch();
}
TransactionObserver::$recalculate = true;
Log::debug(sprintf('Recalculated %d transactions.', $set->count()));
}
}

View File

@ -35,15 +35,12 @@ use FireflyIII\Repositories\Account\AccountRepositoryInterface;
use Illuminate\Console\Command; use Illuminate\Console\Command;
use Illuminate\Support\Collection; use Illuminate\Support\Collection;
/** class CorrectsOpeningBalanceCurrencies extends Command
* Class CorrectOpeningBalanceCurrencies
*/
class CorrectOpeningBalanceCurrencies extends Command
{ {
use ShowsFriendlyMessages; use ShowsFriendlyMessages;
protected $description = 'Will make sure that opening balance transaction currencies match the account they\'re for.'; protected $description = 'Will make sure that opening balance transaction currencies match the account they\'re for.';
protected $signature = 'firefly-iii:fix-ob-currencies'; protected $signature = 'correction:opening-balance-currencies';
/** /**
* Execute the console command. * Execute the console command.
@ -62,10 +59,6 @@ class CorrectOpeningBalanceCurrencies extends Command
$message = sprintf('Corrected %d opening balance transaction(s).', $count); $message = sprintf('Corrected %d opening balance transaction(s).', $count);
$this->friendlyInfo($message); $this->friendlyInfo($message);
} }
if (0 === $count) {
$message = 'There was nothing to fix in the opening balance transactions.';
$this->friendlyPositive($message);
}
return 0; return 0;
} }

View File

@ -29,17 +29,12 @@ use FireflyIII\Models\PiggyBankEvent;
use FireflyIII\Models\TransactionJournal; use FireflyIII\Models\TransactionJournal;
use Illuminate\Console\Command; use Illuminate\Console\Command;
/** class CorrectsPiggyBanks extends Command
* Report (and fix) piggy banks.
*
* Class FixPiggies
*/
class FixPiggies extends Command
{ {
use ShowsFriendlyMessages; use ShowsFriendlyMessages;
protected $description = 'Fixes common issues with piggy banks.'; protected $description = 'Fixes common issues with piggy banks.';
protected $signature = 'firefly-iii:fix-piggies'; protected $signature = 'correction:piggy-banks';
/** /**
* Execute the console command. * Execute the console command.
@ -66,9 +61,6 @@ class FixPiggies extends Command
continue; continue;
} }
} }
if (0 === $count) {
$this->friendlyPositive('All piggy bank events are OK.');
}
if (0 !== $count) { if (0 !== $count) {
$this->friendlyInfo(sprintf('Fixed %d piggy bank event(s).', $count)); $this->friendlyInfo(sprintf('Fixed %d piggy bank event(s).', $count));
} }

View File

@ -28,11 +28,11 @@ use FireflyIII\User;
use Illuminate\Console\Command; use Illuminate\Console\Command;
use Symfony\Component\Console\Command\Command as CommandAlias; use Symfony\Component\Console\Command\Command as CommandAlias;
class MigratePreferences extends Command class CorrectsPreferences extends Command
{ {
protected $description = 'Give Firefly III preferences a user group ID so they can be made administration specific.'; protected $description = 'Give Firefly III preferences a user group ID so they can be made administration specific.';
protected $signature = 'firefly-iii:migrate-preferences'; protected $signature = 'correction:preferences';
/** /**
* Execute the console command. * Execute the console command.
@ -50,7 +50,7 @@ class MigratePreferences extends Command
if (null === $preference) { if (null === $preference) {
continue; continue;
} }
if (null !== $preference->user_group_id) { if (null === $preference->user_group_id) {
$preference->user_group_id = $user->user_group_id; $preference->user_group_id = $user->user_group_id;
$preference->save(); $preference->save();
++$count; ++$count;

View File

@ -33,15 +33,12 @@ use FireflyIII\Repositories\User\UserRepositoryInterface;
use FireflyIII\User; use FireflyIII\User;
use Illuminate\Console\Command; use Illuminate\Console\Command;
/** class CorrectsRecurringTransactions extends Command
* Class FixRecurringTransactions
*/
class FixRecurringTransactions extends Command
{ {
use ShowsFriendlyMessages; use ShowsFriendlyMessages;
protected $description = 'Fixes recurring transactions with the wrong transaction type.'; protected $description = 'Fixes recurring transactions with the wrong transaction type.';
protected $signature = 'firefly-iii:fix-recurring-transactions'; protected $signature = 'correction:recurring-transactions';
private int $count = 0; private int $count = 0;
private RecurringRepositoryInterface $recurringRepos; private RecurringRepositoryInterface $recurringRepos;
private UserRepositoryInterface $userRepos; private UserRepositoryInterface $userRepos;
@ -53,9 +50,6 @@ class FixRecurringTransactions extends Command
{ {
$this->stupidLaravel(); $this->stupidLaravel();
$this->correctTransactions(); $this->correctTransactions();
if (0 === $this->count) {
$this->friendlyPositive('All recurring transactions are OK.');
}
return 0; return 0;
} }

View File

@ -1,5 +1,25 @@
<?php <?php
/*
* CorrectsTimezoneInformation.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); declare(strict_types=1);
/* /*
* AddTimezonesToDates.php * AddTimezonesToDates.php
@ -21,7 +41,7 @@ declare(strict_types=1);
* along with this program. If not, see https://www.gnu.org/licenses/. * along with this program. If not, see https://www.gnu.org/licenses/.
*/ */
namespace FireflyIII\Console\Commands\Integrity; namespace FireflyIII\Console\Commands\Correction;
use FireflyIII\Console\Commands\ShowsFriendlyMessages; use FireflyIII\Console\Commands\ShowsFriendlyMessages;
use FireflyIII\Models\AccountBalance; use FireflyIII\Models\AccountBalance;
@ -41,16 +61,25 @@ use Illuminate\Console\Command;
use Illuminate\Database\QueryException; use Illuminate\Database\QueryException;
use Illuminate\Support\Facades\Log; use Illuminate\Support\Facades\Log;
class AddTimezonesToDates extends Command class CorrectsTimezoneInformation extends Command
{ {
use ShowsFriendlyMessages; use ShowsFriendlyMessages;
/** public static array $models
* The name and signature of the console command. = [
* AccountBalance::class => ['date'], // done
* @var string AvailableBudget::class => ['start_date', 'end_date'], // done
*/ Bill::class => ['date', 'end_date', 'extension_date'], // done
protected $signature = 'firefly-iii:add-timezones-to-dates'; BudgetLimit::class => ['start_date', 'end_date'], // done
CurrencyExchangeRate::class => ['date'], // done
InvitedUser::class => ['expires'],
PiggyBankEvent::class => ['date'],
PiggyBankRepetition::class => ['start_date', 'target_date'],
PiggyBank::class => ['start_date', 'target_date'], // done
Recurrence::class => ['first_date', 'repeat_until', 'latest_date'],
Tag::class => ['date'],
TransactionJournal::class => ['date'],
];
/** /**
* The console command description. * The console command description.
@ -59,20 +88,12 @@ class AddTimezonesToDates extends Command
*/ */
protected $description = 'Make sure all dates have a timezone.'; protected $description = 'Make sure all dates have a timezone.';
public static array $models = [ /**
AccountBalance::class => ['date'], // done * The name and signature of the console command.
AvailableBudget::class => ['start_date', 'end_date'], // done *
Bill::class => ['date', 'end_date', 'extension_date'], // done * @var string
BudgetLimit::class => ['start_date', 'end_date'], // done */
CurrencyExchangeRate::class => ['date'], // done protected $signature = 'correction:timezones';
InvitedUser::class => ['expires'],
PiggyBankEvent::class => ['date'],
PiggyBankRepetition::class => ['startdate', 'targetdate'],
PiggyBank::class => ['startdate', 'targetdate'], // done
Recurrence::class => ['first_date', 'repeat_until', 'latest_date'],
Tag::class => ['date'],
TransactionJournal::class => ['date'],
];
/** /**
* Execute the console command. * Execute the console command.
@ -106,8 +127,6 @@ class AddTimezonesToDates extends Command
Log::error($e->getMessage()); Log::error($e->getMessage());
} }
if (0 === $count) { if (0 === $count) {
$this->friendlyPositive(sprintf('Timezone information is present in field "%s" of model "%s".', $field, $shortModel));
return; return;
} }
$this->friendlyInfo(sprintf('Adding timezone information to field "%s" of model "%s".', $field, $shortModel)); $this->friendlyInfo(sprintf('Adding timezone information to field "%s" of model "%s".', $field, $shortModel));

View File

@ -32,16 +32,14 @@ use FireflyIII\Models\TransactionJournal;
use FireflyIII\Models\TransactionType; use FireflyIII\Models\TransactionType;
use Illuminate\Console\Command; use Illuminate\Console\Command;
use Illuminate\Support\Collection; use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Log;
/** class CorrectsTransactionTypes extends Command
* Class FixTransactionTypes
*/
class FixTransactionTypes extends Command
{ {
use ShowsFriendlyMessages; use ShowsFriendlyMessages;
protected $description = 'Make sure all transactions are of the correct type, based on source + dest.'; protected $description = 'Make sure all transactions are of the correct type, based on source + dest.';
protected $signature = 'firefly-iii:fix-transaction-types'; protected $signature = 'correction:transaction-types';
/** /**
* Execute the console command. * Execute the console command.
@ -50,6 +48,7 @@ class FixTransactionTypes extends Command
{ {
$count = 0; $count = 0;
$journals = $this->collectJournals(); $journals = $this->collectJournals();
Log::debug(sprintf('In FixTransactionTypes, found %d journals.', $journals->count()));
/** @var TransactionJournal $journal */ /** @var TransactionJournal $journal */
foreach ($journals as $journal) { foreach ($journals as $journal) {

View File

@ -29,15 +29,12 @@ use FireflyIII\Models\TransactionJournal;
use FireflyIII\Models\TransactionType; use FireflyIII\Models\TransactionType;
use Illuminate\Console\Command; use Illuminate\Console\Command;
/** class CorrectsTransferBudgets extends Command
* Class TransferBudgets
*/
class TransferBudgets extends Command
{ {
use ShowsFriendlyMessages; use ShowsFriendlyMessages;
protected $description = 'Removes budgets from transfers.'; protected $description = 'Removes budgets from transfers.';
protected $signature = 'firefly-iii:fix-transfer-budgets'; protected $signature = 'correction:transfer-budgets';
/** /**
* Execute the console command. * Execute the console command.
@ -60,10 +57,6 @@ class TransferBudgets extends Command
$entry->budgets()->sync([]); $entry->budgets()->sync([]);
++$count; ++$count;
} }
if (0 === $count) {
$message = 'No invalid budget/journal entries.';
$this->friendlyPositive($message);
}
if (0 !== $count) { if (0 !== $count) {
$message = sprintf('Corrected %d invalid budget/journal entries (entry).', $count); $message = sprintf('Corrected %d invalid budget/journal entries (entry).', $count);
app('log')->debug($message); app('log')->debug($message);

View File

@ -25,6 +25,7 @@ declare(strict_types=1);
namespace FireflyIII\Console\Commands\Correction; namespace FireflyIII\Console\Commands\Correction;
use FireflyIII\Console\Commands\ShowsFriendlyMessages; use FireflyIII\Console\Commands\ShowsFriendlyMessages;
use FireflyIII\Enums\TransactionTypeEnum;
use FireflyIII\Models\Transaction; use FireflyIII\Models\Transaction;
use FireflyIII\Models\TransactionJournal; use FireflyIII\Models\TransactionJournal;
use FireflyIII\Models\TransactionType; use FireflyIII\Models\TransactionType;
@ -32,15 +33,12 @@ use FireflyIII\Support\Models\AccountBalanceCalculator;
use Illuminate\Console\Command; use Illuminate\Console\Command;
use Illuminate\Support\Facades\Log; use Illuminate\Support\Facades\Log;
/** class CorrectsUnevenAmount extends Command
* Class FixUnevenAmount
*/
class FixUnevenAmount extends Command
{ {
use ShowsFriendlyMessages; use ShowsFriendlyMessages;
protected $description = 'Fix journals with uneven amounts.'; protected $description = 'Fix journals with uneven amounts.';
protected $signature = 'firefly-iii:fix-uneven-amount'; protected $signature = 'correction:uneven-amounts';
private int $count; private int $count;
/** /**
@ -54,13 +52,112 @@ class FixUnevenAmount extends Command
$this->matchCurrencies(); $this->matchCurrencies();
if (config('firefly.feature_flags.running_balance_column')) { if (config('firefly.feature_flags.running_balance_column')) {
$this->friendlyInfo('Will recalculate transaction running balance columns. This may take a LONG time. Please be patient.'); $this->friendlyInfo('Will recalculate transaction running balance columns. This may take a LONG time. Please be patient.');
AccountBalanceCalculator::recalculateAll(true); AccountBalanceCalculator::recalculateAll(false);
$this->friendlyInfo('Done recalculating transaction running balance columns.'); $this->friendlyInfo('Done recalculating transaction running balance columns.');
} }
return 0; return 0;
} }
private function convertOldStyleTransfers(): void
{
Log::debug('convertOldStyleTransfers()');
// select transactions with a foreign amount and a foreign currency. and it's a transfer. and they are different.
$transactions = Transaction::distinct()
->leftJoin('transaction_journals', 'transaction_journals.id', 'transactions.transaction_journal_id')
->leftJoin('transaction_types', 'transaction_types.id', 'transaction_journals.transaction_type_id')
->where('transaction_types.type', TransactionTypeEnum::TRANSFER->value)
->whereNotNull('foreign_currency_id')
->whereNotNull('foreign_amount')->get(['transactions.transaction_journal_id'])
;
$count = 0;
Log::debug(sprintf('Found %d potential journal(s)', $transactions->count()));
/** @var Transaction $transaction */
foreach ($transactions as $transaction) {
/** @var null|TransactionJournal $journal */
$journal = TransactionJournal::find($transaction->transaction_journal_id);
if (null === $journal) {
Log::debug('Found no journal, continue.');
continue;
}
// needs to be a transfer.
if (TransactionType::TRANSFER !== $journal->transactionType->type) {
Log::debug('Must be a transfer, continue.');
continue;
}
/** @var null|Transaction $destination */
$destination = $journal->transactions()->where('amount', '>', 0)->first();
/** @var null|Transaction $source */
$source = $journal->transactions()->where('amount', '<', 0)->first();
if (null === $destination || null === $source) {
Log::debug('Source or destination transaction is NULL, continue.');
// will be picked up later.
continue;
}
if ($source->transaction_currency_id === $destination->transaction_currency_id) {
Log::debug('Ready to swap data between transactions.');
$destination->foreign_currency_id = $source->transaction_currency_id;
$destination->foreign_amount = app('steam')->positive($source->amount);
$destination->transaction_currency_id = $source->foreign_currency_id;
$destination->amount = app('steam')->positive($source->foreign_amount);
$destination->balance_dirty = true;
$source->balance_dirty = true;
$destination->save();
$source->save();
$this->friendlyWarning(sprintf('Corrected foreign amounts of transfer #%d.', $journal->id));
++$count;
}
}
}
private function fixUnevenAmounts(): void
{
$journals = \DB::table('transactions')
->groupBy('transaction_journal_id')
->whereNull('deleted_at')
->get(['transaction_journal_id', \DB::raw('SUM(amount) AS the_sum')])
;
/** @var \stdClass $entry */
foreach ($journals as $entry) {
$sum = (string) $entry->the_sum;
if (!is_numeric($sum)
|| '' === $sum // @phpstan-ignore-line
|| str_contains($sum, 'e')
|| str_contains($sum, ',')) {
$message = sprintf(
'Journal #%d has an invalid sum ("%s"). No sure what to do.',
$entry->transaction_journal_id,
$entry->the_sum
);
$this->friendlyWarning($message);
app('log')->warning($message);
++$this->count;
continue;
}
$res = -1;
try {
$res = bccomp($sum, '0');
} catch (\ValueError $e) {
$this->friendlyError(sprintf('Could not bccomp("%s", "0").', $sum));
Log::error($e->getMessage());
Log::error($e->getTraceAsString());
}
if (0 !== $res) {
$this->fixJournal($entry->transaction_journal_id);
}
}
}
private function fixJournal(int $param): void private function fixJournal(int $param): void
{ {
// one of the transactions is bad. // one of the transactions is bad.
@ -129,78 +226,6 @@ class FixUnevenAmount extends Command
++$this->count; ++$this->count;
} }
private function fixUnevenAmounts(): void
{
$journals = \DB::table('transactions')
->groupBy('transaction_journal_id')
->whereNull('deleted_at')
->get(['transaction_journal_id', \DB::raw('SUM(amount) AS the_sum')])
;
/** @var \stdClass $entry */
foreach ($journals as $entry) {
$sum = (string) $entry->the_sum;
if (!is_numeric($sum)
|| '' === $sum // @phpstan-ignore-line
|| str_contains($sum, 'e')
|| str_contains($sum, ',')) {
$message = sprintf(
'Journal #%d has an invalid sum ("%s"). No sure what to do.',
$entry->transaction_journal_id,
$entry->the_sum
);
$this->friendlyWarning($message);
app('log')->warning($message);
++$this->count;
continue;
}
$res = -1;
try {
$res = bccomp($sum, '0');
} catch (\ValueError $e) {
$this->friendlyError(sprintf('Could not bccomp("%s", "0").', $sum));
Log::error($e->getMessage());
Log::error($e->getTraceAsString());
}
if (0 !== $res) {
$this->fixJournal($entry->transaction_journal_id);
}
}
if (0 === $this->count) {
$this->friendlyPositive('Database amount integrity is OK');
}
}
private function matchCurrencies(): void
{
$journals = TransactionJournal::leftJoin('transactions', 'transaction_journals.id', 'transactions.transaction_journal_id')
->where('transactions.transaction_currency_id', '!=', \DB::raw('transaction_journals.transaction_currency_id'))
->get(['transaction_journals.*'])
;
$count = 0;
/** @var TransactionJournal $journal */
foreach ($journals as $journal) {
if (!$this->isForeignCurrencyTransfer($journal)) {
Transaction::where('transaction_journal_id', $journal->id)->update(['transaction_currency_id' => $journal->transaction_currency_id]);
++$count;
continue;
}
Log::debug(sprintf('Can skip foreign currency transfer #%d.', $journal->id));
}
if (0 === $count) {
$this->friendlyPositive('Journal currency integrity is OK');
return;
}
$this->friendlyPositive(sprintf('Fixed %d journal(s) with mismatched currencies.', $journals->count()));
}
private function isForeignCurrencyTransfer(TransactionJournal $journal): bool private function isForeignCurrencyTransfer(TransactionJournal $journal): bool
{ {
if (TransactionType::TRANSFER !== $journal->transactionType->type) { if (TransactionType::TRANSFER !== $journal->transactionType->type) {
@ -235,63 +260,29 @@ class FixUnevenAmount extends Command
return false; return false;
} }
private function convertOldStyleTransfers(): void private function matchCurrencies(): void
{ {
Log::debug('convertOldStyleTransfers()'); $journals = TransactionJournal::leftJoin('transactions', 'transaction_journals.id', 'transactions.transaction_journal_id')
// select transactions with a foreign amount and a foreign currency. and it's a transfer. and they are different. ->where('transactions.transaction_currency_id', '!=', \DB::raw('transaction_journals.transaction_currency_id'))
$transactions = Transaction::distinct() ->get(['transaction_journals.*'])
->whereNotNull('foreign_currency_id')
->whereNotNull('foreign_amount')->get(['transactions.transaction_journal_id'])
; ;
$count = 0; $count = 0;
Log::debug(sprintf('Found %d potential journal(s)', $transactions->count())); /** @var TransactionJournal $journal */
foreach ($journals as $journal) {
/** @var Transaction $transaction */ if (!$this->isForeignCurrencyTransfer($journal)) {
foreach ($transactions as $transaction) { Transaction::where('transaction_journal_id', $journal->id)->update(['transaction_currency_id' => $journal->transaction_currency_id]);
/** @var null|TransactionJournal $journal */
$journal = TransactionJournal::find($transaction->transaction_journal_id);
if (null === $journal) {
Log::debug('Found no journal, continue.');
continue;
}
// needs to be a transfer.
if (TransactionType::TRANSFER !== $journal->transactionType->type) {
Log::debug('Must be a transfer, continue.');
continue;
}
/** @var null|Transaction $destination */
$destination = $journal->transactions()->where('amount', '>', 0)->first();
/** @var null|Transaction $source */
$source = $journal->transactions()->where('amount', '<', 0)->first();
if (null === $destination || null === $source) {
Log::debug('Source or destination transaction is NULL, continue.');
// will be picked up later.
continue;
}
if ($source->transaction_currency_id === $destination->transaction_currency_id) {
Log::debug('Ready to swap data between transactions.');
$destination->foreign_currency_id = $source->transaction_currency_id;
$destination->foreign_amount = app('steam')->positive($source->amount);
$destination->transaction_currency_id = $source->foreign_currency_id;
$destination->amount = app('steam')->positive($source->foreign_amount);
$destination->balance_dirty = true;
$source->balance_dirty = true;
$destination->save();
$source->save();
$this->friendlyWarning(sprintf('Corrected foreign amounts of transfer #%d.', $journal->id));
++$count; ++$count;
continue;
} }
Log::debug(sprintf('Can skip foreign currency transfer #%d.', $journal->id));
} }
if (0 === $count) { if (0 === $count) {
$this->friendlyPositive('No "old style" foreign currency transfers.');
return; return;
} }
$this->friendlyPositive(sprintf('Fixed %d journal(s) with mismatched currencies.', $journals->count()));
} }
} }

View File

@ -29,16 +29,13 @@ use FireflyIII\Repositories\User\UserRepositoryInterface;
use FireflyIII\User; use FireflyIII\User;
use Illuminate\Console\Command; use Illuminate\Console\Command;
/** class CreatesAccessTokens extends Command
* Class CreateAccessTokens
*/
class CreateAccessTokens extends Command
{ {
use ShowsFriendlyMessages; use ShowsFriendlyMessages;
protected $description = 'Creates user access tokens which are used for command line access to personal data.'; protected $description = 'Creates user access tokens which are used for command line access to personal data.';
protected $signature = 'firefly-iii:create-access-tokens'; protected $signature = 'correction:access-tokens';
/** /**
* Execute the console command. * Execute the console command.
@ -64,9 +61,6 @@ class CreateAccessTokens extends Command
++$count; ++$count;
} }
} }
if (0 === $count) {
$this->friendlyPositive('Verified access tokens.');
}
return 0; return 0;
} }

View File

@ -1,8 +1,8 @@
<?php <?php
/* /*
* CreateGroupMemberships.php * CreatesGroupMemberships.php
* Copyright (c) 2023 james@firefly-iii.org * Copyright (c) 2024 james@firefly-iii.org.
* *
* This file is part of Firefly III (https://github.com/firefly-iii). * This file is part of Firefly III (https://github.com/firefly-iii).
* *
@ -17,12 +17,12 @@
* GNU Affero General Public License for more details. * GNU Affero General Public License for more details.
* *
* You should have received a copy of the GNU Affero General Public License * 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/>. * along with this program. If not, see https://www.gnu.org/licenses/.
*/ */
declare(strict_types=1); declare(strict_types=1);
namespace FireflyIII\Console\Commands\Integrity; namespace FireflyIII\Console\Commands\Correction;
use FireflyIII\Console\Commands\ShowsFriendlyMessages; use FireflyIII\Console\Commands\ShowsFriendlyMessages;
use FireflyIII\Enums\UserRoleEnum; use FireflyIII\Enums\UserRoleEnum;
@ -33,16 +33,13 @@ use FireflyIII\Models\UserRole;
use FireflyIII\User; use FireflyIII\User;
use Illuminate\Console\Command; use Illuminate\Console\Command;
/** class CreatesGroupMemberships extends Command
* Class CreateGroupMemberships
*/
class CreateGroupMemberships extends Command
{ {
use ShowsFriendlyMessages; use ShowsFriendlyMessages;
public const string CONFIG_NAME = '560_create_group_memberships'; public const string CONFIG_NAME = '560_create_group_memberships';
protected $description = 'Update group memberships'; protected $description = 'Update group memberships';
protected $signature = 'firefly-iii:create-group-memberships'; protected $signature = 'correction:create-group-memberships';
/** /**
* Execute the console command. * Execute the console command.
@ -52,7 +49,6 @@ class CreateGroupMemberships extends Command
public function handle(): int public function handle(): int
{ {
$this->createGroupMemberships(); $this->createGroupMemberships();
$this->friendlyPositive('Validated group memberships');
return 0; return 0;
} }

View File

@ -28,16 +28,13 @@ use FireflyIII\Console\Commands\ShowsFriendlyMessages;
use FireflyIII\Models\LinkType; use FireflyIII\Models\LinkType;
use Illuminate\Console\Command; use Illuminate\Console\Command;
/** class CreatesLinkTypes extends Command
* Class CreateLinkTypes. Created all link types in case a migration hasn't fired.
*/
class CreateLinkTypes extends Command
{ {
use ShowsFriendlyMessages; use ShowsFriendlyMessages;
protected $description = 'Creates all link types.'; protected $description = 'Creates all link types.';
protected $signature = 'firefly-iii:create-link-types'; protected $signature = 'correction:link-types';
/** /**
* Execute the console command. * Execute the console command.
@ -66,9 +63,6 @@ class CreateLinkTypes extends Command
$link->editable = false; $link->editable = false;
$link->save(); $link->save();
} }
if (0 === $count) {
$this->friendlyPositive('All link types are OK');
}
return 0; return 0;
} }

View File

@ -29,15 +29,12 @@ use FireflyIII\Models\TransactionJournal;
use FireflyIII\Models\TransactionType; use FireflyIII\Models\TransactionType;
use Illuminate\Console\Command; use Illuminate\Console\Command;
/** class RemovesBills extends Command
* Class RemoveBills
*/
class RemoveBills extends Command
{ {
use ShowsFriendlyMessages; use ShowsFriendlyMessages;
protected $description = 'Remove bills from transactions that shouldn\'t have one.'; protected $description = 'Remove bills from transactions that shouldn\'t have one.';
protected $signature = 'firefly-iii:remove-bills'; protected $signature = 'correction:bills';
/** /**
* Execute the console command. * Execute the console command.
@ -60,7 +57,6 @@ class RemoveBills extends Command
if ($journals->count() > 0) { if ($journals->count() > 0) {
$this->friendlyInfo('Fixed all transaction journals so they have correct bill information.'); $this->friendlyInfo('Fixed all transaction journals so they have correct bill information.');
} }
$this->friendlyPositive('All bills and journals are OK');
return 0; return 0;
} }

View File

@ -29,15 +29,12 @@ use FireflyIII\Console\Commands\ShowsFriendlyMessages;
use FireflyIII\Models\TransactionGroup; use FireflyIII\Models\TransactionGroup;
use Illuminate\Console\Command; use Illuminate\Console\Command;
/** class RemovesEmptyGroups extends Command
* Class DeleteEmptyGroups
*/
class DeleteEmptyGroups extends Command
{ {
use ShowsFriendlyMessages; use ShowsFriendlyMessages;
protected $description = 'Delete empty transaction groups.'; protected $description = 'Delete empty transaction groups.';
protected $signature = 'firefly-iii:delete-empty-groups'; protected $signature = 'correction:empty-groups';
/** /**
* Execute the console command. * Execute the console command.
@ -61,9 +58,6 @@ class DeleteEmptyGroups extends Command
TransactionGroup::whereNull('deleted_at')->whereIn('id', $chunk)->delete(); TransactionGroup::whereNull('deleted_at')->whereIn('id', $chunk)->delete();
} }
} }
if (0 === $total) {
$this->friendlyInfo('Verified there are no empty groups.');
}
return 0; return 0;
} }

View File

@ -30,16 +30,13 @@ use FireflyIII\Models\TransactionJournal;
use Illuminate\Console\Command; use Illuminate\Console\Command;
use Illuminate\Database\QueryException; use Illuminate\Database\QueryException;
/** class RemovesEmptyJournals extends Command
* Class DeleteEmptyJournals
*/
class DeleteEmptyJournals extends Command
{ {
use ShowsFriendlyMessages; use ShowsFriendlyMessages;
protected $description = 'Delete empty and uneven transaction journals.'; protected $description = 'Delete empty and uneven transaction journals.';
protected $signature = 'firefly-iii:delete-empty-journals'; protected $signature = 'correction:empty-journals';
/** /**
* Execute the console command. * Execute the console command.
@ -82,9 +79,6 @@ class DeleteEmptyJournals extends Command
++$total; ++$total;
} }
} }
if (0 === $total) {
$this->friendlyPositive('No uneven transaction journals.');
}
} }
private function deleteEmptyJournals(): void private function deleteEmptyJournals(): void
@ -107,8 +101,5 @@ class DeleteEmptyJournals extends Command
$this->friendlyInfo(sprintf('Deleted empty transaction journal #%d', $entry->id)); $this->friendlyInfo(sprintf('Deleted empty transaction journal #%d', $entry->id));
++$count; ++$count;
} }
if (0 === $count) {
$this->friendlyPositive('No empty transaction journals.');
}
} }
} }

View File

@ -32,13 +32,13 @@ use Illuminate\Console\Command;
/** /**
* Deletes transactions where the journal has been deleted. * Deletes transactions where the journal has been deleted.
*/ */
class DeleteOrphanedTransactions extends Command class RemovesOrphanedTransactions extends Command
{ {
use ShowsFriendlyMessages; use ShowsFriendlyMessages;
protected $description = 'Deletes orphaned transactions.'; protected $description = 'Deletes orphaned transactions.';
protected $signature = 'firefly-iii:delete-orphaned-transactions'; protected $signature = 'correction:orphaned-transactions';
/** /**
* Execute the console command. * Execute the console command.
@ -63,7 +63,7 @@ class DeleteOrphanedTransactions extends Command
; ;
$count = $set->count(); $count = $set->count();
if (0 === $count) { if (0 === $count) {
$this->friendlyPositive('No orphaned journals.'); // $this->friendlyPositive('No orphaned journals.');
return; return;
} }
@ -116,9 +116,6 @@ class DeleteOrphanedTransactions extends Command
++$count; ++$count;
} }
} }
if (0 === $count) {
$this->friendlyPositive('No orphaned transactions.');
}
} }
private function deleteFromOrphanedAccounts(): void private function deleteFromOrphanedAccounts(): void
@ -147,8 +144,5 @@ class DeleteOrphanedTransactions extends Command
); );
++$count; ++$count;
} }
if (0 === $count) {
$this->friendlyPositive('No orphaned accounts.');
}
} }
} }

View File

@ -29,16 +29,13 @@ use FireflyIII\Models\Transaction;
use FireflyIII\Models\TransactionJournal; use FireflyIII\Models\TransactionJournal;
use Illuminate\Console\Command; use Illuminate\Console\Command;
/** class RemovesZeroAmount extends Command
* Class DeleteZeroAmount
*/
class DeleteZeroAmount extends Command
{ {
use ShowsFriendlyMessages; use ShowsFriendlyMessages;
protected $description = 'Delete transactions with zero amount.'; protected $description = 'Delete transactions with zero amount.';
protected $signature = 'firefly-iii:delete-zero-amount'; protected $signature = 'correction:zero-amounts';
/** /**
* Execute the console command. * Execute the console command.
@ -56,9 +53,6 @@ class DeleteZeroAmount extends Command
Transaction::where('transaction_journal_id', $journal->id)->delete(); Transaction::where('transaction_journal_id', $journal->id)->delete();
} }
if (0 === $journals->count()) {
$this->friendlyPositive('No zero-amount transaction journals.');
}
return 0; return 0;
} }

View File

@ -1,8 +1,8 @@
<?php <?php
/** /*
* RestoreOAuthKeys.php * RestoresOAuthKeys.php
* Copyright (c) 2020 james@firefly-iii.org * Copyright (c) 2024 james@firefly-iii.org.
* *
* This file is part of Firefly III (https://github.com/firefly-iii). * This file is part of Firefly III (https://github.com/firefly-iii).
* *
@ -17,26 +17,23 @@
* GNU Affero General Public License for more details. * GNU Affero General Public License for more details.
* *
* You should have received a copy of the GNU Affero General Public License * 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/>. * along with this program. If not, see https://www.gnu.org/licenses/.
*/ */
declare(strict_types=1); declare(strict_types=1);
namespace FireflyIII\Console\Commands\Integrity; namespace FireflyIII\Console\Commands\Correction;
use FireflyIII\Console\Commands\ShowsFriendlyMessages; use FireflyIII\Console\Commands\ShowsFriendlyMessages;
use FireflyIII\Support\System\OAuthKeys; use FireflyIII\Support\System\OAuthKeys;
use Illuminate\Console\Command; use Illuminate\Console\Command;
/** class RestoresOAuthKeys extends Command
* Class RestoreOAuthKeys
*/
class RestoreOAuthKeys extends Command
{ {
use ShowsFriendlyMessages; use ShowsFriendlyMessages;
protected $description = 'Will restore the OAuth keys generated for the system.'; protected $description = 'Will restore the OAuth keys generated for the system.';
protected $signature = 'firefly-iii:restore-oauth-keys'; protected $signature = 'correction:restore-oauth-keys';
/** /**
* Execute the console command. * Execute the console command.
@ -76,7 +73,6 @@ class RestoreOAuthKeys extends Command
return; return;
} }
$this->friendlyPositive('OAuth keys are OK');
} }
private function keysInDatabase(): bool private function keysInDatabase(): bool

View File

@ -28,13 +28,10 @@ use FireflyIII\Models\Account;
use FireflyIII\Services\Internal\Support\CreditRecalculateService; use FireflyIII\Services\Internal\Support\CreditRecalculateService;
use Illuminate\Console\Command; use Illuminate\Console\Command;
/** class TriggersCreditCalculation extends Command
* Class CorrectionSkeleton
*/
class TriggerCreditCalculation extends Command
{ {
protected $description = 'Triggers the credit recalculation service for liabilities.'; protected $description = 'Triggers the credit recalculation service for liabilities.';
protected $signature = 'firefly-iii:trigger-credit-recalculation'; protected $signature = 'correction:recalculates-liabilities';
/** /**
* Execute the console command. * Execute the console command.

View File

@ -36,10 +36,7 @@ use FireflyIII\Support\Export\ExportDataGenerator;
use Illuminate\Console\Command; use Illuminate\Console\Command;
use Illuminate\Support\Collection; use Illuminate\Support\Collection;
/** class ExportsData extends Command
* Class ExportData
*/
class ExportData extends Command
{ {
use ShowsFriendlyMessages; use ShowsFriendlyMessages;
use VerifiesAccessToken; use VerifiesAccessToken;

View File

@ -4,10 +4,6 @@ namespace FireflyIII\Console\Commands\Integrity;
use Illuminate\Console\Command; use Illuminate\Console\Command;
/**
* Class ReportSkeleton
* TODO DONT FORGET TO ADD THIS TO THE DOCKER BUILD
*/
class ReportSkeleton extends Command class ReportSkeleton extends Command
{ {

View File

@ -31,16 +31,13 @@ use FireflyIII\Models\Category;
use FireflyIII\Models\Tag; use FireflyIII\Models\Tag;
use Illuminate\Console\Command; use Illuminate\Console\Command;
/** class ReportsEmptyObjects extends Command
* Class ReportEmptyObjects
*/
class ReportEmptyObjects extends Command
{ {
use ShowsFriendlyMessages; use ShowsFriendlyMessages;
protected $description = 'Reports on empty database objects.'; protected $description = 'Reports on empty database objects.';
protected $signature = 'firefly-iii:report-empty-objects'; protected $signature = 'integrity:empty-objects';
/** /**
* Execute the console command. * Execute the console command.

View File

@ -27,10 +27,7 @@ namespace FireflyIII\Console\Commands\Integrity;
use FireflyIII\Console\Commands\ShowsFriendlyMessages; use FireflyIII\Console\Commands\ShowsFriendlyMessages;
use Illuminate\Console\Command; use Illuminate\Console\Command;
/** class ReportsIntegrity extends Command
* Class ReportIntegrity
*/
class ReportIntegrity extends Command
{ {
use ShowsFriendlyMessages; use ShowsFriendlyMessages;
@ -48,11 +45,8 @@ class ReportIntegrity extends Command
return 1; return 1;
} }
$commands = [ $commands = [
'firefly-iii:add-timezones-to-dates', 'integrity:empty-objects',
'firefly-iii:create-group-memberships', 'integrity:total-sums',
'firefly-iii:report-empty-objects',
'firefly-iii:report-sum',
'firefly-iii:upgrade-group-information',
]; ];
foreach ($commands as $command) { foreach ($commands as $command) {
$this->friendlyLine(sprintf('Now executing %s', $command)); $this->friendlyLine(sprintf('Now executing %s', $command));

View File

@ -29,15 +29,12 @@ use FireflyIII\Repositories\User\UserRepositoryInterface;
use FireflyIII\User; use FireflyIII\User;
use Illuminate\Console\Command; use Illuminate\Console\Command;
/** class ReportsSums extends Command
* Class ReportSkeleton
*/
class ReportSum extends Command
{ {
use ShowsFriendlyMessages; use ShowsFriendlyMessages;
protected $description = 'Report on the total sum of transactions. Must be 0.'; protected $description = 'Report on the total sum of transactions. Must be 0.';
protected $signature = 'firefly-iii:report-sum'; protected $signature = 'integrity:total-sums';
/** /**
* Execute the console command. * Execute the console command.
@ -59,18 +56,22 @@ class ReportSum extends Command
/** @var User $user */ /** @var User $user */
foreach ($userRepository->all() as $user) { foreach ($userRepository->all() as $user) {
$sum = (string)$user->transactions()->selectRaw('SUM(amount) + SUM(foreign_amount) as total')->value('total'); $sum = (string) $user->transactions()->selectRaw('SUM(amount) as total')->value('total');
if (!is_numeric($sum)) { $foreign = (string) $user->transactions()->selectRaw('SUM(foreign_amount) as total')->value('total');
$message = sprintf('Error: Transactions for user #%d (%s) have an invalid sum ("%s").', $user->id, $user->email, $sum); $sum = '' === $sum ? '0' : $sum;
$foreign = '' === $foreign ? '0' : $foreign;
$total = bcadd($sum, $foreign);
if (!is_numeric($total)) {
$message = sprintf('Error: Transactions for user #%d (%s) have an invalid sum ("%s").', $user->id, $user->email, $total);
$this->friendlyError($message); $this->friendlyError($message);
continue; continue;
} }
if (0 !== bccomp($sum, '0')) { if (0 !== bccomp($total, '0')) {
$message = sprintf('Error: Transactions for user #%d (%s) are off by %s!', $user->id, $user->email, $sum); $message = sprintf('Error: Transactions for user #%d (%s) are off by %s!', $user->id, $user->email, $total);
$this->friendlyError($message); $this->friendlyError($message);
} }
if (0 === bccomp($sum, '0')) { if (0 === bccomp($total, '0')) {
$this->friendlyPositive(sprintf('Amount integrity OK for user #%d', $user->id)); $this->friendlyPositive(sprintf('Amount integrity OK for user #%d', $user->id));
} }
} }

View File

@ -28,7 +28,7 @@ use Illuminate\Console\Command;
use Illuminate\Support\Facades\Artisan; use Illuminate\Support\Facades\Artisan;
use Symfony\Component\Console\Command\Command as CommandAlias; use Symfony\Component\Console\Command\Command as CommandAlias;
class LaravelPassportKeys extends Command class CallsLaravelPassportKeys extends Command
{ {
use ShowsFriendlyMessages; use ShowsFriendlyMessages;

View File

@ -28,10 +28,7 @@ use FireflyIII\Console\Commands\ShowsFriendlyMessages;
use Illuminate\Console\Command; use Illuminate\Console\Command;
use PDO; use PDO;
/** class CreatesDatabase extends Command
* Class CreateDatabase
*/
class CreateDatabase extends Command
{ {
use ShowsFriendlyMessages; use ShowsFriendlyMessages;

View File

@ -29,16 +29,13 @@ use FireflyIII\Repositories\User\UserRepositoryInterface;
use Illuminate\Console\Command; use Illuminate\Console\Command;
use Illuminate\Support\Facades\Hash; use Illuminate\Support\Facades\Hash;
/** class CreatesFirstUser extends Command
* Class CreateFirstUser
*/
class CreateFirstUser extends Command
{ {
use ShowsFriendlyMessages; use ShowsFriendlyMessages;
protected $description = 'Creates a new user and gives admin rights. Outputs the password on the command line. Strictly for testing.'; protected $description = 'Creates a new user and gives admin rights. Outputs the password on the command line. Strictly for testing.';
protected $signature = 'firefly-iii:create-first-user {email}'; protected $signature = 'system:create-first-user {email}';
private UserRepositoryInterface $repository; private UserRepositoryInterface $repository;
/** /**

View File

@ -44,13 +44,11 @@ use Illuminate\Support\Collection;
use Illuminate\Support\Facades\DB; use Illuminate\Support\Facades\DB;
/** /**
* Class ForceDecimalSize
*
* This command was inspired by https://github.com/elliot-gh. It will check all amount fields * This command was inspired by https://github.com/elliot-gh. It will check all amount fields
* and their values and correct them to the correct number of decimal places. This fixes issues where * and their values and correct them to the correct number of decimal places. This fixes issues where
* Firefly III would store 0.01 as 0.01000000000000000020816681711721685132943093776702880859375. * Firefly III would store 0.01 as 0.01000000000000000020816681711721685132943093776702880859375.
*/ */
class ForceDecimalSize extends Command class ForcesDecimalSize extends Command
{ {
use ShowsFriendlyMessages; use ShowsFriendlyMessages;
@ -83,8 +81,8 @@ class ForceDecimalSize extends Command
'currency_exchange_rates' => ['rate', 'user_rate'], 'currency_exchange_rates' => ['rate', 'user_rate'],
'limit_repetitions' => ['amount'], 'limit_repetitions' => ['amount'],
'piggy_bank_events' => ['amount'], 'piggy_bank_events' => ['amount'],
'piggy_bank_repetitions' => ['currentamount'], 'piggy_bank_repetitions' => ['current_amount'],
'piggy_banks' => ['targetamount'], 'piggy_banks' => ['target_amount'],
'recurrences_transactions' => ['amount', 'foreign_amount'], 'recurrences_transactions' => ['amount', 'foreign_amount'],
'transactions' => ['amount', 'foreign_amount'], 'transactions' => ['amount', 'foreign_amount'],
]; ];

View File

@ -33,10 +33,7 @@ use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log; use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Schema; use Illuminate\Support\Facades\Schema;
/** class ForcesMigrations extends Command
* Class ForceMigration
*/
class ForceMigration extends Command
{ {
use ShowsFriendlyMessages; use ShowsFriendlyMessages;
use VerifiesAccessToken; use VerifiesAccessToken;

View File

@ -27,16 +27,13 @@ namespace FireflyIII\Console\Commands\System;
use FireflyIII\Support\System\GeneratesInstallationId; use FireflyIII\Support\System\GeneratesInstallationId;
use Illuminate\Console\Command; use Illuminate\Console\Command;
/** class OutputsInstructions extends Command
* Class UpgradeFireflyInstructions.
*/
class UpgradeFireflyInstructions extends Command
{ {
use GeneratesInstallationId; use GeneratesInstallationId;
protected $description = 'Instructions in case of upgrade trouble.'; protected $description = 'Instructions in case of upgrade trouble.';
protected $signature = 'firefly:instructions {task}'; protected $signature = 'firefly-iii:instructions {task=install}';
/** /**
* Execute the console command. * Execute the console command.
@ -79,7 +76,7 @@ class UpgradeFireflyInstructions extends Command
} }
$prefix = 'v'; $prefix = 'v';
if (str_starts_with($version, 'develop')) { if (str_starts_with($version, 'develop') || str_starts_with($version, 'branch')) {
$prefix = ''; $prefix = '';
} }
@ -94,6 +91,8 @@ class UpgradeFireflyInstructions extends Command
$this->boxedInfo('There are no extra upgrade instructions.'); $this->boxedInfo('There are no extra upgrade instructions.');
$this->boxed('Firefly III should be ready for use.'); $this->boxed('Firefly III should be ready for use.');
$this->boxed(''); $this->boxed('');
$this->donationText();
$this->boxed('');
$this->showLine(); $this->showLine();
return; return;
@ -102,6 +101,8 @@ class UpgradeFireflyInstructions extends Command
$this->boxed(sprintf('Thank you for updating to Firefly III, %s%s!', $prefix, $version)); $this->boxed(sprintf('Thank you for updating to Firefly III, %s%s!', $prefix, $version));
$this->boxedInfo($text); $this->boxedInfo($text);
$this->boxed(''); $this->boxed('');
$this->donationText();
$this->boxed('');
$this->showLine(); $this->showLine();
} }
@ -213,6 +214,8 @@ class UpgradeFireflyInstructions extends Command
$this->boxedInfo('There are no extra installation instructions.'); $this->boxedInfo('There are no extra installation instructions.');
$this->boxed('Firefly III should be ready for use.'); $this->boxed('Firefly III should be ready for use.');
$this->boxed(''); $this->boxed('');
$this->donationText();
$this->boxed('');
$this->showLine(); $this->showLine();
return; return;
@ -221,6 +224,15 @@ class UpgradeFireflyInstructions extends Command
$this->boxed(sprintf('Thank you for installing Firefly III, %s%s!', $prefix, $version)); $this->boxed(sprintf('Thank you for installing Firefly III, %s%s!', $prefix, $version));
$this->boxedInfo($text); $this->boxedInfo($text);
$this->boxed(''); $this->boxed('');
$this->donationText();
$this->boxed('');
$this->showLine(); $this->showLine();
} }
private function donationText(): void
{
$this->boxed('Did you know you can support the development of Firefly III?');
$this->boxed('You can donate in many ways, like GitHub Sponsors or Patreon.');
$this->boxed('For more information, please visit https://bit.ly/donate-to-Firefly-III');
}
} }

View File

@ -26,10 +26,7 @@ namespace FireflyIII\Console\Commands\System;
use Illuminate\Console\Command; use Illuminate\Console\Command;
/** class OutputsVersion extends Command
* Class OutputVersion
*/
class OutputVersion extends Command
{ {
protected $description = 'Outputs the Firefly III version'; protected $description = 'Outputs the Firefly III version';

View File

@ -29,10 +29,7 @@ use FireflyIII\Models\Attachment;
use Illuminate\Console\Command; use Illuminate\Console\Command;
use Illuminate\Contracts\Encryption\DecryptException; use Illuminate\Contracts\Encryption\DecryptException;
/** class ScansAttachments extends Command
* Class ScanAttachments.
*/
class ScanAttachments extends Command
{ {
use ShowsFriendlyMessages; use ShowsFriendlyMessages;

View File

@ -27,10 +27,7 @@ namespace FireflyIII\Console\Commands\System;
use FireflyIII\Console\Commands\ShowsFriendlyMessages; use FireflyIII\Console\Commands\ShowsFriendlyMessages;
use Illuminate\Console\Command; use Illuminate\Console\Command;
/** class SetsLatestVersion extends Command
* Class SetLatestVersion
*/
class SetLatestVersion extends Command
{ {
use ShowsFriendlyMessages; use ShowsFriendlyMessages;

View File

@ -29,9 +29,6 @@ use Illuminate\Console\Command;
use Illuminate\Database\QueryException; use Illuminate\Database\QueryException;
use League\Flysystem\FilesystemException; use League\Flysystem\FilesystemException;
/**
* Class VerifySecurityAlerts
*/
class VerifySecurityAlerts extends Command class VerifySecurityAlerts extends Command
{ {
use ShowsFriendlyMessages; use ShowsFriendlyMessages;

View File

@ -40,9 +40,6 @@ use Illuminate\Console\Command;
use Illuminate\Support\Collection; use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Log; use Illuminate\Support\Facades\Log;
/**
* Class ApplyRules
*/
class ApplyRules extends Command class ApplyRules extends Command
{ {
use ShowsFriendlyMessages; use ShowsFriendlyMessages;
@ -128,7 +125,7 @@ class ApplyRules extends Command
$ruleEngine->addOperator(['type' => 'account_id', 'value' => $list]); $ruleEngine->addOperator(['type' => 'account_id', 'value' => $list]);
// add the date as a filter: // add the date as a filter:
$ruleEngine->addOperator(['type' => 'date_after', 'value' => $this->startDate->format('Y-m-d')]); $ruleEngine->addOperator(['type' => 'date_after', 'value' => $this->start_date->format('Y-m-d')]);
$ruleEngine->addOperator(['type' => 'date_before', 'value' => $this->endDate->format('Y-m-d')]); $ruleEngine->addOperator(['type' => 'date_before', 'value' => $this->endDate->format('Y-m-d')]);
// start running rules. // start running rules.
@ -296,7 +293,7 @@ class ApplyRules extends Command
[$inputEnd, $inputStart] = [$inputStart, $inputEnd]; [$inputEnd, $inputStart] = [$inputStart, $inputEnd];
} }
$this->startDate = $inputStart; $this->start_date = $inputStart;
$this->endDate = $inputEnd; $this->endDate = $inputEnd;
} }

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