Make some charts multi-currency.

This commit is contained in:
James Cole 2018-08-28 05:21:23 +02:00
parent 46136d94e9
commit e1c829f4fa
12 changed files with 196 additions and 18 deletions

View File

@ -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:.
*

View File

@ -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.
*

View File

@ -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

View File

@ -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
{

View File

@ -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.
*

View File

@ -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.
*

View File

@ -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!

View File

@ -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: {

View File

@ -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

View File

@ -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');

View File

@ -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',

View File

@ -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',