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;
|
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:.
|
* Will generate a (ChartJS) compatible array from the given input. Expects this format:.
|
||||||
*
|
*
|
||||||
|
@ -27,6 +27,13 @@ namespace FireflyIII\Generator\Chart\Basic;
|
|||||||
*/
|
*/
|
||||||
interface GeneratorInterface
|
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.
|
* 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.
|
* Shows the balances for all the user's frontpage accounts.
|
||||||
*
|
*
|
||||||
* TODO this chart is not multi-currency aware.
|
|
||||||
*
|
|
||||||
* @param AccountRepositoryInterface $repository
|
* @param AccountRepositoryInterface $repository
|
||||||
*
|
*
|
||||||
* @return JsonResponse
|
* @return JsonResponse
|
||||||
|
@ -29,6 +29,7 @@ use FireflyIII\Http\Controllers\Controller;
|
|||||||
use FireflyIII\Models\Bill;
|
use FireflyIII\Models\Bill;
|
||||||
use FireflyIII\Models\Transaction;
|
use FireflyIII\Models\Transaction;
|
||||||
use FireflyIII\Repositories\Bill\BillRepositoryInterface;
|
use FireflyIII\Repositories\Bill\BillRepositoryInterface;
|
||||||
|
use FireflyIII\Repositories\Currency\CurrencyRepositoryInterface;
|
||||||
use FireflyIII\Support\CacheProperties;
|
use FireflyIII\Support\CacheProperties;
|
||||||
use Illuminate\Http\JsonResponse;
|
use Illuminate\Http\JsonResponse;
|
||||||
use Illuminate\Support\Collection;
|
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).
|
* 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
|
* @param BillRepositoryInterface $repository
|
||||||
*
|
*
|
||||||
* @return JsonResponse
|
* @return JsonResponse
|
||||||
@ -69,17 +68,28 @@ class BillController extends Controller
|
|||||||
$cache->addProperty($end);
|
$cache->addProperty($end);
|
||||||
$cache->addProperty('chart.bill.frontpage');
|
$cache->addProperty('chart.bill.frontpage');
|
||||||
if ($cache->has()) {
|
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.
|
$data = $this->generator->multiCurrencyPieChart($chartData);
|
||||||
$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);
|
|
||||||
$cache->store($data);
|
$cache->store($data);
|
||||||
|
|
||||||
return response()->json($data);
|
return response()->json($data);
|
||||||
@ -93,8 +103,6 @@ class BillController extends Controller
|
|||||||
* @param Bill $bill
|
* @param Bill $bill
|
||||||
*
|
*
|
||||||
* @return JsonResponse
|
* @return JsonResponse
|
||||||
*
|
|
||||||
* TODO this chart is not multi-currency aware.
|
|
||||||
*/
|
*/
|
||||||
public function single(TransactionCollectorInterface $collector, Bill $bill): JsonResponse
|
public function single(TransactionCollectorInterface $collector, Bill $bill): JsonResponse
|
||||||
{
|
{
|
||||||
|
@ -199,6 +199,36 @@ class BillRepository implements BillRepositoryInterface
|
|||||||
return $sum;
|
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.
|
* 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;
|
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.
|
* Get all bills with these ID's.
|
||||||
*
|
*
|
||||||
|
@ -88,6 +88,17 @@ interface BillRepositoryInterface
|
|||||||
*/
|
*/
|
||||||
public function getBillsPaidInRange(Carbon $start, Carbon $end): string;
|
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.
|
* 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;
|
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.
|
* Get all bills with these ID's.
|
||||||
*
|
*
|
||||||
|
@ -163,6 +163,13 @@ class Steam
|
|||||||
|
|
||||||
$balances[$formatted] = $startBalance;
|
$balances[$formatted] = $startBalance;
|
||||||
$currencyId = (int)$repository->getMetaValue($account, 'currency_id');
|
$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();
|
$start->addDay();
|
||||||
|
|
||||||
// query!
|
// 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 = {
|
var defaultPieOptions = {
|
||||||
tooltips: {
|
tooltips: {
|
||||||
callbacks: {
|
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
|
* @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');
|
lineChart(accountFrontpageUri, 'accounts-chart');
|
||||||
|
|
||||||
if (billCount > 0) {
|
if (billCount > 0) {
|
||||||
pieChart('chart/bill/frontpage', 'bills-chart');
|
multiCurrencyPieChart('chart/bill/frontpage', 'bills-chart');
|
||||||
}
|
}
|
||||||
stackedColumnChart('chart/budget/frontpage', 'budgets-chart');
|
stackedColumnChart('chart/budget/frontpage', 'budgets-chart');
|
||||||
columnChart('chart/category/frontpage', 'categories-chart');
|
columnChart('chart/category/frontpage', 'categories-chart');
|
||||||
|
@ -183,7 +183,9 @@ return [
|
|||||||
'scopes_will_be_able' => 'This application will be able to:',
|
'scopes_will_be_able' => 'This application will be able to:',
|
||||||
'button_authorize' => 'Authorize',
|
'button_authorize' => 'Authorize',
|
||||||
'none_in_select_list' => '(none)',
|
'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:
|
// check for updates:
|
||||||
'update_check_title' => 'Check for updates',
|
'update_check_title' => 'Check for updates',
|
||||||
|
@ -104,7 +104,7 @@ try {
|
|||||||
$what = config('firefly.shortNamesByFullName.' . $account->accountType->type);
|
$what = config('firefly.shortNamesByFullName.' . $account->accountType->type);
|
||||||
|
|
||||||
$breadcrumbs->parent('accounts.index', $what);
|
$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) {
|
if (null !== $start && null !== $end) {
|
||||||
$title = trans(
|
$title = trans(
|
||||||
'firefly.between_dates_breadcrumb',
|
'firefly.between_dates_breadcrumb',
|
||||||
|
Loading…
Reference in New Issue
Block a user