2016-01-27 20:54:14 +01:00
|
|
|
<?php
|
|
|
|
|
/**
|
|
|
|
|
* BudgetReportHelper.php
|
2017-10-21 08:40:00 +02:00
|
|
|
* Copyright (c) 2017 thegrumpydictator@gmail.com
|
2016-01-27 20:54:14 +01:00
|
|
|
*
|
2017-10-21 08:40:00 +02:00
|
|
|
* This file is part of Firefly III.
|
2016-10-05 06:52:15 +02:00
|
|
|
*
|
2017-10-21 08:40:00 +02:00
|
|
|
* Firefly III is free software: you can redistribute it and/or modify
|
|
|
|
|
* it under the terms of the GNU General Public License as published by
|
|
|
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
|
|
|
* (at your option) any later version.
|
|
|
|
|
*
|
|
|
|
|
* Firefly III 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 General Public License for more details.
|
|
|
|
|
*
|
|
|
|
|
* You should have received a copy of the GNU General Public License
|
2017-12-17 14:41:58 +01:00
|
|
|
* along with Firefly III. If not, see <http://www.gnu.org/licenses/>.
|
2016-01-27 20:54:14 +01:00
|
|
|
*/
|
2017-04-09 07:44:22 +02:00
|
|
|
declare(strict_types=1);
|
2016-05-20 12:27:31 +02:00
|
|
|
|
2016-01-27 20:54:14 +01:00
|
|
|
namespace FireflyIII\Helpers\Report;
|
|
|
|
|
|
|
|
|
|
use Carbon\Carbon;
|
2016-04-24 20:00:20 +02:00
|
|
|
use FireflyIII\Models\Budget;
|
2016-12-30 08:41:48 +01:00
|
|
|
use FireflyIII\Models\BudgetLimit;
|
2019-08-30 08:09:39 +02:00
|
|
|
use FireflyIII\Repositories\Budget\BudgetLimitRepositoryInterface;
|
2016-05-02 20:49:19 +02:00
|
|
|
use FireflyIII\Repositories\Budget\BudgetRepositoryInterface;
|
2019-08-30 09:13:10 +02:00
|
|
|
use FireflyIII\Repositories\Budget\NoBudgetRepositoryInterface;
|
2019-08-30 08:19:55 +02:00
|
|
|
use FireflyIII\Repositories\Budget\OperationsRepositoryInterface;
|
2016-01-27 20:54:14 +01:00
|
|
|
use Illuminate\Support\Collection;
|
2018-09-06 12:29:32 +02:00
|
|
|
use Log;
|
2016-01-27 20:54:14 +01:00
|
|
|
|
|
|
|
|
/**
|
2017-11-15 12:25:49 +01:00
|
|
|
* Class BudgetReportHelper.
|
2018-08-29 10:57:42 +02:00
|
|
|
*
|
|
|
|
|
* @codeCoverageIgnore
|
2016-01-27 20:54:14 +01:00
|
|
|
*/
|
|
|
|
|
class BudgetReportHelper implements BudgetReportHelperInterface
|
|
|
|
|
{
|
2019-08-30 08:09:39 +02:00
|
|
|
/** @var BudgetLimitRepositoryInterface */
|
|
|
|
|
private $blRepository;
|
2019-08-30 09:13:10 +02:00
|
|
|
/** @var NoBudgetRepositoryInterface */
|
|
|
|
|
private $noBudgetRepository;
|
2019-08-30 08:19:55 +02:00
|
|
|
/** @var OperationsRepositoryInterface */
|
|
|
|
|
private $opsRepository;
|
2018-07-07 23:14:16 +02:00
|
|
|
/** @var BudgetRepositoryInterface The budget repository interface. */
|
2016-05-18 07:01:27 +02:00
|
|
|
private $repository;
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* BudgetReportHelper constructor.
|
|
|
|
|
*/
|
2019-08-30 08:09:39 +02:00
|
|
|
public function __construct()
|
2016-05-18 07:01:27 +02:00
|
|
|
{
|
2019-08-30 09:13:10 +02:00
|
|
|
$this->repository = app(BudgetRepositoryInterface::class);
|
|
|
|
|
$this->blRepository = app(BudgetLimitRepositoryInterface::class);
|
|
|
|
|
$this->opsRepository = app(OperationsRepositoryInterface::class);
|
|
|
|
|
$this->noBudgetRepository = app(NoBudgetRepositoryInterface::class);
|
2018-12-15 07:59:02 +01:00
|
|
|
if ('testing' === config('app.env')) {
|
2019-06-07 18:20:15 +02:00
|
|
|
Log::warning(sprintf('%s should not be instantiated in the TEST environment!', get_class($this)));
|
2018-09-06 12:29:32 +02:00
|
|
|
}
|
|
|
|
|
|
2016-05-18 07:01:27 +02:00
|
|
|
}
|
2016-01-27 20:54:14 +01:00
|
|
|
|
2016-06-16 20:52:30 +02:00
|
|
|
/**
|
2018-07-07 23:14:16 +02:00
|
|
|
* Get the full budget report.
|
|
|
|
|
*
|
2019-08-16 08:27:08 +02:00
|
|
|
* TODO one big method is very complex.
|
|
|
|
|
*
|
2016-01-27 20:54:14 +01:00
|
|
|
* @param Carbon $start
|
|
|
|
|
* @param Carbon $end
|
|
|
|
|
* @param Collection $accounts
|
|
|
|
|
*
|
2017-02-11 10:05:58 +01:00
|
|
|
* @return array
|
2016-01-27 20:54:14 +01:00
|
|
|
*/
|
2017-02-11 10:05:58 +01:00
|
|
|
public function getBudgetReport(Carbon $start, Carbon $end, Collection $accounts): array
|
2016-01-27 20:54:14 +01:00
|
|
|
{
|
2017-02-11 10:05:58 +01:00
|
|
|
$set = $this->repository->getBudgets();
|
2019-08-16 08:27:08 +02:00
|
|
|
$array = [
|
|
|
|
|
'budgets' => [],
|
|
|
|
|
'sums' => [],
|
|
|
|
|
];
|
2016-01-27 20:54:14 +01:00
|
|
|
|
2016-05-06 06:15:46 +02:00
|
|
|
/** @var Budget $budget */
|
2016-01-27 20:54:14 +01:00
|
|
|
foreach ($set as $budget) {
|
2019-08-16 08:27:08 +02:00
|
|
|
$entry = [
|
|
|
|
|
'budget_id' => $budget->id,
|
|
|
|
|
'budget_name' => $budget->name,
|
|
|
|
|
'no_budget' => false,
|
|
|
|
|
'rows' => [],
|
|
|
|
|
];
|
|
|
|
|
// get multi currency expenses first:
|
2019-08-30 08:09:39 +02:00
|
|
|
$budgetLimits = $this->blRepository->getBudgetLimits($budget, $start, $end);
|
2019-08-30 08:19:55 +02:00
|
|
|
$expenses = $this->opsRepository->spentInPeriodMc(new Collection([$budget]), $accounts, $start, $end);
|
2019-08-21 18:07:15 +02:00
|
|
|
$defaultCurrency = app('amount')->getDefaultCurrencyByUser($budget->user);
|
2019-08-22 18:07:42 +02:00
|
|
|
Log::debug(sprintf('Default currency for getBudgetReport is %s', $defaultCurrency->code));
|
2019-08-16 08:27:08 +02:00
|
|
|
if (0 === count($expenses)) {
|
|
|
|
|
// list the budget limits, basic amounts.
|
|
|
|
|
/** @var BudgetLimit $limit */
|
|
|
|
|
foreach ($budgetLimits as $limit) {
|
2019-08-21 18:07:15 +02:00
|
|
|
$currency = $limit->transactionCurrency ?? $defaultCurrency;
|
2019-08-22 18:07:42 +02:00
|
|
|
Log::debug(sprintf('Default currency for limit #%d is %s', $limit->id, $currency->code));
|
|
|
|
|
$row = [
|
2019-08-16 08:27:08 +02:00
|
|
|
'limit_id' => $limit->id,
|
|
|
|
|
'start_date' => $limit->start_date,
|
|
|
|
|
'end_date' => $limit->end_date,
|
|
|
|
|
'budgeted' => $limit->amount,
|
|
|
|
|
'spent' => '0',
|
|
|
|
|
'left' => $limit->amount,
|
2019-09-03 16:56:46 +02:00
|
|
|
'overspent' => '0',
|
2019-08-21 18:07:15 +02:00
|
|
|
'currency_id' => $currency->id,
|
|
|
|
|
'currency_code' => $currency->code,
|
|
|
|
|
'currency_name' => $currency->name,
|
|
|
|
|
'currency_symbol' => $currency->symbol,
|
2019-08-22 18:07:33 +02:00
|
|
|
'currency_decimal_places' => $currency->decimal_places,
|
2017-02-11 10:05:58 +01:00
|
|
|
];
|
2019-08-16 08:27:08 +02:00
|
|
|
|
|
|
|
|
$entry['rows'][] = $row;
|
2016-02-04 08:53:56 +01:00
|
|
|
}
|
2016-01-27 20:54:14 +01:00
|
|
|
}
|
2019-08-16 08:27:08 +02:00
|
|
|
foreach ($expenses as $expense) {
|
|
|
|
|
$limit = $this->budgetLimitInCurrency($expense['currency_id'], $budgetLimits);
|
|
|
|
|
$row = [
|
|
|
|
|
'limit_id' => null,
|
|
|
|
|
'start_date' => null,
|
|
|
|
|
'end_date' => null,
|
|
|
|
|
'budgeted' => null,
|
|
|
|
|
'spent' => $expense['amount'],
|
|
|
|
|
'left' => null,
|
2019-09-03 16:56:46 +02:00
|
|
|
'overspent' => '0',
|
2019-08-16 08:27:08 +02:00
|
|
|
'currency_id' => $expense['currency_id'],
|
|
|
|
|
'currency_code' => $expense['currency_name'],
|
|
|
|
|
'currency_name' => $expense['currency_name'],
|
|
|
|
|
'currency_symbol' => $expense['currency_symbol'],
|
|
|
|
|
'currency_decimal_places' => $expense['currency_decimal_places'],
|
2017-02-11 10:05:58 +01:00
|
|
|
];
|
2019-08-16 08:27:08 +02:00
|
|
|
if (null !== $limit) {
|
|
|
|
|
// yes
|
|
|
|
|
$row['start_date'] = $limit->start_date;
|
|
|
|
|
$row['end_date'] = $limit->end_date;
|
|
|
|
|
$row['budgeted'] = $limit->amount;
|
|
|
|
|
$row['limit_id'] = $limit->id;
|
|
|
|
|
|
|
|
|
|
// less than zero? Set to 0.0
|
|
|
|
|
$row['left'] = -1 === bccomp(bcadd($limit->amount, $row['spent']), '0') ? '0' : bcadd($limit->amount, $row['spent']);
|
|
|
|
|
|
|
|
|
|
// spent > budgeted? then sum, otherwise other sum
|
2019-09-03 16:56:46 +02:00
|
|
|
$row['overspent'] = 1 === bccomp(bcmul($row['spent'],'-1'), $row['budgeted']) ? bcadd($row['spent'], $row['budgeted']) : '0';
|
2019-08-16 08:27:08 +02:00
|
|
|
}
|
|
|
|
|
$entry['rows'][] = $row;
|
2016-01-27 20:54:14 +01:00
|
|
|
}
|
2019-08-16 08:27:08 +02:00
|
|
|
$array['budgets'][] = $entry;
|
2016-01-27 20:54:14 +01:00
|
|
|
}
|
2019-08-30 09:13:10 +02:00
|
|
|
$noBudget = $this->noBudgetRepository->spentInPeriodWoBudgetMc($accounts, $start, $end);
|
2019-08-16 08:27:08 +02:00
|
|
|
$noBudgetEntry = [
|
|
|
|
|
'budget_id' => null,
|
|
|
|
|
'budget_name' => null,
|
|
|
|
|
'no_budget' => true,
|
|
|
|
|
'rows' => [],
|
2017-02-11 10:05:58 +01:00
|
|
|
];
|
2019-08-16 08:27:08 +02:00
|
|
|
foreach ($noBudget as $row) {
|
|
|
|
|
$noBudgetEntry['rows'][] = [
|
|
|
|
|
'limit_id' => null,
|
|
|
|
|
'start_date' => null,
|
|
|
|
|
'end_date' => null,
|
|
|
|
|
'budgeted' => null,
|
|
|
|
|
'spent' => $row['amount'],
|
|
|
|
|
'left' => null,
|
|
|
|
|
'overspent' => null,
|
|
|
|
|
'currency_id' => $row['currency_id'],
|
|
|
|
|
'currency_code' => $row['currency_code'],
|
|
|
|
|
'currency_name' => $row['currency_name'],
|
|
|
|
|
'currency_symbol' => $row['currency_symbol'],
|
|
|
|
|
'currency_decimal_places' => $row['currency_decimal_places'],
|
|
|
|
|
];
|
|
|
|
|
}
|
|
|
|
|
$array['budgets'][] = $noBudgetEntry;
|
2016-04-24 20:00:20 +02:00
|
|
|
|
2019-08-16 08:27:08 +02:00
|
|
|
// fill sums:
|
|
|
|
|
/** @var array $budget */
|
|
|
|
|
foreach ($array['budgets'] as $budget) {
|
|
|
|
|
/** @var array $row */
|
|
|
|
|
foreach ($budget['rows'] as $row) {
|
2019-08-21 18:07:15 +02:00
|
|
|
$currencyId = $row['currency_id'];
|
|
|
|
|
$array['sums'][$currencyId] = $array['sums'][$currencyId] ?? [
|
2019-08-16 08:27:08 +02:00
|
|
|
'currency_id' => $row['currency_id'],
|
|
|
|
|
'currency_code' => $row['currency_code'],
|
|
|
|
|
'currency_name' => $row['currency_name'],
|
|
|
|
|
'currency_symbol' => $row['currency_symbol'],
|
|
|
|
|
'currency_decimal_places' => $row['currency_decimal_places'],
|
|
|
|
|
'budgeted' => '0',
|
|
|
|
|
'spent' => '0',
|
|
|
|
|
'left' => '0',
|
|
|
|
|
'overspent' => '0',
|
|
|
|
|
];
|
2019-08-21 18:07:15 +02:00
|
|
|
$array['sums'][$currencyId]['budgeted'] = bcadd($array['sums'][$currencyId]['budgeted'], $row['budgeted'] ?? '0');
|
|
|
|
|
$array['sums'][$currencyId]['spent'] = bcadd($array['sums'][$currencyId]['spent'], $row['spent'] ?? '0');
|
|
|
|
|
$array['sums'][$currencyId]['left'] = bcadd($array['sums'][$currencyId]['left'], $row['left'] ?? '0');
|
2019-08-16 08:27:08 +02:00
|
|
|
$array['sums'][$currencyId]['overspent'] = bcadd($array['sums'][$currencyId]['overspent'], $row['overspent'] ?? '0');
|
2016-04-24 20:00:20 +02:00
|
|
|
}
|
|
|
|
|
}
|
2019-08-21 18:07:15 +02:00
|
|
|
|
2019-08-16 08:27:08 +02:00
|
|
|
return $array;
|
2016-04-24 20:00:20 +02:00
|
|
|
}
|
|
|
|
|
|
2016-05-18 07:01:27 +02:00
|
|
|
/**
|
2019-08-16 08:27:08 +02:00
|
|
|
* Returns from the collection the budget limit with the indicated currency ID
|
2018-07-08 07:59:58 +02:00
|
|
|
*
|
2019-08-16 08:27:08 +02:00
|
|
|
* @param int $currencyId
|
|
|
|
|
* @param Collection $budgetLimits
|
2016-05-18 07:01:27 +02:00
|
|
|
*
|
2019-08-16 08:27:08 +02:00
|
|
|
* @return BudgetLimit|null
|
2016-05-18 07:01:27 +02:00
|
|
|
*/
|
2019-08-16 08:27:08 +02:00
|
|
|
private function budgetLimitInCurrency(int $currencyId, Collection $budgetLimits): ?BudgetLimit
|
2016-05-18 07:01:27 +02:00
|
|
|
{
|
2019-08-16 08:27:08 +02:00
|
|
|
return $budgetLimits->first(
|
|
|
|
|
static function (BudgetLimit $limit) use ($currencyId) {
|
|
|
|
|
return $limit->transaction_currency_id === $currencyId;
|
|
|
|
|
}
|
|
|
|
|
);
|
2016-05-18 07:01:27 +02:00
|
|
|
}
|
2016-01-28 21:50:20 +01:00
|
|
|
}
|