This commit is contained in:
James Cole
2025-04-22 20:41:08 +02:00
parent dba8bba41a
commit 576c5f242c
3 changed files with 96 additions and 56 deletions

View File

@@ -43,6 +43,7 @@ use FireflyIII\Support\Http\Api\ExchangeRateConverter;
use FireflyIII\Support\Report\Summarizer\TransactionSummarizer; use FireflyIII\Support\Report\Summarizer\TransactionSummarizer;
use FireflyIII\User; use FireflyIII\User;
use Illuminate\Http\JsonResponse; use Illuminate\Http\JsonResponse;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Log; use Illuminate\Support\Facades\Log;
/** /**
@@ -104,10 +105,10 @@ class BasicController extends Controller
$billData = $this->getSubscriptionInformation($start, $end); $billData = $this->getSubscriptionInformation($start, $end);
$spentData = $this->getLeftToSpendInfo($start, $end); $spentData = $this->getLeftToSpendInfo($start, $end);
$netWorthData = $this->getNetWorthInfo($end); $netWorthData = $this->getNetWorthInfo($end);
// $balanceData = []; // $balanceData = [];
// $billData = []; // $billData = [];
// $spentData = []; // $spentData = [];
// $netWorthData = []; // $netWorthData = [];
$total = array_merge($balanceData, $billData, $spentData, $netWorthData); $total = array_merge($balanceData, $billData, $spentData, $netWorthData);
// give new keys // give new keys
@@ -539,26 +540,63 @@ class BasicController extends Controller
), ),
]; ];
} }
unset($leftToSpend);
if (0 === count($return)) { if (0 === count($return)) {
$currency = $this->nativeCurrency; // a small trick to get every expense in this period, regardless of budget.
$return[$currency->id] = [ $spent = $this->opsRepository->sumExpenses($start, $end, null, new Collection());
'key' => sprintf('left-to-spend-in-%s', $currency->code), foreach ($spent as $row) {
'title' => trans('firefly.box_left_to_spend_in_currency', ['currency' => $currency->symbol]), // either an amount was budgeted or 0 is available.
'monetary_value' => '0', $currencyId = (int) $row['currency_id'];
'no_available_budgets' => true, $spentInCurrency = $row['sum'];
'currency_id' => (string) $currency->id, $perDay = '0';
'currency_code' => $currency->code, if (0 !== $days && -1 === bccomp($spentInCurrency, '0')) {
'currency_symbol' => $currency->symbol, $perDay = bcdiv($spentInCurrency, (string) $days);
'currency_decimal_places' => $currency->decimal_places, }
'value_parsed' => app('amount')->formatFlat($currency->symbol, $currency->decimal_places, '0', false),
'local_icon' => 'money', Log::debug(sprintf('Spent %s %s', $row['currency_code'], $row['sum']));
'sub_title' => app('amount')->formatFlat(
$currency->symbol, $return[$currencyId] = [
$currency->decimal_places, 'key' => sprintf('left-to-spend-in-%s', $row['currency_code']),
'0', 'title' => trans('firefly.spent'),
false 'no_available_budgets' => true,
), 'monetary_value' => $spentInCurrency,
]; 'currency_id' => (string) $row['currency_id'],
'currency_code' => $row['currency_code'],
'currency_symbol' => $row['currency_symbol'],
'currency_decimal_places' => $row['currency_decimal_places'],
'value_parsed' => app('amount')->formatFlat($row['currency_symbol'], $row['currency_decimal_places'], $spentInCurrency, false),
'local_icon' => 'money',
'sub_title' => app('amount')->formatFlat(
$row['currency_symbol'],
$row['currency_decimal_places'],
$perDay,
false
),
];
}
// $amount = '0';
// // $days
// // fill in by money spent, just count it.
// $currency = $this->nativeCurrency;
// $return[$currency->id] = [
// 'key' => sprintf('left-to-spend-in-%s', $currency->code),
// 'title' => trans('firefly.box_left_to_spend_in_currency', ['currency' => $currency->symbol]),
// 'monetary_value' => '0',
// 'no_available_budgets' => true,
// 'currency_id' => (string) $currency->id,
// 'currency_code' => $currency->code,
// 'currency_symbol' => $currency->symbol,
// 'currency_decimal_places' => $currency->decimal_places,
// 'value_parsed' => app('amount')->formatFlat($currency->symbol, $currency->decimal_places, '0', false),
// 'local_icon' => 'money',
// 'sub_title' => app('amount')->formatFlat(
// $currency->symbol,
// $currency->decimal_places,
// '0',
// false
// ),
// ];
} }
return array_values($return); return array_values($return);

View File

@@ -31,7 +31,6 @@ use FireflyIII\Models\Account;
use FireflyIII\Models\Budget; use FireflyIII\Models\Budget;
use FireflyIII\Models\TransactionCurrency; use FireflyIII\Models\TransactionCurrency;
use FireflyIII\Repositories\Account\AccountRepositoryInterface; use FireflyIII\Repositories\Account\AccountRepositoryInterface;
use FireflyIII\Support\Facades\Amount;
use FireflyIII\Support\Report\Summarizer\TransactionSummarizer; use FireflyIII\Support\Report\Summarizer\TransactionSummarizer;
use FireflyIII\Support\Repositories\UserGroup\UserGroupInterface; use FireflyIII\Support\Repositories\UserGroup\UserGroupInterface;
use FireflyIII\Support\Repositories\UserGroup\UserGroupTrait; use FireflyIII\Support\Repositories\UserGroup\UserGroupTrait;
@@ -63,7 +62,7 @@ class OperationsRepository implements OperationsRepositoryInterface, UserGroupIn
++$count; ++$count;
app('log')->debug(sprintf('Found %d budget limits. Per day is %s, total is %s', $count, $perDay, $total)); app('log')->debug(sprintf('Found %d budget limits. Per day is %s, total is %s', $count, $perDay, $total));
} }
$avg = $total; $avg = $total;
if ($count > 0) { if ($count > 0) {
$avg = bcdiv($total, (string) $count); $avg = bcdiv($total, (string) $count);
} }
@@ -85,21 +84,21 @@ class OperationsRepository implements OperationsRepositoryInterface, UserGroupIn
// get all transactions: // get all transactions:
/** @var GroupCollectorInterface $collector */ /** @var GroupCollectorInterface $collector */
$collector = app(GroupCollectorInterface::class); $collector = app(GroupCollectorInterface::class);
$collector->setAccounts($accounts)->setRange($start, $end); $collector->setAccounts($accounts)->setRange($start, $end);
$collector->setBudgets($budgets); $collector->setBudgets($budgets);
$journals = $collector->getExtractedJournals(); $journals = $collector->getExtractedJournals();
// loop transactions: // loop transactions:
/** @var array $journal */ /** @var array $journal */
foreach ($journals as $journal) { foreach ($journals as $journal) {
// prep data array for currency: // prep data array for currency:
$budgetId = (int) $journal['budget_id']; $budgetId = (int) $journal['budget_id'];
$budgetName = $journal['budget_name']; $budgetName = $journal['budget_name'];
$currencyId = (int) $journal['currency_id']; $currencyId = (int) $journal['currency_id'];
$key = sprintf('%d-%d', $budgetId, $currencyId); $key = sprintf('%d-%d', $budgetId, $currencyId);
$data[$key] ??= [ $data[$key] ??= [
'id' => $budgetId, 'id' => $budgetId,
'name' => sprintf('%s (%s)', $budgetName, $journal['currency_name']), 'name' => sprintf('%s (%s)', $budgetName, $journal['currency_name']),
'sum' => '0', 'sum' => '0',
@@ -137,13 +136,13 @@ class OperationsRepository implements OperationsRepositoryInterface, UserGroupIn
$collector->setBudgets($this->getBudgets()); $collector->setBudgets($this->getBudgets());
} }
$collector->withBudgetInformation()->withAccountInformation()->withCategoryInformation(); $collector->withBudgetInformation()->withAccountInformation()->withCategoryInformation();
$journals = $collector->getExtractedJournals(); $journals = $collector->getExtractedJournals();
$array = []; $array = [];
foreach ($journals as $journal) { foreach ($journals as $journal) {
$currencyId = (int) $journal['currency_id']; $currencyId = (int) $journal['currency_id'];
$budgetId = (int) $journal['budget_id']; $budgetId = (int) $journal['budget_id'];
$budgetName = (string) $journal['budget_name']; $budgetName = (string) $journal['budget_name'];
// catch "no category" entries. // catch "no category" entries.
if (0 === $budgetId) { if (0 === $budgetId) {
@@ -151,7 +150,7 @@ class OperationsRepository implements OperationsRepositoryInterface, UserGroupIn
} }
// info about the currency: // info about the currency:
$array[$currencyId] ??= [ $array[$currencyId] ??= [
'budgets' => [], 'budgets' => [],
'currency_id' => $currencyId, 'currency_id' => $currencyId,
'currency_name' => $journal['currency_name'], 'currency_name' => $journal['currency_name'],
@@ -203,8 +202,9 @@ class OperationsRepository implements OperationsRepositoryInterface, UserGroupIn
?Collection $accounts = null, ?Collection $accounts = null,
?Collection $budgets = null, ?Collection $budgets = null,
?TransactionCurrency $currency = null, ?TransactionCurrency $currency = null,
bool $convertToNative = false bool $convertToNative = false
): array { ): array
{
Log::debug(sprintf('Start of %s(date, date, array, array, "%s", "%s").', __METHOD__, $currency?->code, var_export($convertToNative, true))); Log::debug(sprintf('Start of %s(date, date, array, array, "%s", "%s").', __METHOD__, $currency?->code, var_export($convertToNative, true)));
// this collector excludes all transfers TO liabilities (which are also withdrawals) // this collector excludes all transfers TO liabilities (which are also withdrawals)
// because those expenses only become expenses once they move from the liability to the friend. // because those expenses only become expenses once they move from the liability to the friend.
@@ -212,8 +212,8 @@ class OperationsRepository implements OperationsRepositoryInterface, UserGroupIn
$repository = app(AccountRepositoryInterface::class); $repository = app(AccountRepositoryInterface::class);
$repository->setUser($this->user); $repository->setUser($this->user);
$subset = $repository->getAccountsByType(config('firefly.valid_liabilities')); $subset = $repository->getAccountsByType(config('firefly.valid_liabilities'));
$selection = new Collection(); $selection = new Collection();
/** @var Account $account */ /** @var Account $account */
foreach ($subset as $account) { foreach ($subset as $account) {
@@ -223,12 +223,11 @@ class OperationsRepository implements OperationsRepositoryInterface, UserGroupIn
} }
/** @var GroupCollectorInterface $collector */ /** @var GroupCollectorInterface $collector */
$collector = app(GroupCollectorInterface::class); $collector = app(GroupCollectorInterface::class);
$collector->setUser($this->user) $collector->setUser($this->user)
->setRange($start, $end) ->setRange($start, $end)
// ->excludeDestinationAccounts($selection) // ->excludeDestinationAccounts($selection)
->setTypes([TransactionTypeEnum::WITHDRAWAL->value]) ->setTypes([TransactionTypeEnum::WITHDRAWAL->value]);
;
if (null !== $accounts) { if (null !== $accounts) {
$collector->setAccounts($accounts); $collector->setAccounts($accounts);
@@ -240,8 +239,10 @@ class OperationsRepository implements OperationsRepositoryInterface, UserGroupIn
Log::debug(sprintf('Limit to normal currency %s', $currency->code)); Log::debug(sprintf('Limit to normal currency %s', $currency->code));
$collector->setNormalCurrency($currency); $collector->setNormalCurrency($currency);
} }
$collector->setBudgets($budgets); if ($budgets->count() > 0) {
$journals = $collector->getExtractedJournals(); $collector->setBudgets($budgets);
}
$journals = $collector->getExtractedJournals();
// same but for transactions in the foreign currency: // same but for transactions in the foreign currency:
if (null !== $currency) { if (null !== $currency) {

View File

@@ -62,6 +62,7 @@ function drawChart() {
// net worth // net worth
var net_worth = []; var net_worth = [];
var keepGreen = false; var keepGreen = false;
var makeBlue = false;
for (key in data) { for (key in data) {
// balance // balance
@@ -80,17 +81,14 @@ function drawChart() {
// left to spend // left to spend
if (key.substring(0, 17) === 'left-to-spend-in-') { if (key.substring(0, 17) === 'left-to-spend-in-') {
left_to_spend_top.push(data[key].value_parsed);
left_to_spend_bottom.push(data[key].sub_title);
if(true === data[key].no_available_budgets) { if(true === data[key].no_available_budgets) {
left_to_spend_top.push('---'); makeBlue = true;
left_to_spend_bottom.push('---'); $('#box-left-to-spend-text').text(data[key].title);
keepGreen = true;
} }
if(false === data[key].no_available_budgets) { if(false === data[key].no_available_budgets && parseFloat(data[key].monetary_value) > 0) {
left_to_spend_top.push(data[key].value_parsed); keepGreen = true;
left_to_spend_bottom.push(data[key].sub_title);
if (parseFloat(data[key].monetary_value) > 0) {
keepGreen = true;
}
} }
} }
@@ -102,6 +100,9 @@ function drawChart() {
if(!keepGreen) { if(!keepGreen) {
$('#box-left-to-spend-box').removeClass('bg-green-gradient').addClass('bg-red-gradient') $('#box-left-to-spend-box').removeClass('bg-green-gradient').addClass('bg-red-gradient')
} }
if(makeBlue) {
$('#box-left-to-spend-box').removeClass('bg-red-gradient').removeClass('bg-green-gradient').addClass('bg-blue-gradient')
}
// balance // balance
$('#box-balance-sums').html(balance_top.join(', ')); $('#box-balance-sums').html(balance_top.join(', '));