1 | <?php |
||||||
2 | |||||||
3 | /** |
||||||
4 | * SummaryController.php |
||||||
5 | * Copyright (c) 2019 [email protected] |
||||||
6 | * |
||||||
7 | * This file is part of Firefly III. |
||||||
8 | * |
||||||
9 | * Firefly III is free software: you can redistribute it and/or modify |
||||||
10 | * it under the terms of the GNU General Public License as published by |
||||||
11 | * the Free Software Foundation, either version 3 of the License, or |
||||||
12 | * (at your option) any later version. |
||||||
13 | * |
||||||
14 | * Firefly III is distributed in the hope that it will be useful, |
||||||
15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||||
16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||||
17 | * GNU General Public License for more details. |
||||||
18 | * |
||||||
19 | * You should have received a copy of the GNU General Public License |
||||||
20 | * along with Firefly III. If not, see <http://www.gnu.org/licenses/>. |
||||||
21 | */ |
||||||
22 | |||||||
23 | declare(strict_types=1); |
||||||
24 | |||||||
25 | namespace FireflyIII\Api\V1\Controllers; |
||||||
26 | |||||||
27 | |||||||
28 | use Carbon\Carbon; |
||||||
29 | use FireflyIII\Exceptions\FireflyException; |
||||||
30 | use FireflyIII\Helpers\Collector\TransactionCollectorInterface; |
||||||
31 | use FireflyIII\Helpers\Report\NetWorthInterface; |
||||||
32 | use FireflyIII\Models\Account; |
||||||
33 | use FireflyIII\Models\AccountType; |
||||||
34 | use FireflyIII\Models\Transaction; |
||||||
35 | use FireflyIII\Models\TransactionCurrency; |
||||||
36 | use FireflyIII\Models\TransactionType; |
||||||
37 | use FireflyIII\Repositories\Account\AccountRepositoryInterface; |
||||||
38 | use FireflyIII\Repositories\Bill\BillRepositoryInterface; |
||||||
39 | use FireflyIII\Repositories\Budget\BudgetRepositoryInterface; |
||||||
40 | use FireflyIII\Repositories\Currency\CurrencyRepositoryInterface; |
||||||
41 | use FireflyIII\User; |
||||||
42 | use Illuminate\Http\JsonResponse; |
||||||
43 | use Illuminate\Http\Request; |
||||||
44 | use Illuminate\Support\Collection; |
||||||
45 | |||||||
46 | /** |
||||||
47 | * Class SummaryController |
||||||
48 | */ |
||||||
49 | class SummaryController extends Controller |
||||||
50 | { |
||||||
51 | /** @var AccountRepositoryInterface */ |
||||||
52 | private $accountRepository; |
||||||
53 | /** @var BillRepositoryInterface */ |
||||||
54 | private $billRepository; |
||||||
55 | /** @var BudgetRepositoryInterface */ |
||||||
56 | private $budgetRepository; |
||||||
57 | /** @var CurrencyRepositoryInterface */ |
||||||
58 | private $currencyRepos; |
||||||
59 | |||||||
60 | /** |
||||||
61 | * AccountController constructor. |
||||||
62 | */ |
||||||
63 | public function __construct() |
||||||
64 | { |
||||||
65 | parent::__construct(); |
||||||
66 | $this->middleware( |
||||||
67 | function ($request, $next) { |
||||||
68 | /** @var User $user */ |
||||||
69 | $user = auth()->user(); |
||||||
70 | $this->currencyRepos = app(CurrencyRepositoryInterface::class); |
||||||
71 | $this->billRepository = app(BillRepositoryInterface::class); |
||||||
72 | $this->budgetRepository = app(BudgetRepositoryInterface::class); |
||||||
73 | $this->accountRepository = app(AccountRepositoryInterface::class); |
||||||
74 | |||||||
75 | $this->billRepository->setUser($user); |
||||||
76 | $this->currencyRepos->setUser($user); |
||||||
77 | $this->budgetRepository->setUser($user); |
||||||
78 | $this->accountRepository->setUser($user); |
||||||
79 | |||||||
80 | |||||||
81 | return $next($request); |
||||||
82 | } |
||||||
83 | ); |
||||||
84 | } |
||||||
85 | |||||||
86 | /** |
||||||
87 | * @param Request $request |
||||||
88 | * |
||||||
89 | * @return JsonResponse |
||||||
90 | * @throws FireflyException |
||||||
91 | */ |
||||||
92 | public function basic(Request $request): JsonResponse |
||||||
93 | { |
||||||
94 | // parameters for boxes: |
||||||
95 | $start = (string)$request->get('start'); |
||||||
96 | $end = (string)$request->get('end'); |
||||||
97 | if ('' === $start || '' === $end) { |
||||||
98 | throw new FireflyException('Start and end are mandatory parameters.'); |
||||||
99 | } |
||||||
100 | $start = Carbon::createFromFormat('Y-m-d', $start); |
||||||
101 | $end = Carbon::createFromFormat('Y-m-d', $end); |
||||||
102 | // balance information: |
||||||
103 | $balanceData = $this->getBalanceInformation($start, $end); |
||||||
0 ignored issues
–
show
Bug
introduced
by
Loading history...
It seems like
$end can also be of type false ; however, parameter $end of FireflyIII\Api\V1\Contro...getBalanceInformation() does only seem to accept Carbon\Carbon , maybe add an additional type check?
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
Loading history...
|
|||||||
104 | $billData = $this->getBillInformation($start, $end); |
||||||
0 ignored issues
–
show
It seems like
$start can also be of type false ; however, parameter $start of FireflyIII\Api\V1\Contro...r::getBillInformation() does only seem to accept Carbon\Carbon , maybe add an additional type check?
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
Loading history...
It seems like
$end can also be of type false ; however, parameter $end of FireflyIII\Api\V1\Contro...r::getBillInformation() does only seem to accept Carbon\Carbon , maybe add an additional type check?
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
Loading history...
|
|||||||
105 | $spentData = $this->getLeftToSpendInfo($start, $end); |
||||||
0 ignored issues
–
show
It seems like
$start can also be of type false ; however, parameter $start of FireflyIII\Api\V1\Contro...r::getLeftToSpendInfo() does only seem to accept Carbon\Carbon , maybe add an additional type check?
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
Loading history...
It seems like
$end can also be of type false ; however, parameter $end of FireflyIII\Api\V1\Contro...r::getLeftToSpendInfo() does only seem to accept Carbon\Carbon , maybe add an additional type check?
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
Loading history...
|
|||||||
106 | $networthData = $this->getNetWorthInfo($start, $end); |
||||||
0 ignored issues
–
show
It seems like
$start can also be of type false ; however, parameter $start of FireflyIII\Api\V1\Contro...ller::getNetWorthInfo() does only seem to accept Carbon\Carbon , maybe add an additional type check?
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
Loading history...
It seems like
$end can also be of type false ; however, parameter $end of FireflyIII\Api\V1\Contro...ller::getNetWorthInfo() does only seem to accept Carbon\Carbon , maybe add an additional type check?
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
Loading history...
|
|||||||
107 | $total = array_merge($balanceData, $billData, $spentData, $networthData); |
||||||
108 | |||||||
109 | // TODO: liabilities with icon line-chart |
||||||
110 | |||||||
111 | return response()->json($total); |
||||||
112 | |||||||
113 | } |
||||||
114 | |||||||
115 | /** |
||||||
116 | * Check if date is outside session range. |
||||||
117 | * |
||||||
118 | * @param Carbon $date |
||||||
119 | * |
||||||
120 | * @param Carbon $start |
||||||
121 | * @param Carbon $end |
||||||
122 | * |
||||||
123 | * @return bool |
||||||
124 | * @SuppressWarnings(PHPMD.CyclomaticComplexity) |
||||||
125 | */ |
||||||
126 | protected function notInDateRange(Carbon $date, Carbon $start, Carbon $end): bool // Validate a preference |
||||||
127 | { |
||||||
128 | $result = false; |
||||||
129 | if ($start->greaterThanOrEqualTo($date) && $end->greaterThanOrEqualTo($date)) { |
||||||
130 | $result = true; |
||||||
131 | } |
||||||
132 | // start and end in the past? use $end |
||||||
133 | if ($start->lessThanOrEqualTo($date) && $end->lessThanOrEqualTo($date)) { |
||||||
134 | $result = true; |
||||||
135 | } |
||||||
136 | |||||||
137 | return $result; |
||||||
138 | } |
||||||
139 | |||||||
140 | /** |
||||||
141 | * This method will scroll through the results of the spentInPeriodMc() array and return the correct info. |
||||||
142 | * |
||||||
143 | * @param array $spentInfo |
||||||
144 | * @param TransactionCurrency $currency |
||||||
145 | * |
||||||
146 | * @return float |
||||||
147 | */ |
||||||
148 | private function findInSpentArray(array $spentInfo, TransactionCurrency $currency): float |
||||||
149 | { |
||||||
150 | foreach ($spentInfo as $array) { |
||||||
151 | if ($array['currency_id'] === $currency->id) { |
||||||
152 | return $array['amount']; |
||||||
153 | } |
||||||
154 | } |
||||||
155 | |||||||
156 | return 0.0; |
||||||
157 | } |
||||||
158 | |||||||
159 | /** |
||||||
160 | * @param Carbon $start |
||||||
161 | * @param Carbon $end |
||||||
162 | * |
||||||
163 | * @return array |
||||||
164 | */ |
||||||
165 | private function getBalanceInformation(Carbon $start, Carbon $end): array |
||||||
166 | { |
||||||
167 | // prep some arrays: |
||||||
168 | $incomes = []; |
||||||
169 | $expenses = []; |
||||||
170 | $sums = []; |
||||||
171 | $return = []; |
||||||
172 | |||||||
173 | // collect income of user: |
||||||
174 | /** @var TransactionCollectorInterface $collector */ |
||||||
175 | $collector = app(TransactionCollectorInterface::class); |
||||||
176 | $collector->setAllAssetAccounts()->setRange($start, $end) |
||||||
177 | ->setTypes([TransactionType::DEPOSIT]) |
||||||
178 | ->withOpposingAccount(); |
||||||
179 | $set = $collector->getTransactions(); |
||||||
180 | /** @var Transaction $transaction */ |
||||||
181 | foreach ($set as $transaction) { |
||||||
182 | $currencyId = (int)$transaction->transaction_currency_id; |
||||||
183 | $incomes[$currencyId] = $incomes[$currencyId] ?? '0'; |
||||||
184 | $incomes[$currencyId] = bcadd($incomes[$currencyId], $transaction->transaction_amount); |
||||||
185 | $sums[$currencyId] = $sums[$currencyId] ?? '0'; |
||||||
186 | $sums[$currencyId] = bcadd($sums[$currencyId], $transaction->transaction_amount); |
||||||
187 | } |
||||||
188 | |||||||
189 | // collect expenses: |
||||||
190 | /** @var TransactionCollectorInterface $collector */ |
||||||
191 | $collector = app(TransactionCollectorInterface::class); |
||||||
192 | $collector->setAllAssetAccounts()->setRange($start, $end) |
||||||
193 | ->setTypes([TransactionType::WITHDRAWAL]) |
||||||
194 | ->withOpposingAccount(); |
||||||
195 | $set = $collector->getTransactions(); |
||||||
196 | /** @var Transaction $transaction */ |
||||||
197 | foreach ($set as $transaction) { |
||||||
198 | $currencyId = (int)$transaction->transaction_currency_id; |
||||||
199 | $expenses[$currencyId] = $expenses[$currencyId] ?? '0'; |
||||||
200 | $expenses[$currencyId] = bcadd($expenses[$currencyId], $transaction->transaction_amount); |
||||||
201 | $sums[$currencyId] = $sums[$currencyId] ?? '0'; |
||||||
202 | $sums[$currencyId] = bcadd($sums[$currencyId], $transaction->transaction_amount); |
||||||
203 | } |
||||||
204 | |||||||
205 | // format amounts: |
||||||
206 | $keys = array_keys($sums); |
||||||
207 | foreach ($keys as $currencyId) { |
||||||
208 | $currency = $this->currencyRepos->findNull($currencyId); |
||||||
209 | if (null === $currency) { |
||||||
210 | continue; |
||||||
211 | } |
||||||
212 | // create objects for big array. |
||||||
213 | $return[] = [ |
||||||
214 | 'key' => sprintf('balance-in-%s', $currency->code), |
||||||
215 | 'title' => trans('firefly.box_balance_in_currency', ['currency' => $currency->symbol]), |
||||||
216 | 'monetary_value' => round($sums[$currencyId] ?? 0, $currency->decimal_places), |
||||||
217 | 'currency_id' => $currency->id, |
||||||
218 | 'currency_code' => $currency->code, |
||||||
219 | 'currency_symbol' => $currency->symbol, |
||||||
220 | 'currency_decimal_places' => $currency->decimal_places, |
||||||
221 | 'value_parsed' => app('amount')->formatAnything($currency, $sums[$currencyId] ?? '0', false), |
||||||
222 | 'local_icon' => 'balance-scale', |
||||||
223 | 'sub_title' => app('amount')->formatAnything($currency, $expenses[$currencyId] ?? '0', false) . |
||||||
224 | ' + ' . app('amount')->formatAnything($currency, $incomes[$currencyId] ?? '0', false), |
||||||
225 | ]; |
||||||
226 | $return[] = [ |
||||||
227 | 'key' => sprintf('spent-in-%s', $currency->code), |
||||||
228 | 'title' => trans('firefly.box_spent_in_currency', ['currency' => $currency->symbol]), |
||||||
229 | 'monetary_value' => round($expenses[$currencyId] ?? 0, $currency->decimal_places), |
||||||
230 | 'currency_id' => $currency->id, |
||||||
231 | 'currency_code' => $currency->code, |
||||||
232 | 'currency_symbol' => $currency->symbol, |
||||||
233 | 'currency_decimal_places' => $currency->decimal_places, |
||||||
234 | 'value_parsed' => app('amount')->formatAnything($currency, $expenses[$currencyId] ?? '0', false), |
||||||
235 | 'local_icon' => 'balance-scale', |
||||||
236 | 'sub_title' => '', |
||||||
237 | ]; |
||||||
238 | $return[] = [ |
||||||
239 | 'key' => sprintf('earned-in-%s', $currency->code), |
||||||
240 | 'title' => trans('firefly.box_earned_in_currency', ['currency' => $currency->symbol]), |
||||||
241 | 'monetary_value' => round($incomes[$currencyId] ?? 0, $currency->decimal_places), |
||||||
242 | 'currency_id' => $currency->id, |
||||||
243 | 'currency_code' => $currency->code, |
||||||
244 | 'currency_symbol' => $currency->symbol, |
||||||
245 | 'currency_decimal_places' => $currency->decimal_places, |
||||||
246 | 'value_parsed' => app('amount')->formatAnything($currency, $incomes[$currencyId] ?? '0', false), |
||||||
247 | 'local_icon' => 'balance-scale', |
||||||
248 | 'sub_title' => '', |
||||||
249 | ]; |
||||||
250 | } |
||||||
251 | |||||||
252 | return $return; |
||||||
253 | } |
||||||
254 | |||||||
255 | /** |
||||||
256 | * @param Carbon $start |
||||||
257 | * @param Carbon $end |
||||||
258 | * |
||||||
259 | * @return array |
||||||
260 | */ |
||||||
261 | private function getBillInformation(Carbon $start, Carbon $end): array |
||||||
262 | { |
||||||
263 | /* |
||||||
264 | * Since both this method and the chart use the exact same data, we can suffice |
||||||
265 | * with calling the one method in the bill repository that will get this amount. |
||||||
266 | */ |
||||||
267 | $paidAmount = $this->billRepository->getBillsPaidInRangePerCurrency($start, $end); |
||||||
268 | $unpaidAmount = $this->billRepository->getBillsUnpaidInRangePerCurrency($start, $end); |
||||||
269 | $return = []; |
||||||
270 | foreach ($paidAmount as $currencyId => $amount) { |
||||||
271 | $amount = bcmul($amount, '-1'); |
||||||
272 | $currency = $this->currencyRepos->findNull((int)$currencyId); |
||||||
273 | if (null === $currency) { |
||||||
274 | continue; |
||||||
275 | } |
||||||
276 | $return[] = [ |
||||||
277 | 'key' => sprintf('bills-paid-in-%s', $currency->code), |
||||||
278 | 'title' => trans('firefly.box_bill_paid_in_currency', ['currency' => $currency->symbol]), |
||||||
279 | 'monetary_value' => round($amount, $currency->decimal_places), |
||||||
0 ignored issues
–
show
$amount of type string is incompatible with the type double expected by parameter $val of round() .
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
Loading history...
|
|||||||
280 | 'currency_id' => $currency->id, |
||||||
281 | 'currency_code' => $currency->code, |
||||||
282 | 'currency_symbol' => $currency->symbol, |
||||||
283 | 'currency_decimal_places' => $currency->decimal_places, |
||||||
284 | 'value_parsed' => app('amount')->formatAnything($currency, $amount, false), |
||||||
285 | 'local_icon' => 'check', |
||||||
286 | 'sub_title' => '', |
||||||
287 | ]; |
||||||
288 | } |
||||||
289 | |||||||
290 | foreach ($unpaidAmount as $currencyId => $amount) { |
||||||
291 | $amount = bcmul($amount, '-1'); |
||||||
292 | $currency = $this->currencyRepos->findNull((int)$currencyId); |
||||||
293 | if (null === $currency) { |
||||||
294 | continue; |
||||||
295 | } |
||||||
296 | $return[] = [ |
||||||
297 | 'key' => sprintf('bills-unpaid-in-%s', $currency->code), |
||||||
298 | 'title' => trans('firefly.box_bill_unpaid_in_currency', ['currency' => $currency->symbol]), |
||||||
299 | 'monetary_value' => round($amount, $currency->decimal_places), |
||||||
300 | 'currency_id' => $currency->id, |
||||||
301 | 'currency_code' => $currency->code, |
||||||
302 | 'currency_symbol' => $currency->symbol, |
||||||
303 | 'currency_decimal_places' => $currency->decimal_places, |
||||||
304 | 'value_parsed' => app('amount')->formatAnything($currency, $amount, false), |
||||||
305 | 'local_icon' => 'calendar-o', |
||||||
306 | 'sub_title' => '', |
||||||
307 | ]; |
||||||
308 | } |
||||||
309 | |||||||
310 | return $return; |
||||||
311 | } |
||||||
312 | |||||||
313 | /** |
||||||
314 | * @param Carbon $start |
||||||
315 | * @param Carbon $end |
||||||
316 | * |
||||||
317 | * @return array |
||||||
318 | */ |
||||||
319 | private function getLeftToSpendInfo(Carbon $start, Carbon $end): array |
||||||
320 | { |
||||||
321 | $return = []; |
||||||
322 | $today = new Carbon; |
||||||
323 | $available = $this->budgetRepository->getAvailableBudgetWithCurrency($start, $end); |
||||||
324 | $budgets = $this->budgetRepository->getActiveBudgets(); |
||||||
325 | $spentInfo = $this->budgetRepository->spentInPeriodMc($budgets, new Collection, $start, $end); |
||||||
326 | foreach ($available as $currencyId => $amount) { |
||||||
327 | $currency = $this->currencyRepos->findNull($currencyId); |
||||||
328 | if (null === $currency) { |
||||||
329 | continue; |
||||||
330 | } |
||||||
331 | $spentInCurrency = (string)$this->findInSpentArray($spentInfo, $currency); |
||||||
332 | $leftToSpend = bcadd($amount, $spentInCurrency); |
||||||
333 | |||||||
334 | $days = $today->diffInDays($end) + 1; |
||||||
335 | $perDay = '0'; |
||||||
336 | if (0 !== $days && bccomp($leftToSpend, '0') > -1) { |
||||||
337 | $perDay = bcdiv($leftToSpend, (string)$days); |
||||||
338 | } |
||||||
339 | |||||||
340 | $return[] = [ |
||||||
341 | 'key' => sprintf('left-to-spend-in-%s', $currency->code), |
||||||
342 | 'title' => trans('firefly.box_left_to_spend_in_currency', ['currency' => $currency->symbol]), |
||||||
343 | 'monetary_value' => round($leftToSpend, $currency->decimal_places), |
||||||
0 ignored issues
–
show
$leftToSpend of type string is incompatible with the type double expected by parameter $val of round() .
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
Loading history...
|
|||||||
344 | 'currency_id' => $currency->id, |
||||||
345 | 'currency_code' => $currency->code, |
||||||
346 | 'currency_symbol' => $currency->symbol, |
||||||
347 | 'currency_decimal_places' => $currency->decimal_places, |
||||||
348 | 'value_parsed' => app('amount')->formatAnything($currency, $leftToSpend, false), |
||||||
349 | 'local_icon' => 'money', |
||||||
350 | 'sub_title' => (string)trans('firefly.box_spend_per_day', ['amount' => app('amount')->formatAnything($currency, $perDay, false)]), |
||||||
351 | ]; |
||||||
352 | } |
||||||
353 | |||||||
354 | return $return; |
||||||
355 | } |
||||||
356 | |||||||
357 | /** |
||||||
358 | * @param Carbon $start |
||||||
359 | * @param Carbon $end |
||||||
360 | * |
||||||
361 | * @return array |
||||||
362 | */ |
||||||
363 | private function getNetWorthInfo(Carbon $start, Carbon $end): array |
||||||
364 | { |
||||||
365 | /** @var User $user */ |
||||||
366 | $user = auth()->user(); |
||||||
367 | $date = Carbon::now()->startOfDay(); |
||||||
368 | |||||||
369 | |||||||
370 | // start and end in the future? use $end |
||||||
371 | if ($this->notInDateRange($date, $start, $end)) { |
||||||
372 | /** @var Carbon $date */ |
||||||
373 | $date = session('end', Carbon::now()->endOfMonth()); |
||||||
374 | } |
||||||
375 | |||||||
376 | /** @var NetWorthInterface $netWorthHelper */ |
||||||
377 | $netWorthHelper = app(NetWorthInterface::class); |
||||||
378 | $netWorthHelper->setUser($user); |
||||||
379 | $allAccounts = $this->accountRepository->getActiveAccountsByType([AccountType::ASSET, AccountType::DEBT, AccountType::LOAN, AccountType::MORTGAGE]); |
||||||
380 | |||||||
381 | // filter list on preference of being included. |
||||||
382 | $filtered = $allAccounts->filter( |
||||||
383 | function (Account $account) { |
||||||
384 | $includeNetWorth = $this->accountRepository->getMetaValue($account, 'include_net_worth'); |
||||||
385 | |||||||
386 | return null === $includeNetWorth ? true : '1' === $includeNetWorth; |
||||||
387 | } |
||||||
388 | ); |
||||||
389 | |||||||
390 | $netWorthSet = $netWorthHelper->getNetWorthByCurrency($filtered, $date); |
||||||
391 | $return = []; |
||||||
392 | foreach ($netWorthSet as $index => $data) { |
||||||
393 | /** @var TransactionCurrency $currency */ |
||||||
394 | $currency = $data['currency']; |
||||||
395 | $amount = round($data['balance'], $currency->decimal_places); |
||||||
396 | if (0.0 === $amount) { |
||||||
397 | continue; |
||||||
398 | } |
||||||
399 | // return stuff |
||||||
400 | $return[] = [ |
||||||
401 | 'key' => sprintf('net-worth-in-%s', $currency->code), |
||||||
402 | 'title' => trans('firefly.box_net_worth_in_currency', ['currency' => $currency->symbol]), |
||||||
403 | 'monetary_value' => $amount, |
||||||
404 | 'currency_id' => $currency->id, |
||||||
405 | 'currency_code' => $currency->code, |
||||||
406 | 'currency_symbol' => $currency->symbol, |
||||||
407 | 'currency_decimal_places' => $currency->decimal_places, |
||||||
408 | 'value_parsed' => app('amount')->formatAnything($currency, $data['balance'], false), |
||||||
409 | 'local_icon' => 'line-chart', |
||||||
410 | 'sub_title' => '', |
||||||
411 | ]; |
||||||
412 | } |
||||||
413 | |||||||
414 | return $return; |
||||||
415 | } |
||||||
416 | |||||||
417 | } |
||||||
418 |