Clean up API and display of transactions.

This commit is contained in:
James Cole
2025-08-08 20:18:04 +02:00
parent 73512b0365
commit deca4fed56
14 changed files with 435 additions and 444 deletions

View File

@@ -0,0 +1,97 @@
<?php
declare(strict_types=1);
namespace FireflyIII\Api\V1\Controllers\Chart;
use FireflyIII\Api\V1\Controllers\Controller;
use FireflyIII\Api\V1\Requests\Chart\ChartRequest;
use FireflyIII\Enums\TransactionTypeEnum;
use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Helpers\Collector\GroupCollectorInterface;
use FireflyIII\Models\TransactionCurrency;
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
use FireflyIII\Support\Chart\ChartData;
use FireflyIII\Support\Facades\Amount;
use FireflyIII\Support\Http\Api\AccountBalanceGrouped;
use FireflyIII\Support\Http\Api\CleansChartData;
use FireflyIII\Support\Http\Api\CollectsAccountsFromFilter;
use Illuminate\Http\JsonResponse;
/**
* Class BalanceController
*/
class BalanceController extends Controller
{
use CleansChartData;
use CollectsAccountsFromFilter;
private ChartData $chartData;
private GroupCollectorInterface $collector;
private AccountRepositoryInterface $repository;
// private TransactionCurrency $default;
public function __construct()
{
parent::__construct();
$this->middleware(
function ($request, $next) {
$this->repository = app(AccountRepositoryInterface::class);
$this->collector = app(GroupCollectorInterface::class);
$userGroup = $this->validateUserGroup($request);
$this->repository->setUserGroup($userGroup);
$this->collector->setUserGroup($userGroup);
$this->chartData = new ChartData();
// $this->default = app('amount')->getPrimaryCurrency();
return $next($request);
}
);
}
/**
* The code is practically a duplicate of ReportController::operations.
*
* Currency is up to the account/transactions in question, but conversion to the default
* currency is possible.
*
* If the transaction being processed is already in native currency OR if the
* foreign amount is in the native currency, the amount will not be converted.
*
* @throws FireflyException
*/
public function balance(ChartRequest $request): JsonResponse
{
$queryParameters = $request->getParameters();
$accounts = $this->getAccountList($queryParameters);
// prepare for currency conversion and data collection:
/** @var TransactionCurrency $primary */
$primary = Amount::getPrimaryCurrency();
// get journals for entire period:
$this->collector->setRange($queryParameters['start'], $queryParameters['end'])
->withAccountInformation()
->setXorAccounts($accounts)
->setTypes([TransactionTypeEnum::WITHDRAWAL->value, TransactionTypeEnum::DEPOSIT->value, TransactionTypeEnum::RECONCILIATION->value, TransactionTypeEnum::TRANSFER->value]);
$journals = $this->collector->getExtractedJournals();
$object = new AccountBalanceGrouped();
$object->setPreferredRange($queryParameters['period']);
$object->setPrimary($primary);
$object->setAccounts($accounts);
$object->setJournals($journals);
$object->setStart($queryParameters['start']);
$object->setEnd($queryParameters['end']);
$object->groupByCurrencyAndPeriod();
$data = $object->convertToChartData();
foreach ($data as $entry) {
$this->chartData->add($entry);
}
return response()->json($this->chartData->render());
}
}

View File

@@ -40,6 +40,7 @@ use FireflyIII\Models\TransactionCurrency;
use FireflyIII\Models\UserGroup; use FireflyIII\Models\UserGroup;
use FireflyIII\Repositories\PiggyBank\PiggyBankRepositoryInterface; use FireflyIII\Repositories\PiggyBank\PiggyBankRepositoryInterface;
use FireflyIII\Repositories\UserGroup\UserGroupRepositoryInterface; use FireflyIII\Repositories\UserGroup\UserGroupRepositoryInterface;
use FireflyIII\Support\Facades\Amount;
use FireflyIII\Support\Facades\Preferences; use FireflyIII\Support\Facades\Preferences;
use FireflyIII\Support\Http\Api\ExchangeRateConverter; use FireflyIII\Support\Http\Api\ExchangeRateConverter;
use Illuminate\Console\Command; use Illuminate\Console\Command;
@@ -72,6 +73,8 @@ class CorrectsPrimaryCurrencyAmounts extends Command
/** @var UserGroupRepositoryInterface $repository */ /** @var UserGroupRepositoryInterface $repository */
$repository = app(UserGroupRepositoryInterface::class); $repository = app(UserGroupRepositoryInterface::class);
Preferences::mark();
/** @var UserGroup $userGroup */ /** @var UserGroup $userGroup */
foreach ($repository->getAll() as $userGroup) { foreach ($repository->getAll() as $userGroup) {
$this->recalculateForGroup($userGroup); $this->recalculateForGroup($userGroup);
@@ -87,8 +90,7 @@ class CorrectsPrimaryCurrencyAmounts extends Command
$this->recalculateAccounts($userGroup); $this->recalculateAccounts($userGroup);
// do a check with the group's currency so we can skip some stuff. // do a check with the group's currency so we can skip some stuff.
Preferences::mark(); $currency = Amount::getPrimaryCurrencyByUserGroup($userGroup);
$currency = app('amount')->getPrimaryCurrencyByUserGroup($userGroup);
$this->recalculatePiggyBanks($userGroup, $currency); $this->recalculatePiggyBanks($userGroup, $currency);
$this->recalculateBudgets($userGroup, $currency); $this->recalculateBudgets($userGroup, $currency);

View File

@@ -35,6 +35,6 @@ class UserGroupChangedPrimaryCurrency extends Event
public function __construct(public UserGroup $userGroup) public function __construct(public UserGroup $userGroup)
{ {
Log::debug('User group changed default currency.'); Log::debug('User group changed primary currency.');
} }
} }

View File

@@ -73,6 +73,7 @@ class PreferencesEventHandler
$repository = app(PiggyBankRepositoryInterface::class); $repository = app(PiggyBankRepositoryInterface::class);
$repository->setUserGroup($userGroup); $repository->setUserGroup($userGroup);
$piggyBanks = $repository->getPiggyBanks(); $piggyBanks = $repository->getPiggyBanks();
Log::debug(sprintf('Resetting %d piggy bank(s).', $piggyBanks->count()));
/** @var PiggyBank $piggyBank */ /** @var PiggyBank $piggyBank */
foreach ($piggyBanks as $piggyBank) { foreach ($piggyBanks as $piggyBank) {
@@ -104,6 +105,8 @@ class PreferencesEventHandler
$repository->setUserGroup($userGroup); $repository->setUserGroup($userGroup);
$set = $repository->getBudgets(); $set = $repository->getBudgets();
Log::debug(sprintf('Resetting %d budget(s).', $set->count()));
/** @var Budget $budget */ /** @var Budget $budget */
foreach ($set as $budget) { foreach ($set as $budget) {
foreach ($budget->autoBudgets as $autoBudget) { foreach ($budget->autoBudgets as $autoBudget) {

View File

@@ -35,6 +35,7 @@ use FireflyIII\Models\TransactionCurrency;
use FireflyIII\Repositories\Account\AccountRepositoryInterface; use FireflyIII\Repositories\Account\AccountRepositoryInterface;
use FireflyIII\Repositories\Currency\CurrencyRepositoryInterface; use FireflyIII\Repositories\Currency\CurrencyRepositoryInterface;
use FireflyIII\Support\CacheProperties; use FireflyIII\Support\CacheProperties;
use FireflyIII\Support\Facades\Preferences;
use FireflyIII\Support\Facades\Steam; use FireflyIII\Support\Facades\Steam;
use FireflyIII\Support\Http\Controllers\AugumentData; use FireflyIII\Support\Http\Controllers\AugumentData;
use FireflyIII\Support\Http\Controllers\ChartGeneration; use FireflyIII\Support\Http\Controllers\ChartGeneration;
@@ -42,7 +43,6 @@ use FireflyIII\Support\Http\Controllers\DateCalculation;
use Illuminate\Http\JsonResponse; use Illuminate\Http\JsonResponse;
use Illuminate\Support\Collection; use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Log; use Illuminate\Support\Facades\Log;
use function Safe\json_encode; use function Safe\json_encode;
/** /**
@@ -78,6 +78,7 @@ class AccountController extends Controller
/** /**
* Shows the balances for all the user's expense accounts (on the front page). * Shows the balances for all the user's expense accounts (on the front page).
* 2025-08-06 validated for multi (primary) currency
* *
* This chart is (multi) currency aware. * This chart is (multi) currency aware.
*/ */
@@ -86,14 +87,14 @@ class AccountController extends Controller
Log::debug('ExpenseAccounts'); Log::debug('ExpenseAccounts');
/** @var Carbon $start */ /** @var Carbon $start */
$start = clone session('start', today(config('app.timezone'))->startOfMonth()); $start = clone session('start', today(config('app.timezone'))->startOfMonth());
/** @var Carbon $end */ /** @var Carbon $end */
$end = clone session('end', today(config('app.timezone'))->endOfMonth()); $end = clone session('end', today(config('app.timezone'))->endOfMonth());
$start->startOfDay(); $start->startOfDay();
$end->endOfDay(); $end->endOfDay();
$cache = new CacheProperties(); $cache = new CacheProperties();
$cache->addProperty($start); $cache->addProperty($start);
$cache->addProperty($end); $cache->addProperty($end);
$cache->addProperty($this->convertToPrimary); $cache->addProperty($this->convertToPrimary);
@@ -103,19 +104,19 @@ class AccountController extends Controller
} }
// prep some vars: // prep some vars:
$currencies = []; $currencies = [];
$chartData = []; $chartData = [];
$tempData = []; $tempData = [];
// grab all accounts and names // grab all accounts and names
$accounts = $this->accountRepository->getAccountsByType([AccountTypeEnum::EXPENSE->value]); $accounts = $this->accountRepository->getAccountsByType([AccountTypeEnum::EXPENSE->value]);
$accountNames = $this->extractNames($accounts); $accountNames = $this->extractNames($accounts);
// grab all balances // grab all balances
Log::debug(sprintf('expenseAccounts: finalAccountsBalance("%s")', $start->format('Y-m-d H:i:s'))); Log::debug(sprintf('expenseAccounts: finalAccountsBalance("%s")', $start->format('Y-m-d H:i:s')));
Log::debug(sprintf('expenseAccounts: finalAccountsBalance("%s")', $end->format('Y-m-d H:i:s'))); Log::debug(sprintf('expenseAccounts: finalAccountsBalance("%s")', $end->format('Y-m-d H:i:s')));
$startBalances = Steam::finalAccountsBalance($accounts, $start); $startBalances = Steam::finalAccountsBalance($accounts, $start, $this->primaryCurrency, $this->convertToPrimary);
$endBalances = Steam::finalAccountsBalance($accounts, $end); $endBalances = Steam::finalAccountsBalance($accounts, $end, $this->primaryCurrency, $this->convertToPrimary);
// loop the accounts, then check for balance and currency info. // loop the accounts, then check for balance and currency info.
foreach ($accounts as $account) { foreach ($accounts as $account) {
@@ -143,21 +144,21 @@ class AccountController extends Controller
continue; continue;
} }
// Log::debug(sprintf('Will process expense array "%s" with amount %s', $key, $endBalance)); // Log::debug(sprintf('Will process expense array "%s" with amount %s', $key, $endBalance));
$searchCode = $this->convertToPrimary ? $this->primaryCurrency->code : $key; $searchCode = $this->convertToPrimary ? $this->primaryCurrency->code : $key;
$searchCode = 'balance' === $searchCode || 'pc_balance' === $searchCode ? $this->primaryCurrency->code : $searchCode; $searchCode = 'balance' === $searchCode || 'pc_balance' === $searchCode ? $this->primaryCurrency->code : $searchCode;
// Log::debug(sprintf('Search code is %s', $searchCode)); // Log::debug(sprintf('Search code is %s', $searchCode));
// see if there is an accompanying start amount. // see if there is an accompanying start amount.
// grab the difference and find the currency. // grab the difference and find the currency.
$startBalance = ($startBalances[$account->id][$key] ?? '0'); $startBalance = ($startBalances[$account->id][$key] ?? '0');
// Log::debug(sprintf('Start balance is %s', $startBalance)); // Log::debug(sprintf('Start balance is %s', $startBalance));
$diff = bcsub($endBalance, $startBalance); $diff = bcsub($endBalance, $startBalance);
$currencies[$searchCode] ??= $this->currencyRepository->findByCode($searchCode); $currencies[$searchCode] ??= $this->currencyRepository->findByCode($searchCode);
if (0 !== bccomp($diff, '0')) { if (0 !== bccomp($diff, '0')) {
// store the values in a temporary array. // store the values in a temporary array.
$tempData[] = [ $tempData[] = [
'name' => $accountNames[$account->id], 'name' => $accountNames[$account->id],
'difference' => $diff, 'difference' => $diff,
'diff_float' => (float) $diff, // intentional float 'diff_float' => (float)$diff, // intentional float
'currency_id' => $currencies[$searchCode]->id, 'currency_id' => $currencies[$searchCode]->id,
]; ];
} }
@@ -168,26 +169,26 @@ class AccountController extends Controller
foreach ($currencies as $currency) { foreach ($currencies as $currency) {
$newCurrencies[$currency->id] = $currency; $newCurrencies[$currency->id] = $currency;
} }
$currencies = $newCurrencies; $currencies = $newCurrencies;
// sort temp array by amount. // sort temp array by amount.
$amounts = array_column($tempData, 'diff_float'); $amounts = array_column($tempData, 'diff_float');
array_multisort($amounts, SORT_DESC, $tempData); array_multisort($amounts, SORT_DESC, $tempData);
// loop all found currencies and build the data array for the chart. // loop all found currencies and build the data array for the chart.
/** /**
* @var int $currencyId * @var int $currencyId
* @var TransactionCurrency $currency * @var TransactionCurrency $currency
*/ */
foreach ($currencies as $currencyId => $currency) { foreach ($currencies as $currencyId => $currency) {
$dataSet $dataSet
= [ = [
'label' => (string) trans('firefly.spent'), 'label' => (string)trans('firefly.spent'),
'type' => 'bar', 'type' => 'bar',
'currency_symbol' => $currency->symbol, 'currency_symbol' => $currency->symbol,
'currency_code' => $currency->code, 'currency_code' => $currency->code,
'entries' => $this->expandNames($tempData), 'entries' => $this->expandNames($tempData),
]; ];
$chartData[$currencyId] = $dataSet; $chartData[$currencyId] = $dataSet;
} }
@@ -195,10 +196,10 @@ class AccountController extends Controller
foreach ($tempData as $entry) { foreach ($tempData as $entry) {
$currencyId = $entry['currency_id']; $currencyId = $entry['currency_id'];
$name = $entry['name']; $name = $entry['name'];
$chartData[$currencyId]['entries'][$name] = (float) $entry['difference']; $chartData[$currencyId]['entries'][$name] = (float)$entry['difference'];
} }
$data = $this->generator->multiSet($chartData); $data = $this->generator->multiSet($chartData);
$cache->store($data); $cache->store($data);
return response()->json($data); return response()->json($data);
@@ -206,6 +207,7 @@ class AccountController extends Controller
/** /**
* Expenses per budget for all time, as shown on account overview. * Expenses per budget for all time, as shown on account overview.
*
*/ */
public function expenseBudgetAll(AccountRepositoryInterface $repository, Account $account): JsonResponse public function expenseBudgetAll(AccountRepositoryInterface $repository, Account $account): JsonResponse
{ {
@@ -220,9 +222,10 @@ class AccountController extends Controller
*/ */
public function expenseBudget(Account $account, Carbon $start, Carbon $end): JsonResponse public function expenseBudget(Account $account, Carbon $start, Carbon $end): JsonResponse
{ {
$cache = new CacheProperties(); $cache = new CacheProperties();
$cache->addProperty($account->id); $cache->addProperty($account->id);
$cache->addProperty($start); $cache->addProperty($start);
$cache->addProperty($this->convertToPrimary);
$cache->addProperty($end); $cache->addProperty($end);
$cache->addProperty('chart.account.expense-budget'); $cache->addProperty('chart.account.expense-budget');
if ($cache->has()) { if ($cache->has()) {
@@ -231,7 +234,9 @@ class AccountController extends Controller
/** @var GroupCollectorInterface $collector */ /** @var GroupCollectorInterface $collector */
$collector = app(GroupCollectorInterface::class); $collector = app(GroupCollectorInterface::class);
$collector->setAccounts(new Collection([$account]))->setRange($start, $end)->withBudgetInformation()->setTypes([TransactionTypeEnum::WITHDRAWAL->value]); $collector->setAccounts(new Collection([$account]))
->setRange($start, $end)
->withBudgetInformation()->setTypes([TransactionTypeEnum::WITHDRAWAL->value]);
$journals = $collector->getExtractedJournals(); $journals = $collector->getExtractedJournals();
$chartData = []; $chartData = [];
$result = []; $result = [];
@@ -239,31 +244,49 @@ class AccountController extends Controller
/** @var array $journal */ /** @var array $journal */
foreach ($journals as $journal) { foreach ($journals as $journal) {
$budgetId = (int) $journal['budget_id']; $budgetId = (int)$journal['budget_id'];
$key = sprintf('%d-%d', $budgetId, $journal['currency_id']); $key = sprintf('%d-%d', $budgetId, $journal['currency_id']);
$budgetIds[] = $budgetId; $budgetIds[] = $budgetId;
// currency info:
$currencyId = (int)$journal['currency_id'];
$currencyName = $journal['currency_name'];
$currencySymbol = $journal['currency_symbol'];
$currencyCode = $journal['currency_code'];
$currencyDecimalPlaces = $journal['currency_decimal_places'];
$field = 'amount';
if ($this->convertToPrimary && $this->primaryCurrency->id !== $currencyId) {
$field = 'pc_amount';
$currencyName = $this->primaryCurrency->name;
$currencySymbol = $this->primaryCurrency->symbol;
$currencyCode = $this->primaryCurrency->code;
$currencyDecimalPlaces = $this->primaryCurrency->decimal_places;
}
if (!array_key_exists($key, $result)) { if (!array_key_exists($key, $result)) {
$result[$key] = [ $result[$key] = [
'total' => '0', 'total' => '0',
'budget_id' => $budgetId, 'budget_id' => $budgetId,
'currency_name' => $journal['currency_name'], 'currency_name' => $currencyName,
'currency_symbol' => $journal['currency_symbol'], 'currency_symbol' => $currencySymbol,
'currency_code' => $journal['currency_code'], 'currency_code' => $currencyCode,
'currency_decimal_places' => $currencyDecimalPlaces,
]; ];
} }
$result[$key]['total'] = bcadd((string) $journal['amount'], $result[$key]['total']); $result[$key]['total'] = bcadd((string)$journal[$field], $result[$key]['total']);
} }
$names = $this->getBudgetNames($budgetIds); $names = $this->getBudgetNames($budgetIds);
foreach ($result as $row) { foreach ($result as $row) {
$budgetId = $row['budget_id']; $budgetId = $row['budget_id'];
$name = $names[$budgetId]; $name = $names[$budgetId];
$label = (string) trans('firefly.name_in_currency', ['name' => $name, 'currency' => $row['currency_name']]); $label = (string)trans('firefly.name_in_currency', ['name' => $name, 'currency' => $row['currency_name']]);
$chartData[$label] = ['amount' => $row['total'], 'currency_symbol' => $row['currency_symbol'], 'currency_code' => $row['currency_code']]; $chartData[$label] = ['amount' => $row['total'], 'currency_symbol' => $row['currency_symbol'], 'currency_code' => $row['currency_code']];
} }
$data = $this->generator->multiCurrencyPieChart($chartData); $data = $this->generator->multiCurrencyPieChart($chartData);
$cache->store($data); $cache->store($data);
return response()->json($data); return response()->json($data);
@@ -285,13 +308,14 @@ class AccountController extends Controller
*/ */
public function expenseCategory(Account $account, Carbon $start, Carbon $end): JsonResponse public function expenseCategory(Account $account, Carbon $start, Carbon $end): JsonResponse
{ {
$cache = new CacheProperties(); $cache = new CacheProperties();
$cache->addProperty($account->id); $cache->addProperty($account->id);
$cache->addProperty($start); $cache->addProperty($start);
$cache->addProperty($end); $cache->addProperty($end);
$cache->addProperty($this->convertToPrimary);
$cache->addProperty('chart.account.expense-category'); $cache->addProperty('chart.account.expense-category');
if ($cache->has()) { if ($cache->has()) {
return response()->json($cache->get()); return response()->json($cache->get());
} }
/** @var GroupCollectorInterface $collector */ /** @var GroupCollectorInterface $collector */
@@ -303,28 +327,45 @@ class AccountController extends Controller
/** @var array $journal */ /** @var array $journal */
foreach ($journals as $journal) { foreach ($journals as $journal) {
$key = sprintf('%d-%d', $journal['category_id'], $journal['currency_id']); $key = sprintf('%d-%d', $journal['category_id'], $journal['currency_id']);
if (!array_key_exists($key, $result)) { if (!array_key_exists($key, $result)) {
// currency info:
$currencyId = (int)$journal['currency_id'];
$currencyName = $journal['currency_name'];
$currencySymbol = $journal['currency_symbol'];
$currencyCode = $journal['currency_code'];
$currencyDecimalPlaces = $journal['currency_decimal_places'];
$field = 'amount';
if ($this->convertToPrimary && $this->primaryCurrency->id !== $currencyId) {
$field = 'pc_amount';
$currencyName = $this->primaryCurrency->name;
$currencySymbol = $this->primaryCurrency->symbol;
$currencyCode = $this->primaryCurrency->code;
$currencyDecimalPlaces = $this->primaryCurrency->decimal_places;
}
$result[$key] = [ $result[$key] = [
'total' => '0', 'total' => '0',
'category_id' => (int) $journal['category_id'], 'category_id' => (int)$journal['category_id'],
'currency_name' => $journal['currency_name'], 'currency_name' => $currencyName,
'currency_symbol' => $journal['currency_symbol'], 'currency_code' => $currencyCode,
'currency_code' => $journal['currency_code'], 'currency_symbol' => $currencySymbol,
'currency_decimal_places' => $currencyDecimalPlaces,
]; ];
} }
$result[$key]['total'] = bcadd((string) $journal['amount'], $result[$key]['total']); $result[$key]['total'] = bcadd((string)$journal[$field], $result[$key]['total']);
} }
$names = $this->getCategoryNames(array_keys($result)); $names = $this->getCategoryNames(array_keys($result));
foreach ($result as $row) { foreach ($result as $row) {
$categoryId = $row['category_id']; $categoryId = $row['category_id'];
$name = $names[$categoryId] ?? '(unknown)'; $name = $names[$categoryId] ?? '(unknown)';
$label = (string) trans('firefly.name_in_currency', ['name' => $name, 'currency' => $row['currency_name']]); $label = (string)trans('firefly.name_in_currency', ['name' => $name, 'currency' => $row['currency_name']]);
$chartData[$label] = ['amount' => $row['total'], 'currency_symbol' => $row['currency_symbol'], 'currency_code' => $row['currency_code']]; $chartData[$label] = ['amount' => $row['total'], 'currency_symbol' => $row['currency_symbol'], 'currency_code' => $row['currency_code']];
} }
$data = $this->generator->multiCurrencyPieChart($chartData); $data = $this->generator->multiCurrencyPieChart($chartData);
$cache->store($data); $cache->store($data);
return response()->json($data); return response()->json($data);
@@ -337,18 +378,18 @@ class AccountController extends Controller
* */ * */
public function frontpage(AccountRepositoryInterface $repository): JsonResponse public function frontpage(AccountRepositoryInterface $repository): JsonResponse
{ {
$start = clone session('start', today(config('app.timezone'))->startOfMonth()); $start = clone session('start', today(config('app.timezone'))->startOfMonth());
$end = clone session('end', today(config('app.timezone'))->endOfMonth()); $end = clone session('end', today(config('app.timezone'))->endOfMonth());
$defaultSet = $repository->getAccountsByType([AccountTypeEnum::DEFAULT->value, AccountTypeEnum::ASSET->value])->pluck('id')->toArray(); $defaultSet = $repository->getAccountsByType([AccountTypeEnum::DEFAULT->value, AccountTypeEnum::ASSET->value])->pluck('id')->toArray();
// Log::debug('Default set is ', $defaultSet); // Log::debug('Default set is ', $defaultSet);
$frontpage = app('preferences')->get('frontpageAccounts', $defaultSet); $frontpage = Preferences::get('frontpageAccounts', $defaultSet);
$frontpageArray = !is_array($frontpage->data) ? [] : $frontpage->data; $frontpageArray = !is_array($frontpage->data) ? [] : $frontpage->data;
Log::debug('Frontpage preference set is ', $frontpageArray); Log::debug('Frontpage preference set is ', $frontpageArray);
if (0 === count($frontpageArray)) { if (0 === count($frontpageArray)) {
app('preferences')->set('frontpageAccounts', $defaultSet); Preferences::set('frontpageAccounts', $defaultSet);
Log::debug('frontpage set is empty!'); Log::debug('frontpage set is empty!');
} }
$accounts = $repository->getAccountsById($frontpageArray); $accounts = $repository->getAccountsById($frontpageArray);
// move to end of day for $end. // move to end of day for $end.
$end->endOfDay(); $end->endOfDay();
@@ -372,7 +413,7 @@ class AccountController extends Controller
*/ */
public function incomeCategory(Account $account, Carbon $start, Carbon $end): JsonResponse public function incomeCategory(Account $account, Carbon $start, Carbon $end): JsonResponse
{ {
$cache = new CacheProperties(); $cache = new CacheProperties();
$cache->addProperty($account->id); $cache->addProperty($account->id);
$cache->addProperty($start); $cache->addProperty($start);
$cache->addProperty($end); $cache->addProperty($end);
@@ -392,7 +433,7 @@ class AccountController extends Controller
/** @var array $journal */ /** @var array $journal */
foreach ($journals as $journal) { foreach ($journals as $journal) {
$key = sprintf('%d-%d', $journal['category_id'], $journal['currency_id']); $key = sprintf('%d-%d', $journal['category_id'], $journal['currency_id']);
if (!array_key_exists($key, $result)) { if (!array_key_exists($key, $result)) {
$result[$key] = [ $result[$key] = [
'total' => '0', 'total' => '0',
@@ -402,17 +443,17 @@ class AccountController extends Controller
'currency_code' => $journal['currency_code'], 'currency_code' => $journal['currency_code'],
]; ];
} }
$result[$key]['total'] = bcadd((string) $journal['amount'], $result[$key]['total']); $result[$key]['total'] = bcadd((string)$journal['amount'], $result[$key]['total']);
} }
$names = $this->getCategoryNames(array_keys($result)); $names = $this->getCategoryNames(array_keys($result));
foreach ($result as $row) { foreach ($result as $row) {
$categoryId = $row['category_id']; $categoryId = $row['category_id'];
$name = $names[$categoryId] ?? '(unknown)'; $name = $names[$categoryId] ?? '(unknown)';
$label = (string) trans('firefly.name_in_currency', ['name' => $name, 'currency' => $row['currency_name']]); $label = (string)trans('firefly.name_in_currency', ['name' => $name, 'currency' => $row['currency_name']]);
$chartData[$label] = ['amount' => $row['total'], 'currency_symbol' => $row['currency_symbol'], 'currency_code' => $row['currency_code']]; $chartData[$label] = ['amount' => $row['total'], 'currency_symbol' => $row['currency_symbol'], 'currency_code' => $row['currency_code']];
} }
$data = $this->generator->multiCurrencyPieChart($chartData); $data = $this->generator->multiCurrencyPieChart($chartData);
$cache->store($data); $cache->store($data);
return response()->json($data); return response()->json($data);
@@ -429,7 +470,7 @@ class AccountController extends Controller
$end->endOfDay(); $end->endOfDay();
// TODO not sure if these date ranges will work as expected. // TODO not sure if these date ranges will work as expected.
Log::debug(sprintf('Now in period("%s", "%s")', $start->format('Y-m-d H:i:s'), $end->format('Y-m-d H:i:s'))); Log::debug(sprintf('Now in period("%s", "%s")', $start->format('Y-m-d H:i:s'), $end->format('Y-m-d H:i:s')));
$cache = new CacheProperties(); $cache = new CacheProperties();
$cache->addProperty('chart.account.period'); $cache->addProperty('chart.account.period');
$cache->addProperty($start); $cache->addProperty($start);
$cache->addProperty($end); $cache->addProperty($end);
@@ -440,21 +481,21 @@ class AccountController extends Controller
} }
// collect and filter balances for the entire period. // collect and filter balances for the entire period.
$step = $this->calculateStep($start, $end); $step = $this->calculateStep($start, $end);
Log::debug(sprintf('Step is %s', $step)); Log::debug(sprintf('Step is %s', $step));
$locale = Steam::getLocale(); $locale = Steam::getLocale();
$return = []; $return = [];
// fix for issue https://github.com/firefly-iii/firefly-iii/issues/8041 // fix for issue https://github.com/firefly-iii/firefly-iii/issues/8041
// have to make sure this chart is always based on the balance at the END of the period. // have to make sure this chart is always based on the balance at the END of the period.
// This period depends on the size of the chart // This period depends on the size of the chart
$current = clone $start; $current = clone $start;
$current = app('navigation')->endOfX($current, $step, null); $current = app('navigation')->endOfX($current, $step, null);
$format = (string) trans('config.month_and_day_js', [], $locale); $format = (string)trans('config.month_and_day_js', [], $locale);
$accountCurrency = $this->accountRepository->getAccountCurrency($account); $accountCurrency = $this->accountRepository->getAccountCurrency($account);
$range = Steam::finalAccountBalanceInRange($account, $start, $end, $this->convertToPrimary); $range = Steam::finalAccountBalanceInRange($account, $start, $end, $this->convertToPrimary);
$range = Steam::filterAccountBalances($range, $account, $this->convertToPrimary, $accountCurrency); $range = Steam::filterAccountBalances($range, $account, $this->convertToPrimary, $accountCurrency);
// temp, get end balance. // temp, get end balance.
Log::debug(sprintf('period: Call finalAccountBalance with date/time "%s"', $end->toIso8601String())); Log::debug(sprintf('period: Call finalAccountBalance with date/time "%s"', $end->toIso8601String()));
@@ -465,14 +506,14 @@ class AccountController extends Controller
$accountCurrency ??= $this->primaryCurrency; // do this AFTER getting the balances. $accountCurrency ??= $this->primaryCurrency; // do this AFTER getting the balances.
Log::debug('Start chart loop.'); Log::debug('Start chart loop.');
$newRange = []; $newRange = [];
$expectedIndex = 0; $expectedIndex = 0;
Log::debug('Balances exist at:'); Log::debug('Balances exist at:');
foreach ($range as $key => $value) { foreach ($range as $key => $value) {
$newRange[] = ['date' => $key, 'info' => $value]; $newRange[] = ['date' => $key, 'info' => $value];
Log::debug(sprintf('%d - %s (%s)', count($newRange) - 1, $key, json_encode($value))); Log::debug(sprintf('%d - %s (%s)', count($newRange) - 1, $key, json_encode($value)));
} }
$carbon = Carbon::createFromFormat('Y-m-d', $newRange[0]['date'])->endOfDay(); $carbon = Carbon::createFromFormat('Y-m-d', $newRange[0]['date'])->endOfDay();
Log::debug(sprintf('Start of loop, $carbon is %s', $carbon->format('Y-m-d H:i:s'))); Log::debug(sprintf('Start of loop, $carbon is %s', $carbon->format('Y-m-d H:i:s')));
while ($end->gte($current)) { while ($end->gte($current)) {
$momentBalance = $previous; $momentBalance = $previous;
@@ -493,26 +534,26 @@ class AccountController extends Controller
} }
} }
Log::debug(sprintf('momentBalance is now %s', json_encode($momentBalance))); Log::debug(sprintf('momentBalance is now %s', json_encode($momentBalance)));
$return = $this->updateChartKeys($return, $momentBalance); $return = $this->updateChartKeys($return, $momentBalance);
$previous = $momentBalance; $previous = $momentBalance;
// process each balance thing. // process each balance thing.
foreach ($momentBalance as $key => $amount) { foreach ($momentBalance as $key => $amount) {
$label = $current->isoFormat($format); $label = $current->isoFormat($format);
$return[$key]['entries'][$label] = $amount; $return[$key]['entries'][$label] = $amount;
} }
$current = app('navigation')->addPeriod($current, $step, 0); $current = app('navigation')->addPeriod($current, $step, 0);
// here too, to fix #8041, the data is corrected to the end of the period. // here too, to fix #8041, the data is corrected to the end of the period.
$current = app('navigation')->endOfX($current, $step, null); $current = app('navigation')->endOfX($current, $step, null);
} }
Log::debug('End of chart loop.'); Log::debug('End of chart loop.');
// second loop (yes) to create nice array with info! Yay! // second loop (yes) to create nice array with info! Yay!
$chartData = []; $chartData = [];
foreach ($return as $key => $info) { foreach ($return as $key => $info) {
if ('balance' !== $key && 'pc_balance' !== $key) { if ('balance' !== $key && 'pc_balance' !== $key) {
// assume it's a currency: // assume it's a currency:
$setCurrency = $this->currencyRepository->findByCode((string) $key); $setCurrency = $this->currencyRepository->findByCode((string)$key);
$info['currency_symbol'] = $setCurrency->symbol; $info['currency_symbol'] = $setCurrency->symbol;
$info['currency_code'] = $setCurrency->code; $info['currency_code'] = $setCurrency->code;
$info['label'] = sprintf('%s (%s)', $account->name, $setCurrency->symbol); $info['label'] = sprintf('%s (%s)', $account->name, $setCurrency->symbol);
@@ -525,12 +566,12 @@ class AccountController extends Controller
if ('pc_balance' === $key) { if ('pc_balance' === $key) {
$info['currency_symbol'] = $this->primaryCurrency->symbol; $info['currency_symbol'] = $this->primaryCurrency->symbol;
$info['currency_code'] = $this->primaryCurrency->code; $info['currency_code'] = $this->primaryCurrency->code;
$info['label'] = sprintf('%s (%s) (%s)', $account->name, (string) trans('firefly.sum'), $this->primaryCurrency->symbol); $info['label'] = sprintf('%s (%s) (%s)', $account->name, (string)trans('firefly.sum'), $this->primaryCurrency->symbol);
} }
$chartData[] = $info; $chartData[] = $info;
} }
$data = $this->generator->multiSet($chartData); $data = $this->generator->multiSet($chartData);
$cache->store($data); $cache->store($data);
return response()->json($data); return response()->json($data);
@@ -567,15 +608,15 @@ class AccountController extends Controller
public function revenueAccounts(): JsonResponse public function revenueAccounts(): JsonResponse
{ {
/** @var Carbon $start */ /** @var Carbon $start */
$start = clone session('start', today(config('app.timezone'))->startOfMonth()); $start = clone session('start', today(config('app.timezone'))->startOfMonth());
/** @var Carbon $end */ /** @var Carbon $end */
$end = clone session('end', today(config('app.timezone'))->endOfMonth()); $end = clone session('end', today(config('app.timezone'))->endOfMonth());
$start->startOfDay(); $start->startOfDay();
$end->endOfDay(); $end->endOfDay();
$cache = new CacheProperties(); $cache = new CacheProperties();
$cache->addProperty($start); $cache->addProperty($start);
$cache->addProperty($end); $cache->addProperty($end);
$cache->addProperty($this->convertToPrimary); $cache->addProperty($this->convertToPrimary);
@@ -585,13 +626,13 @@ class AccountController extends Controller
} }
// prep some vars: // prep some vars:
$currencies = []; $currencies = [];
$chartData = []; $chartData = [];
$tempData = []; $tempData = [];
// grab all accounts and names // grab all accounts and names
$accounts = $this->accountRepository->getAccountsByType([AccountTypeEnum::REVENUE->value]); $accounts = $this->accountRepository->getAccountsByType([AccountTypeEnum::REVENUE->value]);
$accountNames = $this->extractNames($accounts); $accountNames = $this->extractNames($accounts);
// grab all balances // grab all balances
Log::debug(sprintf('revAccounts: finalAccountsBalance("%s")', $start->format('Y-m-d H:i:s'))); Log::debug(sprintf('revAccounts: finalAccountsBalance("%s")', $start->format('Y-m-d H:i:s')));
@@ -626,21 +667,21 @@ class AccountController extends Controller
continue; continue;
} }
// Log::debug(sprintf('Will process expense array "%s" with amount %s', $key, $endBalance)); // Log::debug(sprintf('Will process expense array "%s" with amount %s', $key, $endBalance));
$searchCode = $this->convertToPrimary ? $this->primaryCurrency->code : $key; $searchCode = $this->convertToPrimary ? $this->primaryCurrency->code : $key;
$searchCode = 'balance' === $searchCode || 'pc_balance' === $searchCode ? $this->primaryCurrency->code : $searchCode; $searchCode = 'balance' === $searchCode || 'pc_balance' === $searchCode ? $this->primaryCurrency->code : $searchCode;
// Log::debug(sprintf('Search code is %s', $searchCode)); // Log::debug(sprintf('Search code is %s', $searchCode));
// see if there is an accompanying start amount. // see if there is an accompanying start amount.
// grab the difference and find the currency. // grab the difference and find the currency.
$startBalance = ($startBalances[$account->id][$key] ?? '0'); $startBalance = ($startBalances[$account->id][$key] ?? '0');
// Log::debug(sprintf('Start balance is %s', $startBalance)); // Log::debug(sprintf('Start balance is %s', $startBalance));
$diff = bcsub($endBalance, $startBalance); $diff = bcsub($endBalance, $startBalance);
$currencies[$searchCode] ??= $this->currencyRepository->findByCode($searchCode); $currencies[$searchCode] ??= $this->currencyRepository->findByCode($searchCode);
if (0 !== bccomp($diff, '0')) { if (0 !== bccomp($diff, '0')) {
// store the values in a temporary array. // store the values in a temporary array.
$tempData[] = [ $tempData[] = [
'name' => $accountNames[$account->id], 'name' => $accountNames[$account->id],
'difference' => $diff, 'difference' => $diff,
'diff_float' => (float) $diff, // intentional float 'diff_float' => (float)$diff, // intentional float
'currency_id' => $currencies[$searchCode]->id, 'currency_id' => $currencies[$searchCode]->id,
]; ];
} }
@@ -653,26 +694,26 @@ class AccountController extends Controller
foreach ($currencies as $currency) { foreach ($currencies as $currency) {
$newCurrencies[$currency->id] = $currency; $newCurrencies[$currency->id] = $currency;
} }
$currencies = $newCurrencies; $currencies = $newCurrencies;
// sort temp array by amount. // sort temp array by amount.
$amounts = array_column($tempData, 'diff_float'); $amounts = array_column($tempData, 'diff_float');
array_multisort($amounts, SORT_ASC, $tempData); array_multisort($amounts, SORT_ASC, $tempData);
// loop all found currencies and build the data array for the chart. // loop all found currencies and build the data array for the chart.
/** /**
* @var int $currencyId * @var int $currencyId
* @var TransactionCurrency $currency * @var TransactionCurrency $currency
*/ */
foreach ($currencies as $currencyId => $currency) { foreach ($currencies as $currencyId => $currency) {
$dataSet $dataSet
= [ = [
'label' => (string) trans('firefly.earned'), 'label' => (string)trans('firefly.earned'),
'type' => 'bar', 'type' => 'bar',
'currency_symbol' => $currency->symbol, 'currency_symbol' => $currency->symbol,
'currency_code' => $currency->code, 'currency_code' => $currency->code,
'entries' => $this->expandNames($tempData), 'entries' => $this->expandNames($tempData),
]; ];
$chartData[$currencyId] = $dataSet; $chartData[$currencyId] = $dataSet;
} }
@@ -683,7 +724,7 @@ class AccountController extends Controller
$chartData[$currencyId]['entries'][$name] = bcmul($entry['difference'], '-1'); $chartData[$currencyId]['entries'][$name] = bcmul($entry['difference'], '-1');
} }
$data = $this->generator->multiSet($chartData); $data = $this->generator->multiSet($chartData);
$cache->store($data); $cache->store($data);
return response()->json($data); return response()->json($data);

View File

@@ -23,6 +23,7 @@ declare(strict_types=1);
namespace FireflyIII\Http\Controllers; namespace FireflyIII\Http\Controllers;
use FireflyIII\Support\Singleton\PreferencesSingleton;
use JsonException; use JsonException;
use Carbon\Carbon; use Carbon\Carbon;
use FireflyIII\Enums\AccountTypeEnum; use FireflyIII\Enums\AccountTypeEnum;
@@ -269,7 +270,9 @@ class PreferencesController extends Controller
if ($convertToPrimary && !$this->convertToPrimary) { if ($convertToPrimary && !$this->convertToPrimary) {
// set to true! // set to true!
Log::debug('User sets convertToPrimary to true.'); Log::debug('User sets convertToPrimary to true.');
Preferences::set('convert_to_primary', $convertToPrimary); Preferences::set('convert_to_primary', true);
$singleton = PreferencesSingleton::getInstance();
$singleton->resetPreferences();
event(new UserGroupChangedPrimaryCurrency(auth()->user()->userGroup)); event(new UserGroupChangedPrimaryCurrency(auth()->user()->userGroup));
} }
Preferences::set('convert_to_primary', $convertToPrimary); Preferences::set('convert_to_primary', $convertToPrimary);

View File

@@ -25,16 +25,20 @@ declare(strict_types=1);
namespace FireflyIII\Http\Controllers\Transaction; namespace FireflyIII\Http\Controllers\Transaction;
use FireflyIII\Exceptions\FireflyException; use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Helpers\Collector\GroupCollectorInterface;
use FireflyIII\Http\Controllers\Controller; use FireflyIII\Http\Controllers\Controller;
use FireflyIII\Models\TransactionGroup; use FireflyIII\Models\TransactionGroup;
use FireflyIII\Models\TransactionJournal; use FireflyIII\Models\TransactionJournal;
use FireflyIII\Repositories\AuditLogEntry\ALERepositoryInterface; use FireflyIII\Repositories\AuditLogEntry\ALERepositoryInterface;
use FireflyIII\Repositories\TransactionGroup\TransactionGroupRepositoryInterface; use FireflyIII\Repositories\TransactionGroup\TransactionGroupRepositoryInterface;
use FireflyIII\Support\JsonApi\Enrichments\TransactionGroupEnrichment;
use FireflyIII\Transformers\TransactionGroupTransformer; use FireflyIII\Transformers\TransactionGroupTransformer;
use FireflyIII\User;
use Illuminate\Contracts\View\Factory; use Illuminate\Contracts\View\Factory;
use Illuminate\Http\JsonResponse; use Illuminate\Http\JsonResponse;
use Illuminate\View\View; use Illuminate\View\View;
use Symfony\Component\HttpFoundation\ParameterBag; use Symfony\Component\HttpFoundation\ParameterBag;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
/** /**
* Class ShowController * Class ShowController
@@ -57,7 +61,7 @@ class ShowController extends Controller
$this->repository = app(TransactionGroupRepositoryInterface::class); $this->repository = app(TransactionGroupRepositoryInterface::class);
$this->aleRepository = app(ALERepositoryInterface::class); $this->aleRepository = app(ALERepositoryInterface::class);
app('view')->share('title', (string) trans('firefly.transactions')); app('view')->share('title', (string)trans('firefly.transactions'));
app('view')->share('mainTitleIcon', 'fa-exchange'); app('view')->share('mainTitleIcon', 'fa-exchange');
return $next($request); return $next($request);
@@ -80,43 +84,67 @@ class ShowController extends Controller
*/ */
public function show(TransactionGroup $transactionGroup) public function show(TransactionGroup $transactionGroup)
{ {
/** @var User $admin */
$admin = auth()->user();
// use new group collector:
/** @var GroupCollectorInterface $collector */
$collector = app(GroupCollectorInterface::class);
$collector->setUser($admin)->setTransactionGroup($transactionGroup)->withAPIInformation();
$selectedGroup = $collector->getGroups()->first();
if (null === $selectedGroup) {
throw new NotFoundHttpException();
}
// enrich
$enrichment = new TransactionGroupEnrichment();
$enrichment->setUser($admin);
$selectedGroup = $enrichment->enrichSingle($selectedGroup);
/** @var null|TransactionJournal $first */ /** @var null|TransactionJournal $first */
$first = $transactionGroup->transactionJournals()->first(['transaction_journals.*']); $first = $transactionGroup->transactionJournals()->first(['transaction_journals.*']);
$splits = $transactionGroup->transactionJournals()->count(); $splits = $transactionGroup->transactionJournals()->count();
$splits = count($selectedGroup['transactions']);
$keys = array_keys($selectedGroup['transactions']);
$first = $selectedGroup['transactions'][array_shift($keys)];
unset($keys);
if (null === $first) { if (null === $first) {
throw new FireflyException('This transaction is broken :(.'); throw new FireflyException('This transaction is broken :(.');
} }
$type = (string)trans(sprintf('firefly.%s', $first['transaction_type_type']));
$title = 1 === $splits ? $first['description'] : $selectedGroup['title'];
$subTitle = sprintf('%s: "%s"', $type, $title);
$type = (string) trans(sprintf('firefly.%s', $first->transactionType->type)); // enrich
$title = 1 === $splits ? $first->description : $transactionGroup->title; $enrichment = new TransactionGroupEnrichment();
$subTitle = sprintf('%s: "%s"', $type, $title); $enrichment->setUser($admin);
$selectedGroup = $enrichment->enrichSingle($selectedGroup);
/** @var TransactionGroupTransformer $transformer */ /** @var TransactionGroupTransformer $transformer */
$transformer = app(TransactionGroupTransformer::class); $transformer = app(TransactionGroupTransformer::class);
$transformer->setParameters(new ParameterBag()); $transformer->setParameters(new ParameterBag());
$groupArray = $transformer->transformObject($transactionGroup); $groupArray = $transformer->transformObject($transactionGroup);
// do some calculations: // do some calculations:
$amounts = $this->getAmounts($groupArray); $amounts = $this->getAmounts($selectedGroup);
$accounts = $this->getAccounts($groupArray); $accounts = $this->getAccounts($selectedGroup);
foreach (array_keys($groupArray['transactions']) as $index) { foreach (array_keys($selectedGroup['transactions']) as $index) {
$groupArray['transactions'][$index]['tags'] = $this->repository->getTagObjects( $selectedGroup['transactions'][$index]['tags'] = $this->repository->getTagObjects((int)$selectedGroup['transactions'][$index]['transaction_journal_id']);
(int) $groupArray['transactions'][$index]['transaction_journal_id']
);
} }
// get audit log entries: // get audit log entries:
$groupLogEntries = $this->aleRepository->getForObject($transactionGroup); $groupLogEntries = $this->aleRepository->getForObject($transactionGroup);
$logEntries = []; $logEntries = [];
foreach ($transactionGroup->transactionJournals as $journal) { foreach ($selectedGroup['transactions'] as $journal) {
$logEntries[$journal->id] = $this->aleRepository->getForObject($journal); $logEntries[$journal['transaction_journal_id']] = $this->aleRepository->getForId(TransactionJournal::class, $journal['transaction_journal_id']);
} }
$events = $this->repository->getPiggyEvents($transactionGroup); $events = $this->repository->getPiggyEvents($transactionGroup);
$attachments = $this->repository->getAttachments($transactionGroup); $attachments = $this->repository->getAttachments($transactionGroup);
$links = $this->repository->getLinks($transactionGroup); $links = $this->repository->getLinks($transactionGroup);
return view( return view(
'transactions.show', 'transactions.show',
@@ -129,6 +157,7 @@ class ShowController extends Controller
'groupLogEntries', 'groupLogEntries',
'subTitle', 'subTitle',
'splits', 'splits',
'selectedGroup',
'groupArray', 'groupArray',
'events', 'events',
'attachments', 'attachments',
@@ -142,34 +171,38 @@ class ShowController extends Controller
{ {
$amounts = []; $amounts = [];
foreach ($group['transactions'] as $transaction) { foreach ($group['transactions'] as $transaction) {
// add normal amount:
$symbol = $transaction['currency_symbol']; $symbol = $transaction['currency_symbol'];
if (!array_key_exists($symbol, $amounts)) { $amounts[$symbol] ??= [
$amounts[$symbol] = [ 'amount' => '0',
'amount' => '0', 'symbol' => $symbol,
'symbol' => $symbol, 'decimal_places' => $transaction['currency_decimal_places'],
'decimal_places' => $transaction['currency_decimal_places'], ];
]; $amounts[$symbol]['amount'] = bcadd($amounts[$symbol]['amount'], (string)$transaction['amount']);
}
$amounts[$symbol]['amount'] = bcadd($amounts[$symbol]['amount'], (string) $transaction['amount']); // add foreign amount:
if (null !== $transaction['foreign_amount'] && '' !== $transaction['foreign_amount'] if (null !== $transaction['foreign_amount'] && '' !== $transaction['foreign_amount'] && 0 !== bccomp('0', (string)$transaction['foreign_amount'])) {
&& 0 !== bccomp(
'0',
(string) $transaction['foreign_amount']
)) {
// same for foreign currency: // same for foreign currency:
$foreignSymbol = $transaction['foreign_currency_symbol']; $foreignSymbol = $transaction['foreign_currency_symbol'];
if (!array_key_exists($foreignSymbol, $amounts)) { $amounts[$foreignSymbol] ??= [
$amounts[$foreignSymbol] = [ 'amount' => '0',
'amount' => '0', 'symbol' => $foreignSymbol,
'symbol' => $foreignSymbol, 'decimal_places' => $transaction['foreign_currency_decimal_places'],
'decimal_places' => $transaction['foreign_currency_decimal_places'], ];
]; $amounts[$foreignSymbol]['amount'] = bcadd($amounts[$foreignSymbol]['amount'], (string)$transaction['foreign_amount']);
}
$amounts[$foreignSymbol]['amount'] = bcadd(
$amounts[$foreignSymbol]['amount'],
(string) $transaction['foreign_amount']
);
} }
// add primary currency amount
if (null !== $transaction['pc_amount'] && $transaction['currency_id'] !== $this->primaryCurrency->id) {
// same for foreign currency:
$primarySymbol = $this->primaryCurrency->symbol;
$amounts[$primarySymbol] ??= [
'amount' => '0',
'symbol' => $this->primaryCurrency->symbol,
'decimal_places' => $this->primaryCurrency->decimal_places,
];
$amounts[$primarySymbol]['amount'] = bcadd($amounts[$primarySymbol]['amount'], (string)$transaction['pc_amount']);
}
} }
return $amounts; return $amounts;
@@ -177,23 +210,23 @@ class ShowController extends Controller
private function getAccounts(array $group): array private function getAccounts(array $group): array
{ {
$accounts = [ $accounts = [
'source' => [], 'source' => [],
'destination' => [], 'destination' => [],
]; ];
foreach ($group['transactions'] as $transaction) { foreach ($group['transactions'] as $transaction) {
$accounts['source'][] = [ $accounts['source'][] = [
'type' => $transaction['source_type'], 'type' => $transaction['source_account_type'],
'id' => $transaction['source_id'], 'id' => $transaction['source_account_id'],
'name' => $transaction['source_name'], 'name' => $transaction['source_account_name'],
'iban' => $transaction['source_iban'], 'iban' => $transaction['source_account_iban'],
]; ];
$accounts['destination'][] = [ $accounts['destination'][] = [
'type' => $transaction['destination_type'], 'type' => $transaction['destination_account_type'],
'id' => $transaction['destination_id'], 'id' => $transaction['destination_account_id'],
'name' => $transaction['destination_name'], 'name' => $transaction['destination_account_name'],
'iban' => $transaction['destination_iban'], 'iban' => $transaction['destination_account_iban'],
]; ];
} }

View File

@@ -52,4 +52,10 @@ class ALERepository implements ALERepositoryInterface
return $auditLogEntry; return $auditLogEntry;
} }
public function getForId(string $model, int $modelId): Collection
{
// all Models have an ID.
return AuditLogEntry::where('auditable_id', $modelId)->where('auditable_type', $model)->get();
}
} }

View File

@@ -45,6 +45,8 @@ use Illuminate\Support\Collection;
interface ALERepositoryInterface interface ALERepositoryInterface
{ {
public function getForObject(Model $model): Collection; public function getForObject(Model $model): Collection;
public function getForId(string $model, int $modelId): Collection;
public function store(array $data): AuditLogEntry; public function store(array $data): AuditLogEntry;
} }

View File

@@ -117,18 +117,23 @@ class Amount
public function convertToPrimary(?User $user = null): bool public function convertToPrimary(?User $user = null): bool
{ {
$instance = PreferencesSingleton::getInstance(); $instance = PreferencesSingleton::getInstance();
$pref = $instance->getPreference('convert_to_primary'); if (!$user instanceof User) {
if (null === $pref) { $pref = $instance->getPreference('convert_to_primary_no_user');
if (!$user instanceof User) { if (null === $pref) {
$res = true === Preferences::get('convert_to_primary', false)->data && true === config('cer.enabled'); $res = true === Preferences::get('convert_to_primary', false)->data && true === config('cer.enabled');
$instance->setPreference('convert_to_primary', $res); $instance->setPreference('convert_to_primary_no_user', $res);
return $res; return $res;
} }
return $pref;
}
$key = sprintf('convert_to_primary_%d', $user->id);
$pref = $instance->getPreference($key);
if(null === $pref) {
$res = true === Preferences::getForUser($user, 'convert_to_primary', false)->data && true === config('cer.enabled'); $res = true === Preferences::getForUser($user, 'convert_to_primary', false)->data && true === config('cer.enabled');
$instance->setPreference('convert_to_primary', $res); $instance->setPreference($key, $res);
return $res; return $res;
} }
return $pref; return $pref;
} }

View File

@@ -56,7 +56,7 @@ trait ChartGeneration
$cache->addProperty($accounts); $cache->addProperty($accounts);
$cache->addProperty($convertToPrimary); $cache->addProperty($convertToPrimary);
if ($cache->has()) { if ($cache->has()) {
return $cache->get(); return $cache->get();
} }
Log::debug('Regenerate chart.account.account-balance-chart from scratch.'); Log::debug('Regenerate chart.account.account-balance-chart from scratch.');
$locale = app('steam')->getLocale(); $locale = app('steam')->getLocale();
@@ -67,7 +67,7 @@ trait ChartGeneration
/** @var AccountRepositoryInterface $accountRepos */ /** @var AccountRepositoryInterface $accountRepos */
$accountRepos = app(AccountRepositoryInterface::class); $accountRepos = app(AccountRepositoryInterface::class);
$default = app('amount')->getPrimaryCurrency(); $primary = app('amount')->getPrimaryCurrency();
$chartData = []; $chartData = [];
Log::debug(sprintf('Start of accountBalanceChart(list, %s, %s)', $start->format('Y-m-d H:i:s'), $end->format('Y-m-d H:i:s'))); Log::debug(sprintf('Start of accountBalanceChart(list, %s, %s)', $start->format('Y-m-d H:i:s'), $end->format('Y-m-d H:i:s')));
@@ -75,10 +75,10 @@ trait ChartGeneration
/** @var Account $account */ /** @var Account $account */
foreach ($accounts as $account) { foreach ($accounts as $account) {
Log::debug(sprintf('Now at account #%d ("%s)', $account->id, $account->name)); Log::debug(sprintf('Now at account #%d ("%s)', $account->id, $account->name));
$currency = $accountRepos->getAccountCurrency($account) ?? $default; $currency = $accountRepos->getAccountCurrency($account) ?? $primary;
$usePrimary = $convertToPrimary && $default->id !== $currency->id; $usePrimary = $convertToPrimary && $primary->id !== $currency->id;
$field = $convertToPrimary ? 'pc_balance' : 'balance'; $field = $convertToPrimary ? 'pc_balance' : 'balance';
$currency = $usePrimary ? $default : $currency; $currency = $usePrimary ? $primary : $currency;
Log::debug(sprintf('Will use field %s', $field)); Log::debug(sprintf('Will use field %s', $field));
$currentSet = [ $currentSet = [
'label' => $account->name, 'label' => $account->name,

View File

@@ -22,6 +22,10 @@ class PreferencesSingleton
return self::$instance; return self::$instance;
} }
public function resetPreferences(): void {
$this->preferences = [];
}
public function setPreference(string $key, mixed $value): void public function setPreference(string $key, mixed $value): void
{ {
$this->preferences[$key] = $value; $this->preferences[$key] = $value;

View File

@@ -64,7 +64,7 @@
<tbody> <tbody>
<tr> <tr>
<td style="width:40%;">{{ trans('list.type') }}</td> <td style="width:40%;">{{ trans('list.type') }}</td>
<td>{{ first.transactiontype.type|_ }}</td> <td>{{ first.transaction_type_type|_ }}</td>
</tr> </tr>
<tr> <tr>
<td>{{ trans('list.description') }}</td> <td>{{ trans('list.description') }}</td>
@@ -113,7 +113,7 @@
<div class="box-body no-padding"> <div class="box-body no-padding">
<table class="table table-hover"> <table class="table table-hover">
<tbody> <tbody>
{% if first.transactiontype.type != 'Withdrawal' or splits == 1 %} {% if first.transaction_type_type != 'Withdrawal' or splits == 1 %}
<tr> <tr>
<td style="width:40%;"> <td style="width:40%;">
{{ trans_choice('firefly.source_accounts', accounts['source']|length ) }} {{ trans_choice('firefly.source_accounts', accounts['source']|length ) }}
@@ -134,7 +134,7 @@
</tr> </tr>
{% endif %} {% endif %}
{% if first.transactiontype.type != 'Deposit' or splits == 1 %} {% if first.transaction_type_type != 'Deposit' or splits == 1 %}
<tr> <tr>
<td> <td>
{{ trans_choice('firefly.destination_accounts', accounts['destination']|length ) }} {{ trans_choice('firefly.destination_accounts', accounts['destination']|length ) }}
@@ -159,17 +159,17 @@
<td style="width:30%;">{{ 'total_amount'|_ }}</td> <td style="width:30%;">{{ 'total_amount'|_ }}</td>
<td> <td>
{% for amount in amounts %} {% for amount in amounts %}
{% if first.transactiontype.type == 'Withdrawal' %} {% if first.transaction_type_type == 'Withdrawal' %}
{{ formatAmountBySymbol(amount.amount*-1,amount.symbol, amount.decimal_places) }}{% if loop.index0 != amounts|length -1 %}, {% endif %} {{ formatAmountBySymbol(amount.amount*-1,amount.symbol, amount.decimal_places) }}{% if loop.index0 != amounts|length -1 %}, {% endif %}
{% elseif first.transactiontype.type == 'Deposit' %} {% elseif first.transaction_type_type == 'Deposit' %}
{{ formatAmountBySymbol(amount.amount,amount.symbol, amount.decimal_places) }}{% if loop.index0 != amounts|length -1 %}, {% endif %} {{ formatAmountBySymbol(amount.amount,amount.symbol, amount.decimal_places) }}{% if loop.index0 != amounts|length -1 %}, {% endif %}
{% elseif first.transactiontype.type == 'Transfer' %} {% elseif first.transaction_type_type == 'Transfer' %}
<span class="text-info money-transfer"> <span class="text-info money-transfer">
{{ formatAmountBySymbol(amount.amount, amount.symbol, amount.decimal_places, false) }}{% if loop.index0 != amounts|length -1 %}, {% endif %} {{ formatAmountBySymbol(amount.amount, amount.symbol, amount.decimal_places, false) }}{% if loop.index0 != amounts|length -1 %}, {% endif %}
</span> </span>
{% elseif first.transactiontype.type == 'Opening balance' %} {% elseif first.transaction_type_type == 'Opening balance' %}
{# Opening balance stored amount is always negative: find out which way the money goes #} {# Opening balance stored amount is always negative: find out which way the money goes #}
{% if groupArray.transactions[0].source_type == 'Initial balance account' %} {% if groupArray.transactions[0].source_account_type == 'Initial balance account' %}
{{ formatAmountBySymbol(amount.amount*-1,amount.symbol, amount.decimal_places) }} {{ formatAmountBySymbol(amount.amount*-1,amount.symbol, amount.decimal_places) }}
{% else %} {% else %}
{{ formatAmountBySymbol(amount.amount,amount.symbol, amount.decimal_places) }} {{ formatAmountBySymbol(amount.amount,amount.symbol, amount.decimal_places) }}
@@ -202,7 +202,7 @@
{% set boxSize = 4 %} {% set boxSize = 4 %}
{% endif %} {% endif %}
<div class="row"> <div class="row">
{% for index,journal in groupArray.transactions %} {% for index,journal in selectedGroup.transactions %}
<div class="col-lg-{{ boxSize }}"> <div class="col-lg-{{ boxSize }}">
<div class="box"> <div class="box">
<div class="box-header with-border"> <div class="box-header with-border">
@@ -289,48 +289,74 @@
<table class="table"> <table class="table">
<tr> <tr>
<td colspan="2"> <td colspan="2">
<!-- type is: "{{ first.transaction_type_type }}" -->
<!-- type is: "{{ first.transactiontype.type }}" --> {% if 'Cash account' == journal.source_account_type %}
{% if 'Cash account' == journal.source_type %}
<span class="text-success">({{ 'cash'|_ }})</span> <span class="text-success">({{ 'cash'|_ }})</span>
{% else %} {% else %}
<a href="{{ route('accounts.show', journal.source_id) }}" <a href="{{ route('accounts.show', journal.source_account_id) }}"
title="{{ journal.source_iban|default(journal.source_name) }}">{{ journal.source_name }}</a> &rarr; title="{{ journal.source_iban|default(journal.source_account_name) }}">{{ journal.source_account_name }}</a> &rarr;
{% endif %} {% endif %}
{% if first.transactiontype.type == 'Withdrawal' %} {% if first.transaction_type_type == 'Withdrawal' %}
{{ formatAmountBySymbol(journal.amount*-1, journal.currency_symbol, journal.currency_decimal_places) }} {{ formatAmountBySymbol(journal.amount*-1, journal.currency_symbol, journal.currency_decimal_places) }}
{% elseif first.transactiontype.type == 'Deposit' %} {% elseif first.transaction_type_type == 'Deposit' %}
{{ formatAmountBySymbol(journal.amount, journal.currency_symbol, journal.currency_decimal_places) }} {{ formatAmountBySymbol(journal.amount, journal.currency_symbol, journal.currency_decimal_places) }}
{% elseif first.transactiontype.type == 'Transfer' or first.transactiontype.type == 'Opening balance' %} {% elseif first.transaction_type_type == 'Transfer' or first.transaction_type_type == 'Opening balance' %}
<span class="text-info money-transfer"> <span class="text-info money-transfer">
{{ formatAmountBySymbol(journal.amount, journal.currency_symbol, journal.currency_decimal_places, false) }} {{ formatAmountBySymbol(journal.amount, journal.currency_symbol, journal.currency_decimal_places, false) }}
</span> </span>
{% elseif first.transactiontype.type == 'Liability credit' %} {% elseif first.transaction_type_type == 'Liability credit' %}
<span class="text-info money-transfer"> <span class="text-info money-transfer">
{{ formatAmountBySymbol(journal.amount*-1, journal.currency_symbol, journal.currency_decimal_places, false) }} {{ formatAmountBySymbol(journal.amount*-1, journal.currency_symbol, journal.currency_decimal_places, false) }}
</span> </span>
{% endif %} {% endif %}
<!-- do primary currency amount -->
{% if null != journal.pc_amount and primaryCurrency.id != journal.currency_id %}
{% if first.transaction_type_type == 'Withdrawal' %}
({{ formatAmountBySymbol(journal.pc_amount*-1, primaryCurrency.symbol, primaryCurrency.decimal_places) }})
{% elseif first.transaction_type_type == 'Deposit' %}
({{ formatAmountBySymbol(journal.pc_amount, primaryCurrency.symbol, primaryCurrency.decimal_places) }})
{% elseif first.transaction_type_type == 'Transfer' %}
<span class="text-info money-transfer">
({{ formatAmountBySymbol(journal.pc_amount, primaryCurrency.symbol, primaryCurrency.decimal_places, false) }})
</span>
{% endif %}
{% endif %}
<!-- do foreign amount --> <!-- do foreign amount -->
{% if null != journal.foreign_amount %} {% if null != journal.foreign_amount %}
{% if first.transactiontype.type == 'Withdrawal' %} {% if first.transaction_type_type == 'Withdrawal' %}
({{ formatAmountBySymbol(journal.foreign_amount*-1, journal.foreign_currency_symbol, journal.foreign_currency_decimal_places) }}) ({{ formatAmountBySymbol(journal.foreign_amount*-1, journal.foreign_currency_symbol, journal.foreign_currency_decimal_places) }})
{% elseif first.transactiontype.type == 'Deposit' %} {% elseif first.transaction_type_type == 'Deposit' %}
({{ formatAmountBySymbol(journal.foreign_amount, journal.foreign_currency_symbol, journal.foreign_currency_decimal_places) }}) ({{ formatAmountBySymbol(journal.foreign_amount, journal.foreign_currency_symbol, journal.foreign_currency_decimal_places) }})
{% elseif first.transactiontype.type == 'Transfer' %} {% elseif first.transaction_type_type == 'Transfer' %}
<span class="text-info money-transfer"> <span class="text-info money-transfer">
({{ formatAmountBySymbol(journal.foreign_amount, journal.foreign_currency_symbol, journal.foreign_currency_decimal_places, false) }}) ({{ formatAmountBySymbol(journal.foreign_amount, journal.foreign_currency_symbol, journal.foreign_currency_decimal_places, false) }})
</span> </span>
{% endif %} {% endif %}
{% endif %} {% endif %}
<!-- do foreign PC amount -->
{% if null != journal.pc_foreign_amount %}
{% if first.transaction_type_type == 'Withdrawal' %}
({{ formatAmountBySymbol(journal.pc_foreign_amount*-1, primaryCurrency.symbol, primaryCurrency.decimal_places) }})
{% elseif first.transaction_type_type == 'Deposit' %}
({{ formatAmountBySymbol(journal.pc_foreign_amount, primaryCurrency.symbol, primaryCurrency.decimal_places) }})
{% elseif first.transaction_type_type == 'Transfer' %}
<span class="text-info money-transfer">
({{ formatAmountBySymbol(journal.pc_foreign_amount, primaryCurrency.symbol, primaryCurrency.decimal_places, false) }})
</span>
{% endif %}
{% endif %}
&rarr; &rarr;
{% if 'Cash account' == journal.destination_type %} {% if 'Cash account' == journal.destination_account_type %}
<span class="text-success">({{ 'cash'|_ }})</span> <span class="text-success">({{ 'cash'|_ }})</span>
{% else %} {% else %}
<a href="{{ route('accounts.show', journal.destination_id) }}" <a href="{{ route('accounts.show', journal.destination_account_id) }}"
title="{{ journal.destination_iban|default(journal.destination_name) }}">{{ journal.destination_name }}</a> title="{{ journal.destination_iban|default(journal.destination_account_name) }}">{{ journal.destination_account_name }}</a>
{% endif %} {% endif %}
</td> </td>
</tr> </tr>
@@ -346,7 +372,7 @@
</td> </td>
</tr> </tr>
{% endif %} {% endif %}
{% if null != journal.budget_id and first.transactiontype.type == 'Withdrawal' %} {% if null != journal.budget_id and first.transaction_type_type == 'Withdrawal' %}
<tr> <tr>
<td style="width:40%;">{{ 'budget'|_ }}</td> <td style="width:40%;">{{ 'budget'|_ }}</td>
<td> <td>
@@ -354,7 +380,7 @@
</td> </td>
</tr> </tr>
{% endif %} {% endif %}
{% if null != journal.bill_id and first.transactiontype.type == 'Withdrawal' %} {% if null != journal.bill_id and first.transaction_type_type == 'Withdrawal' %}
<tr> <tr>
<td style="width:40%;">{{ 'bill'|_ }}</td> <td style="width:40%;">{{ 'bill'|_ }}</td>
<td> <td>

View File

@@ -24,255 +24,9 @@ declare(strict_types=1);
use Illuminate\Support\Facades\Route; use Illuminate\Support\Facades\Route;
/*
*
* ____ ____ ___ .______ ______ __ __ .___________. _______ _______.
* \ \ / / |__ \ | _ \ / __ \ | | | | | || ____| / |
* \ \/ / ) | | |_) | | | | | | | | | `---| |----`| |__ | (----`
* \ / / / | / | | | | | | | | | | | __| \ \
* \ / / /_ | |\ \----.| `--' | | `--' | | | | |____.----) |
* \__/ |____| | _| `._____| \______/ \______/ |__| |_______|_______/
*/
// AUTOCOMPLETE ROUTES
Route::group(
[
'namespace' => 'FireflyIII\Api\V2\Controllers\Autocomplete',
'prefix' => 'v2/autocomplete',
'as' => 'api.v2.autocomplete.',
],
static function (): void {
Route::get('accounts', ['uses' => 'AccountController@accounts', 'as' => 'accounts']);
// Route::get('categories', ['uses' => 'CategoryController@categories', 'as' => 'categories']);
// Route::get('tags', ['uses' => 'TagController@tags', 'as' => 'tags']);
// Route::get('transaction-descriptions', ['uses' => 'TransactionController@transactionDescriptions', 'as' => 'transaction-descriptions']);
}
);
// USER GROUP ROUTES
Route::group(
[
'namespace' => 'FireflyIII\Api\V2\Controllers\UserGroup',
'prefix' => 'v2/user-groups',
'as' => 'api.v2.user-groups.',
],
static function (): void {
Route::get('', ['uses' => 'IndexController@index', 'as' => 'index']);
Route::post('', ['uses' => 'StoreController@store', 'as' => 'store']);
Route::get('{userGroup}', ['uses' => 'ShowController@show', 'as' => 'show']);
// Route::put('{userGroup}', ['uses' => 'UpdateController@update', 'as' => 'update']);
// Route::post('{userGroup}/use', ['uses' => 'UpdateController@useUserGroup', 'as' => 'use']);
// Route::put('{userGroup}/update-membership', ['uses' => 'UpdateController@updateMembership', 'as' => 'updateMembership']);
// Route::delete('{userGroup}', ['uses' => 'DestroyController@destroy', 'as' => 'destroy']);
}
);
// CHART ROUTES
Route::group(
[
'namespace' => 'FireflyIII\Api\V2\Controllers\Chart',
'prefix' => 'v2/chart',
'as' => 'api.v2.chart.',
],
static function (): void {
// Route::get('account/dashboard', ['uses' => 'AccountController@dashboard', 'as' => 'account.dashboard']);
// Route::get('budget/dashboard', ['uses' => 'BudgetController@dashboard', 'as' => 'budget.dashboard']);
// Route::get('category/dashboard', ['uses' => 'CategoryController@dashboard', 'as' => 'category.dashboard']);
Route::get('balance/balance', ['uses' => 'BalanceController@balance', 'as' => 'balance.balance']);
}
);
// CURRENCY ROUTES
Route::group(
[
'namespace' => 'FireflyIII\Api\V2\Controllers\Model\TransactionCurrency',
'prefix' => 'v2/currencies',
'as' => 'api.v2.currencies.',
],
static function (): void {
Route::get('', ['uses' => 'IndexController@index', 'as' => 'index']);
Route::get('{currency_code}', ['uses' => 'ShowController@show', 'as' => 'show']);
// Route::post('', ['uses' => 'StoreController@store', 'as' => 'store']);
//
// Route::put('{userGroup}', ['uses' => 'UpdateController@update', 'as' => 'update']);
// Route::post('{userGroup}/use', ['uses' => 'UpdateController@useUserGroup', 'as' => 'use']);
// Route::put('{userGroup}/update-membership', ['uses' => 'UpdateController@updateMembership', 'as' => 'updateMembership']);
// Route::delete('{userGroup}', ['uses' => 'DestroyController@destroy', 'as' => 'destroy']);
}
);
// V2 API route for Summary boxes
// BASIC
// Route::group(
// [
// 'namespace' => 'FireflyIII\Api\V2\Controllers\Summary',
// 'prefix' => 'v2/summary',
// 'as' => 'api.v2.summary.',
// ],
// static function (): void {
// // Route::get('basic', ['uses' => 'BasicController@basic', 'as' => 'basic']);
// }
// );
// // V2 API route for all kinds of Transaction lists.
// // A lot of endpoints involve transactions. So any time Firefly III needs to list transactions
// // it's coming from these endpoints.
// Route::group(
// [
// 'namespace' => 'FireflyIII\Api\V2\Controllers\Transaction\List',
// 'prefix' => 'v2',
// 'as' => 'api.v2.',
// ],
// static function (): void {
// // basic list
// // Route::get('transactions', ['uses' => 'TransactionController@list', 'as' => 'transactions.list']);
//
// // list by parent or related object.
// // note how the check is done on the user group, not the user itself.
// // Route::get('accounts/{userGroupAccount}/transactions', ['uses' => 'AccountController@list', 'as' => 'accounts.transactions']);
// }
// );
// V2 API routes for auto complete
//
// // V2 API route for net worth endpoint(s);
// Route::group(
// [
// 'namespace' => 'FireflyIII\Api\V2\Controllers\Summary',
// 'prefix' => 'v2/net-worth',
// 'as' => 'api.v2.net-worth.',
// ],
// static function (): void {
// // Route::get('', ['uses' => 'NetWorthController@get', 'as' => 'index']);
// }
// );
//
// // // V2 API route for accounts.
// // Route::group(
// // [
// // 'namespace' => 'FireflyIII\Api\V2\Controllers\Model\Account',
// // 'prefix' => 'v2/accounts',
// // 'as' => 'api.v2.accounts.',
// // ],
// // static function (): void {
// // Route::get('', ['uses' => 'IndexController@index', 'as' => 'index']);
// // Route::get('{account}', ['uses' => 'ShowController@show', 'as' => 'show']);
// // Route::put('{account}', ['uses' => 'UpdateController@update', 'as' => 'update']);
// // }
// // );
//
// // V2 API route for subscriptions.
// Route::group(
// [
// 'namespace' => 'FireflyIII\Api\V2\Controllers\Model\Bill',
// 'prefix' => 'v2/subscriptions',
// 'as' => 'api.v2.subscriptions.',
// ],
// static function (): void {
// // Route::get('', ['uses' => 'IndexController@index', 'as' => 'index']);
// // Route::get('{userGroupBill}', ['uses' => 'ShowController@show', 'as' => 'show']);
// // Route::get('sum/paid', ['uses' => 'SumController@paid', 'as' => 'sum.paid']);
// // Route::get('sum/unpaid', ['uses' => 'SumController@unpaid', 'as' => 'sum.unpaid']);
// }
// );
//
// // V2 API route for piggy banks.
// Route::group(
// [
// 'namespace' => 'FireflyIII\Api\V2\Controllers\Model\PiggyBank',
// 'prefix' => 'v2/piggy-banks',
// 'as' => 'api.v2.piggy-banks.',
// ],
// static function (): void {
// // Route::get('', ['uses' => 'IndexController@index', 'as' => 'index']);
// }
// );
//
// // V2 API route for transaction currencies
// Route::group(
// [
// 'namespace' => 'FireflyIII\Api\V2\Controllers\Model\Currency',
// 'prefix' => 'v2/currencies',
// 'as' => 'api.v2.currencies.',
// ],
// static function (): void {
// // Route::get('', ['uses' => 'IndexController@index', 'as' => 'index']);
// }
// );
//
// // V2 API route for transactions
// Route::group(
// [
// 'namespace' => 'FireflyIII\Api\V2\Controllers\Model\Transaction',
// 'prefix' => 'v2/transactions',
// 'as' => 'api.v2.transactions.',
// ],
// static function (): void {
// // Route::post('', ['uses' => 'StoreController@post', 'as' => 'store']);
// // Route::get('{userGroupTransaction}', ['uses' => 'ShowController@show', 'as' => 'show']);
// // Route::put('{userGroupTransaction}', ['uses' => 'UpdateController@update', 'as' => 'update']);
// }
// );
// // infinite (transactions) list:
// Route::group(
// [
// 'namespace' => 'FireflyIII\Api\V2\Controllers\Transaction\List',
// 'prefix' => 'v2/infinite/transactions',
// 'as' => 'api.v2.infinite.transactions.',
// ],
// static function (): void {
// // Route::get('', ['uses' => 'TransactionController@infiniteList', 'as' => 'list']);
// }
// );
//
// // V2 API route for budgets and budget limits:
// Route::group(
// [
// 'namespace' => 'FireflyIII\Api\V2\Controllers\Model',
// 'prefix' => 'v2/budgets',
// 'as' => 'api.v2.budgets',
// ],
// static function (): void {
// // Route::get('', ['uses' => 'Budget\IndexController@index', 'as' => 'index']);
// // Route::get('{budget}', ['uses' => 'Budget\ShowController@show', 'as' => 'show']);
// // Route::get('{budget}/limits', ['uses' => 'BudgetLimit\IndexController@index', 'as' => 'budget-limits.index']);
// // Route::get('sum/budgeted', ['uses' => 'Budget\SumController@budgeted', 'as' => 'sum.budgeted']);
// // Route::get('sum/spent', ['uses' => 'Budget\SumController@spent', 'as' => 'sum.spent']);
// // Route::get('{budget}/budgeted', ['uses' => 'Budget\ShowController@budgeted', 'as' => 'budget.budgeted']);
// // Route::get('{budget}/spent', ['uses' => 'Budget\ShowController@spent', 'as' => 'budget.spent']);
// }
// );
//
// // V2 API route for system
// Route::group(
// [
// 'namespace' => 'FireflyIII\Api\V2\Controllers\System',
// 'prefix' => 'v2',
// 'as' => 'api.v2.system.',
// ],
// static function (): void {
// // Route::get('preferences/{preference}', ['uses' => 'PreferencesController@get', 'as' => 'preferences.get']);
// }
// );
//
// V2 JSON API ROUTES
// JsonApiRoute::server('v2')->prefix('v2')
// ->resources(function (ResourceRegistrar $server): void {
// // ACCOUNTS
// $server->resource('accounts', AccountController::class)
// ->relationships(function (Relationships $relations): void {
// $relations->hasOne('user')->readOnly();
// })
// ;
//
// // USERS
// $server->resource('users', JsonApiController::class)->readOnly()->relationships(function (Relationships $relations): void {
// $relations->hasMany('accounts')->readOnly();
// });
// })
// ;
/* /*
* ____ ____ __ .______ ______ __ __ .___________. _______ _______. * ____ ____ __ .______ ______ __ __ .___________. _______ _______.
@@ -330,6 +84,21 @@ Route::group(
); );
// CHART ROUTES. // CHART ROUTES.
// chart balance
// CHART ROUTES
Route::group(
[
'namespace' => 'FireflyIII\Api\V2\Controllers\Chart',
'prefix' => 'v1/chart/balance',
'as' => 'api.v1.chart.balance',
],
static function (): void {
Route::get('balance', ['uses' => 'BalanceController@balance', 'as' => 'balance.balance']);
}
);
// Chart accounts // Chart accounts
Route::group( Route::group(
[ [