mirror of
https://github.com/firefly-iii/firefly-iii.git
synced 2025-02-25 18:45:27 -06:00
Make some charts multi-currency.
This commit is contained in:
parent
46136d94e9
commit
e1c829f4fa
@ -140,6 +140,46 @@ class ChartJsGenerator implements GeneratorInterface
|
||||
return $chartData;
|
||||
}
|
||||
|
||||
/**
|
||||
* Expects data as:.
|
||||
*
|
||||
* key => [value => x, 'currency_symbol' => 'x']
|
||||
*
|
||||
* @param array $data
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function multiCurrencyPieChart(array $data): array
|
||||
{
|
||||
$chartData = [
|
||||
'datasets' => [
|
||||
0 => [],
|
||||
],
|
||||
'labels' => [],
|
||||
];
|
||||
|
||||
$amounts = array_column($data, 'amount');
|
||||
$next = next($amounts);
|
||||
$sortFlag = SORT_ASC;
|
||||
if (!\is_bool($next) && 1 === bccomp((string)$next, '0')) {
|
||||
$sortFlag = SORT_DESC;
|
||||
}
|
||||
array_multisort($amounts, $sortFlag, $data);
|
||||
unset($next, $sortFlag, $amounts);
|
||||
|
||||
$index = 0;
|
||||
foreach ($data as $key => $valueArray) {
|
||||
// make larger than 0
|
||||
$chartData['datasets'][0]['data'][] = (float)app('steam')->positive((string)$valueArray['amount']);
|
||||
$chartData['datasets'][0]['backgroundColor'][] = ChartColour::getColour($index);
|
||||
$chartData['datasets'][0]['currency_symbol'][] = $valueArray['currency_symbol'];
|
||||
$chartData['labels'][] = $key;
|
||||
++$index;
|
||||
}
|
||||
|
||||
return $chartData;
|
||||
}
|
||||
|
||||
/**
|
||||
* Will generate a (ChartJS) compatible array from the given input. Expects this format:.
|
||||
*
|
||||
|
@ -27,6 +27,13 @@ namespace FireflyIII\Generator\Chart\Basic;
|
||||
*/
|
||||
interface GeneratorInterface
|
||||
{
|
||||
/**
|
||||
* @param array $data
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function multiCurrencyPieChart(array $data): array;
|
||||
|
||||
/**
|
||||
* Will generate a Chart JS compatible array from the given input. Expects this format.
|
||||
*
|
||||
|
@ -329,8 +329,6 @@ class AccountController extends Controller
|
||||
/**
|
||||
* Shows the balances for all the user's frontpage accounts.
|
||||
*
|
||||
* TODO this chart is not multi-currency aware.
|
||||
*
|
||||
* @param AccountRepositoryInterface $repository
|
||||
*
|
||||
* @return JsonResponse
|
||||
|
@ -29,6 +29,7 @@ use FireflyIII\Http\Controllers\Controller;
|
||||
use FireflyIII\Models\Bill;
|
||||
use FireflyIII\Models\Transaction;
|
||||
use FireflyIII\Repositories\Bill\BillRepositoryInterface;
|
||||
use FireflyIII\Repositories\Currency\CurrencyRepositoryInterface;
|
||||
use FireflyIII\Support\CacheProperties;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Support\Collection;
|
||||
@ -54,8 +55,6 @@ class BillController extends Controller
|
||||
/**
|
||||
* Shows all bills and whether or not they've been paid this month (pie chart).
|
||||
*
|
||||
* TODO this chart is not multi-currency aware.
|
||||
*
|
||||
* @param BillRepositoryInterface $repository
|
||||
*
|
||||
* @return JsonResponse
|
||||
@ -69,17 +68,28 @@ class BillController extends Controller
|
||||
$cache->addProperty($end);
|
||||
$cache->addProperty('chart.bill.frontpage');
|
||||
if ($cache->has()) {
|
||||
return response()->json($cache->get()); // @codeCoverageIgnore
|
||||
//return response()->json($cache->get()); // @codeCoverageIgnore
|
||||
}
|
||||
/** @var CurrencyRepositoryInterface $currencyRepository */
|
||||
$currencyRepository = app(CurrencyRepositoryInterface::class);
|
||||
|
||||
$chartData = [];
|
||||
$currencies = [];
|
||||
$paid = $repository->getBillsPaidInRangePerCurrency($start, $end); // will be a negative amount.
|
||||
$unpaid = $repository->getBillsUnpaidInRangePerCurrency($start, $end); // will be a positive amount.
|
||||
|
||||
foreach ($paid as $currencyId => $amount) {
|
||||
$currencies[$currencyId] = $currencies[$currencyId] ?? $currencyRepository->findNull($currencyId);
|
||||
$label = (string)trans('firefly.paid_in_currency', ['currency' => $currencies[$currencyId]->name]);
|
||||
$chartData[$label] = ['amount' => $amount, 'currency_symbol' => $currencies[$currencyId]->symbol];
|
||||
}
|
||||
foreach ($unpaid as $currencyId => $amount) {
|
||||
$currencies[$currencyId] = $currencies[$currencyId] ?? $currencyRepository->findNull($currencyId);
|
||||
$label = (string)trans('firefly.unpaid_in_currency', ['currency' => $currencies[$currencyId]->name]);
|
||||
$chartData[$label] = ['amount' => $amount, 'currency_symbol' => $currencies[$currencyId]->symbol];
|
||||
}
|
||||
|
||||
$paid = $repository->getBillsPaidInRange($start, $end); // will be a negative amount.
|
||||
$unpaid = $repository->getBillsUnpaidInRange($start, $end); // will be a positive amount.
|
||||
$chartData = [
|
||||
(string)trans('firefly.unpaid') => $unpaid,
|
||||
(string)trans('firefly.paid') => $paid,
|
||||
];
|
||||
|
||||
$data = $this->generator->pieChart($chartData);
|
||||
$data = $this->generator->multiCurrencyPieChart($chartData);
|
||||
$cache->store($data);
|
||||
|
||||
return response()->json($data);
|
||||
@ -93,8 +103,6 @@ class BillController extends Controller
|
||||
* @param Bill $bill
|
||||
*
|
||||
* @return JsonResponse
|
||||
*
|
||||
* TODO this chart is not multi-currency aware.
|
||||
*/
|
||||
public function single(TransactionCollectorInterface $collector, Bill $bill): JsonResponse
|
||||
{
|
||||
|
@ -199,6 +199,36 @@ class BillRepository implements BillRepositoryInterface
|
||||
return $sum;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the total amount of money paid for the users active bills in the date range given,
|
||||
* grouped per currency.
|
||||
*
|
||||
* @param Carbon $start
|
||||
* @param Carbon $end
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getBillsPaidInRangePerCurrency(Carbon $start, Carbon $end): array
|
||||
{
|
||||
$bills = $this->getActiveBills();
|
||||
$return = [];
|
||||
/** @var Bill $bill */
|
||||
foreach ($bills as $bill) {
|
||||
/** @var Collection $set */
|
||||
$set = $bill->transactionJournals()->after($start)->before($end)->get(['transaction_journals.*']);
|
||||
$currencyId = (int)$bill->transaction_currency_id;
|
||||
if ($set->count() > 0) {
|
||||
$journalIds = $set->pluck('id')->toArray();
|
||||
$amount = (string)Transaction::whereIn('transaction_journal_id', $journalIds)->where('amount', '<', 0)->sum('amount');
|
||||
$return[$currencyId] = $return[$currencyId] ?? '0';
|
||||
$return[$currencyId] = bcadd($amount, $return[$currencyId]);
|
||||
Log::debug(sprintf('Total > 0, so add to sum %f, which becomes %f (currency %d)', $amount, $return[$currencyId], $currencyId));
|
||||
}
|
||||
}
|
||||
|
||||
return $return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the total amount of money due for the users active bills in the date range given. This amount will be positive.
|
||||
*
|
||||
@ -231,6 +261,40 @@ class BillRepository implements BillRepositoryInterface
|
||||
return $sum;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the total amount of money due for the users active bills in the date range given.
|
||||
*
|
||||
* @param Carbon $start
|
||||
* @param Carbon $end
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getBillsUnpaidInRangePerCurrency(Carbon $start, Carbon $end): array
|
||||
{
|
||||
$bills = $this->getActiveBills();
|
||||
$return = [];
|
||||
/** @var Bill $bill */
|
||||
foreach ($bills as $bill) {
|
||||
Log::debug(sprintf('Now at bill #%d (%s)', $bill->id, $bill->name));
|
||||
$dates = $this->getPayDatesInRange($bill, $start, $end);
|
||||
$count = $bill->transactionJournals()->after($start)->before($end)->count();
|
||||
$total = $dates->count() - $count;
|
||||
$currencyId = (int)$bill->transaction_currency_id;
|
||||
|
||||
Log::debug(sprintf('Dates = %d, journalCount = %d, total = %d', $dates->count(), $count, $total));
|
||||
|
||||
if ($total > 0) {
|
||||
$average = bcdiv(bcadd($bill->amount_max, $bill->amount_min), '2');
|
||||
$multi = bcmul($average, (string)$total);
|
||||
$return[$currencyId] = $return[$currencyId] ?? '0';
|
||||
$return[$currencyId] = bcadd($return[$currencyId], $multi);
|
||||
Log::debug(sprintf('Total > 0, so add to sum %f, which becomes %f (for currency %d)', $multi, $return[$currencyId], $currencyId));
|
||||
}
|
||||
}
|
||||
|
||||
return $return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all bills with these ID's.
|
||||
*
|
||||
|
@ -88,6 +88,17 @@ interface BillRepositoryInterface
|
||||
*/
|
||||
public function getBillsPaidInRange(Carbon $start, Carbon $end): string;
|
||||
|
||||
/**
|
||||
* Get the total amount of money paid for the users active bills in the date range given,
|
||||
* grouped per currency.
|
||||
*
|
||||
* @param Carbon $start
|
||||
* @param Carbon $end
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getBillsPaidInRangePerCurrency(Carbon $start, Carbon $end): array;
|
||||
|
||||
/**
|
||||
* Get the total amount of money due for the users active bills in the date range given.
|
||||
*
|
||||
@ -98,6 +109,16 @@ interface BillRepositoryInterface
|
||||
*/
|
||||
public function getBillsUnpaidInRange(Carbon $start, Carbon $end): string;
|
||||
|
||||
/**
|
||||
* Get the total amount of money due for the users active bills in the date range given.
|
||||
*
|
||||
* @param Carbon $start
|
||||
* @param Carbon $end
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getBillsUnpaidInRangePerCurrency(Carbon $start, Carbon $end): array;
|
||||
|
||||
/**
|
||||
* Get all bills with these ID's.
|
||||
*
|
||||
|
@ -163,6 +163,13 @@ class Steam
|
||||
|
||||
$balances[$formatted] = $startBalance;
|
||||
$currencyId = (int)$repository->getMetaValue($account, 'currency_id');
|
||||
|
||||
// use system default currency:
|
||||
if (0 === $currencyId) {
|
||||
$currency = app('amount')->getDefaultCurrencyByUser($account->user);
|
||||
$currencyId = $currency->id;
|
||||
}
|
||||
|
||||
$start->addDay();
|
||||
|
||||
// query!
|
||||
|
14
public/js/ff/charts.defaults.js
vendored
14
public/js/ff/charts.defaults.js
vendored
@ -117,6 +117,20 @@ var defaultChartOptions = {
|
||||
}
|
||||
};
|
||||
|
||||
var pieOptionsWithCurrency = {
|
||||
tooltips: {
|
||||
callbacks: {
|
||||
label: function (tooltipItem, data) {
|
||||
"use strict";
|
||||
var value = data.datasets[0].data[tooltipItem.index];
|
||||
return data.labels[tooltipItem.index] + ': ' + accounting.formatMoney(value, data.datasets[tooltipItem.datasetIndex].currency_symbol[tooltipItem.index]);
|
||||
}
|
||||
}
|
||||
},
|
||||
maintainAspectRatio: true,
|
||||
responsive: true
|
||||
};
|
||||
|
||||
var defaultPieOptions = {
|
||||
tooltips: {
|
||||
callbacks: {
|
||||
|
17
public/js/ff/charts.js
vendored
17
public/js/ff/charts.js
vendored
@ -257,6 +257,23 @@ function pieChart(URI, container) {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param URI
|
||||
* @param container
|
||||
*/
|
||||
function multiCurrencyPieChart(URI, container) {
|
||||
"use strict";
|
||||
|
||||
var colorData = false;
|
||||
var options = $.extend(true, {}, pieOptionsWithCurrency);
|
||||
var chartType = 'pie';
|
||||
|
||||
drawAChart(URI, container, chartType, options, colorData);
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
* @param URI
|
||||
|
2
public/js/ff/index.js
vendored
2
public/js/ff/index.js
vendored
@ -32,7 +32,7 @@ function drawChart() {
|
||||
lineChart(accountFrontpageUri, 'accounts-chart');
|
||||
|
||||
if (billCount > 0) {
|
||||
pieChart('chart/bill/frontpage', 'bills-chart');
|
||||
multiCurrencyPieChart('chart/bill/frontpage', 'bills-chart');
|
||||
}
|
||||
stackedColumnChart('chart/budget/frontpage', 'budgets-chart');
|
||||
columnChart('chart/category/frontpage', 'categories-chart');
|
||||
|
@ -183,7 +183,9 @@ return [
|
||||
'scopes_will_be_able' => 'This application will be able to:',
|
||||
'button_authorize' => 'Authorize',
|
||||
'none_in_select_list' => '(none)',
|
||||
'name_in_currency' => ':name in :currency',
|
||||
'name_in_currency' => ':name in :currency',
|
||||
'paid_in_currency' => 'Paid in :currency',
|
||||
'unpaid_in_currency' => 'Unpaid in :currency',
|
||||
|
||||
// check for updates:
|
||||
'update_check_title' => 'Check for updates',
|
||||
|
@ -104,7 +104,7 @@ try {
|
||||
$what = config('firefly.shortNamesByFullName.' . $account->accountType->type);
|
||||
|
||||
$breadcrumbs->parent('accounts.index', $what);
|
||||
$breadcrumbs->push(limitStringLength($account->name), route('accounts.show', [$account->id]));
|
||||
$breadcrumbs->push(limitStringLength($account->name), route('accounts.show.all', [$account->id]));
|
||||
if (null !== $start && null !== $end) {
|
||||
$title = trans(
|
||||
'firefly.between_dates_breadcrumb',
|
||||
|
Loading…
Reference in New Issue
Block a user