mirror of
				https://github.com/firefly-iii/firefly-iii.git
				synced 2025-11-04 05:15:39 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			568 lines
		
	
	
		
			24 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
			
		
		
	
	
			568 lines
		
	
	
		
			24 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
<?php
 | 
						|
 | 
						|
/**
 | 
						|
 * AccountController.php
 | 
						|
 * Copyright (c) 2019 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\Http\Controllers\Chart;
 | 
						|
 | 
						|
use Carbon\Carbon;
 | 
						|
use FireflyIII\Exceptions\FireflyException;
 | 
						|
use FireflyIII\Generator\Chart\Basic\GeneratorInterface;
 | 
						|
use FireflyIII\Helpers\Collector\GroupCollectorInterface;
 | 
						|
use FireflyIII\Http\Controllers\Controller;
 | 
						|
use FireflyIII\Models\Account;
 | 
						|
use FireflyIII\Models\AccountType;
 | 
						|
use FireflyIII\Models\TransactionCurrency;
 | 
						|
use FireflyIII\Models\TransactionType;
 | 
						|
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
 | 
						|
use FireflyIII\Repositories\UserGroups\Currency\CurrencyRepositoryInterface;
 | 
						|
use FireflyIII\Support\CacheProperties;
 | 
						|
use FireflyIII\Support\Http\Controllers\AugumentData;
 | 
						|
use FireflyIII\Support\Http\Controllers\ChartGeneration;
 | 
						|
use FireflyIII\Support\Http\Controllers\DateCalculation;
 | 
						|
use Illuminate\Http\JsonResponse;
 | 
						|
use Illuminate\Support\Collection;
 | 
						|
 | 
						|
/**
 | 
						|
 * Class AccountController.
 | 
						|
 */
 | 
						|
class AccountController extends Controller
 | 
						|
{
 | 
						|
    use AugumentData;
 | 
						|
    use ChartGeneration;
 | 
						|
    use DateCalculation;
 | 
						|
 | 
						|
    protected GeneratorInterface        $generator;
 | 
						|
    private AccountRepositoryInterface  $accountRepository;
 | 
						|
    private CurrencyRepositoryInterface $currencyRepository;
 | 
						|
 | 
						|
    /**
 | 
						|
     * AccountController constructor.
 | 
						|
     */
 | 
						|
    public function __construct()
 | 
						|
    {
 | 
						|
        parent::__construct();
 | 
						|
 | 
						|
        $this->middleware(
 | 
						|
            function ($request, $next) {
 | 
						|
                $this->generator          = app(GeneratorInterface::class);
 | 
						|
                $this->accountRepository  = app(AccountRepositoryInterface::class);
 | 
						|
                $this->currencyRepository = app(CurrencyRepositoryInterface::class);
 | 
						|
 | 
						|
                return $next($request);
 | 
						|
            }
 | 
						|
        );
 | 
						|
    }
 | 
						|
 | 
						|
    /**
 | 
						|
     * Shows the balances for all the user's expense accounts (on the front page).
 | 
						|
     *
 | 
						|
     * This chart is (multi) currency aware.
 | 
						|
     */
 | 
						|
    public function expenseAccounts(): JsonResponse
 | 
						|
    {
 | 
						|
        /** @var Carbon $start */
 | 
						|
        $start         = clone session('start', today(config('app.timezone'))->startOfMonth());
 | 
						|
 | 
						|
        /** @var Carbon $end */
 | 
						|
        $end           = clone session('end', today(config('app.timezone'))->endOfMonth());
 | 
						|
        $cache         = new CacheProperties();
 | 
						|
        $cache->addProperty($start);
 | 
						|
        $cache->addProperty($end);
 | 
						|
        $cache->addProperty('chart.account.expense-accounts');
 | 
						|
        if ($cache->has()) {
 | 
						|
            return response()->json($cache->get());
 | 
						|
        }
 | 
						|
        $start->subDay();
 | 
						|
 | 
						|
        // prep some vars:
 | 
						|
        $currencies    = [];
 | 
						|
        $chartData     = [];
 | 
						|
        $tempData      = [];
 | 
						|
 | 
						|
        // grab all accounts and names
 | 
						|
        $accounts      = $this->accountRepository->getAccountsByType([AccountType::EXPENSE]);
 | 
						|
        $accountNames  = $this->extractNames($accounts);
 | 
						|
 | 
						|
        // grab all balances
 | 
						|
        $startBalances = app('steam')->balancesPerCurrencyByAccounts($accounts, $start);
 | 
						|
        $endBalances   = app('steam')->balancesPerCurrencyByAccounts($accounts, $end);
 | 
						|
 | 
						|
        // loop the end balances. This is an array for each account ($expenses)
 | 
						|
        foreach ($endBalances as $accountId => $expenses) {
 | 
						|
            $accountId = (int)$accountId;
 | 
						|
            // loop each expense entry (each entry can be a different currency).
 | 
						|
            foreach ($expenses as $currencyId => $endAmount) {
 | 
						|
                $currencyId  = (int)$currencyId;
 | 
						|
 | 
						|
                // see if there is an accompanying start amount.
 | 
						|
                // grab the difference and find the currency.
 | 
						|
                $startAmount = (string)($startBalances[$accountId][$currencyId] ?? '0');
 | 
						|
                $diff        = bcsub((string)$endAmount, $startAmount);
 | 
						|
                $currencies[$currencyId] ??= $this->currencyRepository->find($currencyId);
 | 
						|
                if (0 !== bccomp($diff, '0')) {
 | 
						|
                    // store the values in a temporary array.
 | 
						|
                    $tempData[] = [
 | 
						|
                        'name'        => $accountNames[$accountId],
 | 
						|
                        'difference'  => $diff,
 | 
						|
                        'diff_float'  => (float)$diff, // intentional float
 | 
						|
                        'currency_id' => $currencyId,
 | 
						|
                    ];
 | 
						|
                }
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
        // sort temp array by amount.
 | 
						|
        $amounts       = array_column($tempData, 'diff_float');
 | 
						|
        array_multisort($amounts, SORT_DESC, $tempData);
 | 
						|
 | 
						|
        // loop all found currencies and build the data array for the chart.
 | 
						|
        /**
 | 
						|
         * @var int                 $currencyId
 | 
						|
         * @var TransactionCurrency $currency
 | 
						|
         */
 | 
						|
        foreach ($currencies as $currencyId => $currency) {
 | 
						|
            $dataSet
 | 
						|
                                    = [
 | 
						|
                                        'label'           => (string)trans('firefly.spent'),
 | 
						|
                                        'type'            => 'bar',
 | 
						|
                                        'currency_symbol' => $currency->symbol,
 | 
						|
                                        'currency_code'   => $currency->code,
 | 
						|
                                        'entries'         => $this->expandNames($tempData),
 | 
						|
                                    ];
 | 
						|
            $chartData[$currencyId] = $dataSet;
 | 
						|
        }
 | 
						|
 | 
						|
        // loop temp data and place data in correct array:
 | 
						|
        foreach ($tempData as $entry) {
 | 
						|
            $currencyId                               = $entry['currency_id'];
 | 
						|
            $name                                     = $entry['name'];
 | 
						|
            $chartData[$currencyId]['entries'][$name] = $entry['difference'];
 | 
						|
        }
 | 
						|
 | 
						|
        $data          = $this->generator->multiSet($chartData);
 | 
						|
        $cache->store($data);
 | 
						|
 | 
						|
        return response()->json($data);
 | 
						|
    }
 | 
						|
 | 
						|
    /**
 | 
						|
     * Expenses per budget for all time, as shown on account overview.
 | 
						|
     */
 | 
						|
    public function expenseBudgetAll(AccountRepositoryInterface $repository, Account $account): JsonResponse
 | 
						|
    {
 | 
						|
        $start = $repository->oldestJournalDate($account) ?? today(config('app.timezone'))->startOfMonth();
 | 
						|
        $end   = today(config('app.timezone'));
 | 
						|
 | 
						|
        return $this->expenseBudget($account, $start, $end);
 | 
						|
    }
 | 
						|
 | 
						|
    /**
 | 
						|
     * Expenses per budget, as shown on account overview.
 | 
						|
     */
 | 
						|
    public function expenseBudget(Account $account, Carbon $start, Carbon $end): JsonResponse
 | 
						|
    {
 | 
						|
        $cache     = new CacheProperties();
 | 
						|
        $cache->addProperty($account->id);
 | 
						|
        $cache->addProperty($start);
 | 
						|
        $cache->addProperty($end);
 | 
						|
        $cache->addProperty('chart.account.expense-budget');
 | 
						|
        if ($cache->has()) {
 | 
						|
            return response()->json($cache->get());
 | 
						|
        }
 | 
						|
 | 
						|
        /** @var GroupCollectorInterface $collector */
 | 
						|
        $collector = app(GroupCollectorInterface::class);
 | 
						|
        $collector->setAccounts(new Collection([$account]))->setRange($start, $end)->withBudgetInformation()->setTypes([TransactionType::WITHDRAWAL]);
 | 
						|
        $journals  = $collector->getExtractedJournals();
 | 
						|
        $chartData = [];
 | 
						|
        $result    = [];
 | 
						|
        $budgetIds = [];
 | 
						|
 | 
						|
        /** @var array $journal */
 | 
						|
        foreach ($journals as $journal) {
 | 
						|
            $budgetId              = (int)$journal['budget_id'];
 | 
						|
            $key                   = sprintf('%d-%d', $budgetId, $journal['currency_id']);
 | 
						|
            $budgetIds[]           = $budgetId;
 | 
						|
            if (!array_key_exists($key, $result)) {
 | 
						|
                $result[$key] = [
 | 
						|
                    'total'           => '0',
 | 
						|
                    'budget_id'       => $budgetId,
 | 
						|
                    'currency_name'   => $journal['currency_name'],
 | 
						|
                    'currency_symbol' => $journal['currency_symbol'],
 | 
						|
                    'currency_code'   => $journal['currency_code'],
 | 
						|
                ];
 | 
						|
            }
 | 
						|
            $result[$key]['total'] = bcadd($journal['amount'], $result[$key]['total']);
 | 
						|
        }
 | 
						|
 | 
						|
        $names     = $this->getBudgetNames($budgetIds);
 | 
						|
 | 
						|
        foreach ($result as $row) {
 | 
						|
            $budgetId          = $row['budget_id'];
 | 
						|
            $name              = $names[$budgetId];
 | 
						|
            $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']];
 | 
						|
        }
 | 
						|
 | 
						|
        $data      = $this->generator->multiCurrencyPieChart($chartData);
 | 
						|
        $cache->store($data);
 | 
						|
 | 
						|
        return response()->json($data);
 | 
						|
    }
 | 
						|
 | 
						|
    /**
 | 
						|
     * Expenses grouped by category for account.
 | 
						|
     */
 | 
						|
    public function expenseCategoryAll(AccountRepositoryInterface $repository, Account $account): JsonResponse
 | 
						|
    {
 | 
						|
        $start = $repository->oldestJournalDate($account) ?? today(config('app.timezone'))->startOfMonth();
 | 
						|
        $end   = today(config('app.timezone'));
 | 
						|
 | 
						|
        return $this->expenseCategory($account, $start, $end);
 | 
						|
    }
 | 
						|
 | 
						|
    /**
 | 
						|
     * Expenses per category for one single account.
 | 
						|
     */
 | 
						|
    public function expenseCategory(Account $account, Carbon $start, Carbon $end): JsonResponse
 | 
						|
    {
 | 
						|
        $cache     = new CacheProperties();
 | 
						|
        $cache->addProperty($account->id);
 | 
						|
        $cache->addProperty($start);
 | 
						|
        $cache->addProperty($end);
 | 
						|
        $cache->addProperty('chart.account.expense-category');
 | 
						|
        if ($cache->has()) {
 | 
						|
            return response()->json($cache->get());
 | 
						|
        }
 | 
						|
 | 
						|
        /** @var GroupCollectorInterface $collector */
 | 
						|
        $collector = app(GroupCollectorInterface::class);
 | 
						|
        $collector->setAccounts(new Collection([$account]))->setRange($start, $end)->withCategoryInformation()->setTypes([TransactionType::WITHDRAWAL]);
 | 
						|
        $journals  = $collector->getExtractedJournals();
 | 
						|
        $result    = [];
 | 
						|
        $chartData = [];
 | 
						|
 | 
						|
        /** @var array $journal */
 | 
						|
        foreach ($journals as $journal) {
 | 
						|
            $key                   = sprintf('%d-%d', $journal['category_id'], $journal['currency_id']);
 | 
						|
            if (!array_key_exists($key, $result)) {
 | 
						|
                $result[$key] = [
 | 
						|
                    'total'           => '0',
 | 
						|
                    'category_id'     => (int)$journal['category_id'],
 | 
						|
                    'currency_name'   => $journal['currency_name'],
 | 
						|
                    'currency_symbol' => $journal['currency_symbol'],
 | 
						|
                    'currency_code'   => $journal['currency_code'],
 | 
						|
                ];
 | 
						|
            }
 | 
						|
            $result[$key]['total'] = bcadd($journal['amount'], $result[$key]['total']);
 | 
						|
        }
 | 
						|
        $names     = $this->getCategoryNames(array_keys($result));
 | 
						|
 | 
						|
        foreach ($result as $row) {
 | 
						|
            $categoryId        = $row['category_id'];
 | 
						|
            $name              = $names[$categoryId] ?? '(unknown)';
 | 
						|
            $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']];
 | 
						|
        }
 | 
						|
 | 
						|
        $data      = $this->generator->multiCurrencyPieChart($chartData);
 | 
						|
        $cache->store($data);
 | 
						|
 | 
						|
        return response()->json($data);
 | 
						|
    }
 | 
						|
 | 
						|
    /**
 | 
						|
     * Shows the balances for all the user's frontpage accounts.
 | 
						|
     *
 | 
						|
     * @throws FireflyException
 | 
						|
     *                                              */
 | 
						|
    public function frontpage(AccountRepositoryInterface $repository): JsonResponse
 | 
						|
    {
 | 
						|
        $start          = clone session('start', today(config('app.timezone'))->startOfMonth());
 | 
						|
        $end            = clone session('end', today(config('app.timezone'))->endOfMonth());
 | 
						|
        $defaultSet     = $repository->getAccountsByType([AccountType::DEFAULT, AccountType::ASSET])->pluck('id')->toArray();
 | 
						|
        app('log')->debug('Default set is ', $defaultSet);
 | 
						|
        $frontpage      = app('preferences')->get('frontpageAccounts', $defaultSet);
 | 
						|
        $frontpageArray = !is_array($frontpage->data) ? [] : $frontpage->data;
 | 
						|
        app('log')->debug('Frontpage preference set is ', $frontpageArray);
 | 
						|
        if (0 === count($frontpageArray)) {
 | 
						|
            app('preferences')->set('frontpageAccounts', $defaultSet);
 | 
						|
            app('log')->debug('frontpage set is empty!');
 | 
						|
        }
 | 
						|
        $accounts       = $repository->getAccountsById($frontpageArray);
 | 
						|
 | 
						|
        return response()->json($this->accountBalanceChart($accounts, $start, $end));
 | 
						|
    }
 | 
						|
 | 
						|
    /**
 | 
						|
     * Shows the income grouped by category for an account, in all time.
 | 
						|
     */
 | 
						|
    public function incomeCategoryAll(AccountRepositoryInterface $repository, Account $account): JsonResponse
 | 
						|
    {
 | 
						|
        $start = $repository->oldestJournalDate($account) ?? today(config('app.timezone'))->startOfMonth();
 | 
						|
        $end   = today(config('app.timezone'));
 | 
						|
 | 
						|
        return $this->incomeCategory($account, $start, $end);
 | 
						|
    }
 | 
						|
 | 
						|
    /**
 | 
						|
     * Shows all income per account for each category.
 | 
						|
     */
 | 
						|
    public function incomeCategory(Account $account, Carbon $start, Carbon $end): JsonResponse
 | 
						|
    {
 | 
						|
        $cache     = new CacheProperties();
 | 
						|
        $cache->addProperty($account->id);
 | 
						|
        $cache->addProperty($start);
 | 
						|
        $cache->addProperty($end);
 | 
						|
        $cache->addProperty('chart.account.income-category');
 | 
						|
        if ($cache->has()) {
 | 
						|
            return response()->json($cache->get());
 | 
						|
        }
 | 
						|
 | 
						|
        // grab all journals:
 | 
						|
        /** @var GroupCollectorInterface $collector */
 | 
						|
        $collector = app(GroupCollectorInterface::class);
 | 
						|
 | 
						|
        $collector->setAccounts(new Collection([$account]))->setRange($start, $end)->withCategoryInformation()->setTypes([TransactionType::DEPOSIT]);
 | 
						|
        $journals  = $collector->getExtractedJournals();
 | 
						|
        $result    = [];
 | 
						|
        $chartData = [];
 | 
						|
 | 
						|
        /** @var array $journal */
 | 
						|
        foreach ($journals as $journal) {
 | 
						|
            $key                   = sprintf('%d-%d', $journal['category_id'], $journal['currency_id']);
 | 
						|
            if (!array_key_exists($key, $result)) {
 | 
						|
                $result[$key] = [
 | 
						|
                    'total'           => '0',
 | 
						|
                    'category_id'     => $journal['category_id'],
 | 
						|
                    'currency_name'   => $journal['currency_name'],
 | 
						|
                    'currency_symbol' => $journal['currency_symbol'],
 | 
						|
                    'currency_code'   => $journal['currency_code'],
 | 
						|
                ];
 | 
						|
            }
 | 
						|
            $result[$key]['total'] = bcadd($journal['amount'], $result[$key]['total']);
 | 
						|
        }
 | 
						|
 | 
						|
        $names     = $this->getCategoryNames(array_keys($result));
 | 
						|
        foreach ($result as $row) {
 | 
						|
            $categoryId        = $row['category_id'];
 | 
						|
            $name              = $names[$categoryId] ?? '(unknown)';
 | 
						|
            $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']];
 | 
						|
        }
 | 
						|
        $data      = $this->generator->multiCurrencyPieChart($chartData);
 | 
						|
        $cache->store($data);
 | 
						|
 | 
						|
        return response()->json($data);
 | 
						|
    }
 | 
						|
 | 
						|
    /**
 | 
						|
     * Shows overview of account during a single period.
 | 
						|
     *
 | 
						|
     * @throws FireflyException
 | 
						|
     */
 | 
						|
    public function period(Account $account, Carbon $start, Carbon $end): JsonResponse
 | 
						|
    {
 | 
						|
        $chartData  = [];
 | 
						|
        $cache      = new CacheProperties();
 | 
						|
        $cache->addProperty('chart.account.period');
 | 
						|
        $cache->addProperty($start);
 | 
						|
        $cache->addProperty($end);
 | 
						|
        $cache->addProperty($account->id);
 | 
						|
        if ($cache->has()) {
 | 
						|
            return response()->json($cache->get());
 | 
						|
        }
 | 
						|
        $currencies = $this->accountRepository->getUsedCurrencies($account);
 | 
						|
 | 
						|
        // if the account is not expense or revenue, just use the account's default currency.
 | 
						|
        if (!in_array($account->accountType->type, [AccountType::REVENUE, AccountType::EXPENSE], true)) {
 | 
						|
            $currencies = [$this->accountRepository->getAccountCurrency($account) ?? app('amount')->getDefaultCurrency()];
 | 
						|
        }
 | 
						|
 | 
						|
        /** @var TransactionCurrency $currency */
 | 
						|
        foreach ($currencies as $currency) {
 | 
						|
            $chartData[] = $this->periodByCurrency($start, $end, $account, $currency);
 | 
						|
        }
 | 
						|
 | 
						|
        $data       = $this->generator->multiSet($chartData);
 | 
						|
        $cache->store($data);
 | 
						|
 | 
						|
        return response()->json($data);
 | 
						|
    }
 | 
						|
 | 
						|
    /**
 | 
						|
     * @throws FireflyException
 | 
						|
     */
 | 
						|
    private function periodByCurrency(Carbon $start, Carbon $end, Account $account, TransactionCurrency $currency): array
 | 
						|
    {
 | 
						|
        app('log')->debug(sprintf('Now in periodByCurrency("%s", "%s", %s, "%s")', $start->format('Y-m-d'), $end->format('Y-m-d'), $account->id, $currency->code));
 | 
						|
        $locale            = app('steam')->getLocale();
 | 
						|
        $step              = $this->calculateStep($start, $end);
 | 
						|
        $result            = [
 | 
						|
            'label'           => sprintf('%s (%s)', $account->name, $currency->symbol),
 | 
						|
            'currency_symbol' => $currency->symbol,
 | 
						|
            'currency_code'   => $currency->code,
 | 
						|
        ];
 | 
						|
        $entries           = [];
 | 
						|
        $current           = clone $start;
 | 
						|
        app('log')->debug(sprintf('Step is %s', $step));
 | 
						|
 | 
						|
        // 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.
 | 
						|
        // This period depends on the size of the chart
 | 
						|
        $current           = app('navigation')->endOfX($current, $step, null);
 | 
						|
        app('log')->debug(sprintf('$current date is %s', $current->format('Y-m-d')));
 | 
						|
        if ('1D' === $step) {
 | 
						|
            // per day the entire period, balance for every day.
 | 
						|
            $format   = (string)trans('config.month_and_day_js', [], $locale);
 | 
						|
            $range    = app('steam')->balanceInRange($account, $start, $end, $currency);
 | 
						|
            $previous = array_values($range)[0];
 | 
						|
            while ($end >= $current) {
 | 
						|
                $theDate         = $current->format('Y-m-d');
 | 
						|
                $balance         = $range[$theDate] ?? $previous;
 | 
						|
                $label           = $current->isoFormat($format);
 | 
						|
                $entries[$label] = (float)$balance;
 | 
						|
                $previous        = $balance;
 | 
						|
                $current->addDay();
 | 
						|
            }
 | 
						|
        }
 | 
						|
        if ('1W' === $step || '1M' === $step || '1Y' === $step) {
 | 
						|
            while ($end >= $current) {
 | 
						|
                app('log')->debug(sprintf('Current is: %s', $current->format('Y-m-d')));
 | 
						|
                $balance         = (float)app('steam')->balance($account, $current, $currency);
 | 
						|
                $label           = app('navigation')->periodShow($current, $step);
 | 
						|
                $entries[$label] = $balance;
 | 
						|
                $current         = app('navigation')->addPeriod($current, $step, 0);
 | 
						|
                // here too, to fix #8041, the data is corrected to the end of the period.
 | 
						|
                $current         = app('navigation')->endOfX($current, $step, null);
 | 
						|
            }
 | 
						|
        }
 | 
						|
        $result['entries'] = $entries;
 | 
						|
 | 
						|
        return $result;
 | 
						|
    }
 | 
						|
 | 
						|
    /**
 | 
						|
     * Shows the balances for a given set of dates and accounts.
 | 
						|
     *
 | 
						|
     * TODO this chart is not multi currency aware.
 | 
						|
     *
 | 
						|
     * @throws FireflyException
 | 
						|
     */
 | 
						|
    public function report(Collection $accounts, Carbon $start, Carbon $end): JsonResponse
 | 
						|
    {
 | 
						|
        return response()->json($this->accountBalanceChart($accounts, $start, $end));
 | 
						|
    }
 | 
						|
 | 
						|
    /**
 | 
						|
     * Shows the balances for all the user's revenue accounts.
 | 
						|
     *
 | 
						|
     * This chart is multi-currency aware.
 | 
						|
     */
 | 
						|
    public function revenueAccounts(): JsonResponse
 | 
						|
    {
 | 
						|
        /** @var Carbon $start */
 | 
						|
        $start         = clone session('start', today(config('app.timezone'))->startOfMonth());
 | 
						|
 | 
						|
        /** @var Carbon $end */
 | 
						|
        $end           = clone session('end', today(config('app.timezone'))->endOfMonth());
 | 
						|
        $cache         = new CacheProperties();
 | 
						|
        $cache->addProperty($start);
 | 
						|
        $cache->addProperty($end);
 | 
						|
        $cache->addProperty('chart.account.revenue-accounts');
 | 
						|
        if ($cache->has()) {
 | 
						|
            return response()->json($cache->get());
 | 
						|
        }
 | 
						|
        $start->subDay();
 | 
						|
 | 
						|
        // prep some vars:
 | 
						|
        $currencies    = [];
 | 
						|
        $chartData     = [];
 | 
						|
        $tempData      = [];
 | 
						|
 | 
						|
        // grab all accounts and names
 | 
						|
        $accounts      = $this->accountRepository->getAccountsByType([AccountType::REVENUE]);
 | 
						|
        $accountNames  = $this->extractNames($accounts);
 | 
						|
 | 
						|
        // grab all balances
 | 
						|
        $startBalances = app('steam')->balancesPerCurrencyByAccounts($accounts, $start);
 | 
						|
        $endBalances   = app('steam')->balancesPerCurrencyByAccounts($accounts, $end);
 | 
						|
 | 
						|
        // loop the end balances. This is an array for each account ($expenses)
 | 
						|
        foreach ($endBalances as $accountId => $expenses) {
 | 
						|
            $accountId = (int)$accountId;
 | 
						|
            // loop each expense entry (each entry can be a different currency).
 | 
						|
            foreach ($expenses as $currencyId => $endAmount) {
 | 
						|
                $currencyId  = (int)$currencyId;
 | 
						|
 | 
						|
                // see if there is an accompanying start amount.
 | 
						|
                // grab the difference and find the currency.
 | 
						|
                $startAmount = (string)($startBalances[$accountId][$currencyId] ?? '0');
 | 
						|
                $diff        = bcsub((string)$endAmount, $startAmount);
 | 
						|
                $currencies[$currencyId] ??= $this->currencyRepository->find($currencyId);
 | 
						|
                if (0 !== bccomp($diff, '0')) {
 | 
						|
                    // store the values in a temporary array.
 | 
						|
                    $tempData[] = [
 | 
						|
                        'name'        => $accountNames[$accountId],
 | 
						|
                        'difference'  => $diff,
 | 
						|
                        'diff_float'  => (float)$diff, // intentional float
 | 
						|
                        'currency_id' => $currencyId,
 | 
						|
                    ];
 | 
						|
                }
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
        // sort temp array by amount.
 | 
						|
        $amounts       = array_column($tempData, 'diff_float');
 | 
						|
        array_multisort($amounts, SORT_ASC, $tempData);
 | 
						|
 | 
						|
        // loop all found currencies and build the data array for the chart.
 | 
						|
        /**
 | 
						|
         * @var int                 $currencyId
 | 
						|
         * @var TransactionCurrency $currency
 | 
						|
         */
 | 
						|
        foreach ($currencies as $currencyId => $currency) {
 | 
						|
            $dataSet
 | 
						|
                                    = [
 | 
						|
                                        'label'           => (string)trans('firefly.earned'),
 | 
						|
                                        'type'            => 'bar',
 | 
						|
                                        'currency_symbol' => $currency->symbol,
 | 
						|
                                        'currency_code'   => $currency->code,
 | 
						|
                                        'entries'         => $this->expandNames($tempData),
 | 
						|
                                    ];
 | 
						|
            $chartData[$currencyId] = $dataSet;
 | 
						|
        }
 | 
						|
 | 
						|
        // loop temp data and place data in correct array:
 | 
						|
        foreach ($tempData as $entry) {
 | 
						|
            $currencyId                               = $entry['currency_id'];
 | 
						|
            $name                                     = $entry['name'];
 | 
						|
            $chartData[$currencyId]['entries'][$name] = bcmul($entry['difference'], '-1');
 | 
						|
        }
 | 
						|
 | 
						|
        $data          = $this->generator->multiSet($chartData);
 | 
						|
        $cache->store($data);
 | 
						|
 | 
						|
        return response()->json($data);
 | 
						|
    }
 | 
						|
}
 |