mirror of
				https://github.com/firefly-iii/firefly-iii.git
				synced 2025-10-31 10:47:00 +00:00 
			
		
		
		
	Revamped tag report as well.
This commit is contained in:
		| @@ -39,7 +39,7 @@ use Throwable; | ||||
|  * | ||||
|  * @codeCoverageIgnore | ||||
|  */ | ||||
| class MonthReportGenerator extends Support implements ReportGeneratorInterface | ||||
| class MonthReportGenerator implements ReportGeneratorInterface | ||||
| { | ||||
|     /** @var Collection The accounts in the report. */ | ||||
|     private $accounts; | ||||
|   | ||||
| @@ -39,7 +39,7 @@ use Throwable; | ||||
|  * | ||||
|  * @codeCoverageIgnore | ||||
|  */ | ||||
| class MonthReportGenerator extends Support implements ReportGeneratorInterface | ||||
| class MonthReportGenerator implements ReportGeneratorInterface | ||||
| { | ||||
|     /** @var Collection The included accounts */ | ||||
|     private $accounts; | ||||
|   | ||||
| @@ -1,211 +0,0 @@ | ||||
| <?php | ||||
| /** | ||||
|  * Support.php | ||||
|  * Copyright (c) 2017 thegrumpydictator@gmail.com | ||||
|  * | ||||
|  * This file is part of Firefly III. | ||||
|  * | ||||
|  * 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 | ||||
|  * along with Firefly III. If not, see <http://www.gnu.org/licenses/>. | ||||
|  */ | ||||
| /** @noinspection PhpUndefinedMethodInspection */ | ||||
| declare(strict_types=1); | ||||
|  | ||||
| namespace FireflyIII\Generator\Report; | ||||
|  | ||||
| use FireflyIII\Models\TransactionType; | ||||
|  | ||||
| /** | ||||
|  * Class Support. | ||||
|  * @method array getExpenses() | ||||
|  * @method array getIncome() | ||||
|  * | ||||
|  * @codeCoverageIgnore | ||||
|  */ | ||||
| class Support | ||||
| { | ||||
|     /** | ||||
|      * Get the top expenses. | ||||
|      * | ||||
|      * @return array | ||||
|      */ | ||||
|     public function getTopExpenses(): array | ||||
|     { | ||||
|         $expenses = $this->getExpenses(); | ||||
|         usort($expenses, function ($a, $b) { | ||||
|             return $a['amount'] <=> $b['amount']; | ||||
|         }); | ||||
|  | ||||
|         return $expenses; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Get the top income. | ||||
|      * | ||||
|      * @return array | ||||
|      */ | ||||
|     public function getTopIncome(): array | ||||
|     { | ||||
|         $income = $this->getIncome(); | ||||
|         usort($income, function ($a, $b) { | ||||
|             return $b['amount'] <=> $a['amount']; | ||||
|         }); | ||||
|  | ||||
|         return $income; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Get averages from a collection. | ||||
|      * | ||||
|      * @param array $array | ||||
|      * @param int $sortFlag | ||||
|      * | ||||
|      * @return array | ||||
|      */ | ||||
|     protected function getAverages(array $array, int $sortFlag): array | ||||
|     { | ||||
|         $result = []; | ||||
|         /** @var array $journal */ | ||||
|         foreach ($array as $journal) { | ||||
|             // opposing name and ID: | ||||
|             $opposingId = $journal['destination_account_id']; | ||||
|  | ||||
|             // is not set? | ||||
|             if (!isset($result[$opposingId])) { | ||||
|                 $name                = $journal['destination_account_name']; | ||||
|                 $result[$opposingId] = [ | ||||
|                     'name'    => $name, | ||||
|                     'count'   => 1, | ||||
|                     'id'      => $opposingId, | ||||
|                     'average' => $journal['amount'], | ||||
|                     'sum'     => $journal['amount'], | ||||
|                 ]; | ||||
|                 continue; | ||||
|             } | ||||
|             ++$result[$opposingId]['count']; | ||||
|             $result[$opposingId]['sum']     = bcadd($result[$opposingId]['sum'], $journal['amount']); | ||||
|             $result[$opposingId]['average'] = bcdiv($result[$opposingId]['sum'], (string)$result[$opposingId]['count']); | ||||
|         } | ||||
|  | ||||
|         // sort result by average: | ||||
|         $average = []; | ||||
|         foreach ($result as $key => $row) { | ||||
|             $average[$key] = (float)$row['average']; | ||||
|         } | ||||
|  | ||||
|         array_multisort($average, $sortFlag, $result); | ||||
|  | ||||
|         return $result; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Summarize collection by earned and spent data. | ||||
|      * | ||||
|      *  // it's exactly five. | ||||
|      * | ||||
|      * @param array $spent | ||||
|      * @param array $earned | ||||
|      * | ||||
|      * @return array | ||||
|      */ | ||||
|     protected function getObjectSummary(array $spent, array $earned): array | ||||
|     { | ||||
|         $return = [ | ||||
|             'sum' => [ | ||||
|                 'spent'  => '0', | ||||
|                 'earned' => '0', | ||||
|             ], | ||||
|         ]; | ||||
|  | ||||
|         /** | ||||
|          * @var int | ||||
|          * @var string $entry | ||||
|          */ | ||||
|         foreach ($spent as $objectId => $entry) { | ||||
|             if (!isset($return[$objectId])) { | ||||
|                 $return[$objectId] = ['spent' => '0', 'earned' => '0']; | ||||
|             } | ||||
|  | ||||
|             $return[$objectId]['spent'] = $entry; | ||||
|             $return['sum']['spent']     = bcadd($return['sum']['spent'], $entry); | ||||
|         } | ||||
|  | ||||
|         /** | ||||
|          * @var int | ||||
|          * @var string $entry | ||||
|          */ | ||||
|         foreach ($earned as $objectId => $entry) { | ||||
|             $entry = bcmul($entry, '-1'); | ||||
|             if (!isset($return[$objectId])) { | ||||
|                 $return[$objectId] = ['spent' => '0', 'earned' => '0']; | ||||
|             } | ||||
|  | ||||
|             $return[$objectId]['earned'] = $entry; | ||||
|             $return['sum']['earned']     = bcadd($return['sum']['earned'], $entry); | ||||
|         } | ||||
|  | ||||
|         return $return; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Summarize the data by account. | ||||
|      * | ||||
|      * @param array $array | ||||
|      * | ||||
|      * @return array | ||||
|      */ | ||||
|     protected function summarizeByAccount(array $array): array | ||||
|     { | ||||
|         $result = []; | ||||
|         /** @var array $journal */ | ||||
|         foreach ($array as $journal) { | ||||
|             $accountId          = $journal['source_account_id'] ?? 0; | ||||
|             $result[$accountId] = $result[$accountId] ?? '0'; | ||||
|             $result[$accountId] = bcadd($journal['amount'], $result[$accountId]); | ||||
|         } | ||||
|  | ||||
|         return $result; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Summarize the data by the asset account or liability, depending on the type. | ||||
|      * | ||||
|      * In case of transfers, it will choose the source account. | ||||
|      * | ||||
|      * @param array $array | ||||
|      * | ||||
|      * @return array | ||||
|      */ | ||||
|     protected function summarizeByAssetAccount(array $array): array | ||||
|     { | ||||
|         $result = []; | ||||
|         /** @var array $journal */ | ||||
|         foreach ($array as $journal) { | ||||
|             $accountId = 0; | ||||
|             switch ($journal['transaction_type_type']) { | ||||
|                 case TransactionType::WITHDRAWAL: | ||||
|                 case TransactionType::TRANSFER: | ||||
|                     $accountId = $journal['source_account_id'] ?? 0; | ||||
|                     break; | ||||
|                 case TransactionType::DEPOSIT: | ||||
|                     $accountId = $journal['destination_account_id'] ?? 0; | ||||
|                     break; | ||||
|             } | ||||
|  | ||||
|             $result[$accountId] = $result[$accountId] ?? '0'; | ||||
|             $result[$accountId] = bcadd($journal['amount'], $result[$accountId]); | ||||
|         } | ||||
|  | ||||
|         return $result; | ||||
|     } | ||||
| } | ||||
| @@ -28,8 +28,6 @@ namespace FireflyIII\Generator\Report\Tag; | ||||
| use Carbon\Carbon; | ||||
| use FireflyIII\Generator\Report\ReportGeneratorInterface; | ||||
| use FireflyIII\Generator\Report\Support; | ||||
| use FireflyIII\Helpers\Collector\GroupCollectorInterface; | ||||
| use FireflyIII\Models\TransactionType; | ||||
| use Illuminate\Support\Collection; | ||||
| use Log; | ||||
| use Throwable; | ||||
| @@ -39,7 +37,7 @@ use Throwable; | ||||
|  * | ||||
|  * @codeCoverageIgnore | ||||
|  */ | ||||
| class MonthReportGenerator extends Support implements ReportGeneratorInterface | ||||
| class MonthReportGenerator implements ReportGeneratorInterface | ||||
| { | ||||
|     /** @var Collection The accounts involved */ | ||||
|     private $accounts; | ||||
| @@ -71,30 +69,18 @@ class MonthReportGenerator extends Support implements ReportGeneratorInterface | ||||
|      */ | ||||
|     public function generate(): string | ||||
|     { | ||||
|         $accountIds      = implode(',', $this->accounts->pluck('id')->toArray()); | ||||
|         $tagTags         = implode(',', $this->tags->pluck('tag')->toArray()); | ||||
|         $tagIds          = implode(',', $this->tags->pluck('id')->toArray()); | ||||
|         $reportType      = 'tag'; | ||||
|         $expenses        = $this->getExpenses(); | ||||
|         $income          = $this->getIncome(); | ||||
|         $accountSummary  = $this->getObjectSummary($this->summarizeByAssetAccount($expenses), $this->summarizeByAssetAccount($income)); | ||||
|         $tagSummary      = $this->getObjectSummary($this->summarizeByTag($expenses), $this->summarizeByTag($income)); | ||||
|         $averageExpenses = $this->getAverages($expenses, SORT_ASC); | ||||
|         $averageIncome   = $this->getAverages($income, SORT_DESC); | ||||
|         $topExpenses     = $this->getTopExpenses(); | ||||
|         $topIncome       = $this->getTopIncome(); | ||||
|         $accountIds = implode(',', $this->accounts->pluck('id')->toArray()); | ||||
|         $tagIds     = implode(',', $this->tags->pluck('id')->toArray()); | ||||
|         $reportType = 'tag'; | ||||
|  | ||||
|         // render! | ||||
|         try { | ||||
|             $result = view( | ||||
|                 'reports.tag.month', compact( | ||||
|                                        'accountIds', 'tagTags', 'reportType', 'accountSummary', 'tagSummary', 'averageExpenses', 'averageIncome', 'topIncome', | ||||
|                                        'topExpenses', 'tagIds' | ||||
|                                    ) | ||||
|                 'reports.tag.month', compact('accountIds', 'reportType', 'tagIds') | ||||
|             )->with('start', $this->start)->with('end', $this->end)->with('tags', $this->tags)->with('accounts', $this->accounts)->render(); | ||||
|         } catch (Throwable $e) { | ||||
|             Log::error(sprintf('Cannot render reports.tag.month: %s', $e->getMessage())); | ||||
|             $result = 'Could not render report view.'; | ||||
|             $result = sprintf('Could not render report view: %s', $e->getMessage()); | ||||
|         } | ||||
|  | ||||
|         return $result; | ||||
| @@ -192,79 +178,5 @@ class MonthReportGenerator extends Support implements ReportGeneratorInterface | ||||
|         return $this; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Get expense collection for report. | ||||
|      * | ||||
|      * @return array | ||||
|      */ | ||||
|     protected function getExpenses(): array | ||||
|     { | ||||
|         if (count($this->expenses) > 0) { | ||||
|             Log::debug('Return previous set of expenses.'); | ||||
|  | ||||
|             return $this->expenses; | ||||
|         } | ||||
|  | ||||
|         /** @var GroupCollectorInterface $collector */ | ||||
|         $collector = app(GroupCollectorInterface::class); | ||||
|         $collector->setAccounts($this->accounts)->setRange($this->start, $this->end) | ||||
|                   ->setTypes([TransactionType::WITHDRAWAL, TransactionType::TRANSFER]) | ||||
|                   ->setTags($this->tags)->withAccountInformation(); | ||||
|  | ||||
|         $journals       = $collector->getExtractedJournals(); | ||||
|         $this->expenses = $journals; | ||||
|  | ||||
|         return $journals; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Get the income for this report. | ||||
|      * | ||||
|      * @return array | ||||
|      */ | ||||
|     protected function getIncome(): array | ||||
|     { | ||||
|         if (count($this->income) > 0) { | ||||
|             return $this->income; | ||||
|         } | ||||
|  | ||||
|         /** @var GroupCollectorInterface $collector */ | ||||
|         $collector = app(GroupCollectorInterface::class); | ||||
|         $collector->setAccounts($this->accounts)->setRange($this->start, $this->end) | ||||
|                   ->setTypes([TransactionType::DEPOSIT, TransactionType::TRANSFER]) | ||||
|                   ->setTags($this->tags)->withAccountInformation(); | ||||
|  | ||||
|         $journals     = $collector->getExtractedJournals(); | ||||
|         $this->income = $journals; | ||||
|  | ||||
|         return $journals; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Summarize by tag. | ||||
|      * | ||||
|      * @param array $array | ||||
|      * | ||||
|      * @return array | ||||
|      */ | ||||
|     protected function summarizeByTag(array $array): array | ||||
|     { | ||||
|         $tagIds = array_map('\intval', $this->tags->pluck('id')->toArray()); | ||||
|         $result = []; | ||||
|         /** @var array $journal */ | ||||
|         foreach ($array as $journal) { | ||||
|             /** | ||||
|              * @var int $id | ||||
|              * @var array $tag | ||||
|              */ | ||||
|             foreach ($journal['tags'] as $id => $tag) { | ||||
|                 if (in_array($id, $tagIds, true)) { | ||||
|                     $result[$id] = $result[$id] ?? '0'; | ||||
|                     $result[$id] = bcadd($journal['amount'], $result[$id]); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         return $result; | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -296,7 +296,7 @@ class CategoryReportController extends Controller | ||||
|  | ||||
|     /** | ||||
|      * @param Collection $accounts | ||||
|      * @param Category $category | ||||
|      * @param Category   $category | ||||
|      * @param Carbon     $start | ||||
|      * @param Carbon     $end | ||||
|      * | ||||
| @@ -307,7 +307,7 @@ class CategoryReportController extends Controller | ||||
|     { | ||||
|         $chartData = []; | ||||
|         $spent     = $this->opsRepository->listExpenses($start, $end, $accounts, new Collection([$category])); | ||||
|         $earned     = $this->opsRepository->listIncome($start, $end, $accounts, new Collection([$category])); | ||||
|         $earned    = $this->opsRepository->listIncome($start, $end, $accounts, new Collection([$category])); | ||||
|         $format    = app('navigation')->preferredCarbonLocalizedFormat($start, $end); | ||||
|  | ||||
|         // loop expenses. | ||||
| @@ -435,6 +435,7 @@ class CategoryReportController extends Controller | ||||
|  | ||||
|     /** | ||||
|      * TODO duplicate function | ||||
|      * | ||||
|      * @param Carbon $start | ||||
|      * @param Carbon $end | ||||
|      * | ||||
|   | ||||
| @@ -27,6 +27,7 @@ use FireflyIII\Generator\Chart\Basic\GeneratorInterface; | ||||
| use FireflyIII\Helpers\Chart\MetaPieChartInterface; | ||||
| use FireflyIII\Http\Controllers\Controller; | ||||
| use FireflyIII\Models\Tag; | ||||
| use FireflyIII\Repositories\Tag\OperationsRepositoryInterface; | ||||
| use FireflyIII\Support\CacheProperties; | ||||
| use FireflyIII\Support\Http\Controllers\AugumentData; | ||||
| use FireflyIII\Support\Http\Controllers\TransactionCalculation; | ||||
| @@ -42,8 +43,12 @@ class TagReportController extends Controller | ||||
|     /** @var GeneratorInterface Chart generation methods. */ | ||||
|     protected $generator; | ||||
|  | ||||
|     /** @var OperationsRepositoryInterface */ | ||||
|     private $opsRepository; | ||||
|  | ||||
|     /** | ||||
|      * TagReportController constructor. | ||||
|      * | ||||
|      * @codeCoverageIgnore | ||||
|      */ | ||||
|     public function __construct() | ||||
| @@ -51,6 +56,14 @@ class TagReportController extends Controller | ||||
|         parent::__construct(); | ||||
|         // create chart generator: | ||||
|         $this->generator = app(GeneratorInterface::class); | ||||
|  | ||||
|         $this->middleware( | ||||
|             function ($request, $next) { | ||||
|                 $this->opsRepository = app(OperationsRepositoryInterface::class); | ||||
|  | ||||
|                 return $next($request); | ||||
|             } | ||||
|         ); | ||||
|     } | ||||
|  | ||||
|  | ||||
| @@ -111,12 +124,7 @@ class TagReportController extends Controller | ||||
|         return response()->json($data); | ||||
|     } | ||||
|  | ||||
|  | ||||
|     /** | ||||
|      * Generate expense for tag grouped on budget. | ||||
|      * | ||||
|      * TODO this chart is not multi-currency aware. | ||||
|      * | ||||
|      * @param Collection $accounts | ||||
|      * @param Collection $tags | ||||
|      * @param Carbon     $start | ||||
| @@ -126,25 +134,32 @@ class TagReportController extends Controller | ||||
|      */ | ||||
|     public function budgetExpense(Collection $accounts, Collection $tags, Carbon $start, Carbon $end): JsonResponse | ||||
|     { | ||||
|         /** @var MetaPieChartInterface $helper */ | ||||
|         $helper = app(MetaPieChartInterface::class); | ||||
|         $helper->setAccounts($accounts); | ||||
|         $helper->setTags($tags); | ||||
|         $helper->setStart($start); | ||||
|         $helper->setEnd($end); | ||||
|         $helper->setCollectOtherObjects(false); | ||||
|         $chartData = $helper->generate('expense', 'budget'); | ||||
|         $data      = $this->generator->pieChart($chartData); | ||||
|         $result = []; | ||||
|         $spent  = $this->opsRepository->listExpenses($start, $end, $accounts, $tags); | ||||
|  | ||||
|         // loop expenses. | ||||
|         foreach ($spent as $currency) { | ||||
|             /** @var array $tag */ | ||||
|             foreach ($currency['tags'] as $tag) { | ||||
|                 foreach ($tag['transaction_journals'] as $journal) { | ||||
|                     $objectName               = $journal['budget_name'] ?? trans('firefly.no_budget'); | ||||
|                     $title                    = sprintf('%s (%s)', $objectName, $currency['currency_name']); | ||||
|                     $result[$title]           = $result[$title] ?? [ | ||||
|                             'amount'          => '0', | ||||
|                             'currency_symbol' => $currency['currency_symbol'], | ||||
|                         ]; | ||||
|                     $amount                   = app('steam')->positive($journal['amount']); | ||||
|                     $result[$title]['amount'] = bcadd($result[$title]['amount'], $amount); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         $data = $this->generator->multiCurrencyPieChart($result); | ||||
|  | ||||
|         return response()->json($data); | ||||
|     } | ||||
|  | ||||
|  | ||||
|     /** | ||||
|      * Generate expense for tag grouped on category. | ||||
|      * | ||||
|      * TODO this chart is not multi-currency aware. | ||||
|      * | ||||
|      * @param Collection $accounts | ||||
|      * @param Collection $tags | ||||
|      * @param Carbon     $start | ||||
| @@ -154,188 +169,371 @@ class TagReportController extends Controller | ||||
|      */ | ||||
|     public function categoryExpense(Collection $accounts, Collection $tags, Carbon $start, Carbon $end): JsonResponse | ||||
|     { | ||||
|         /** @var MetaPieChartInterface $helper */ | ||||
|         $helper = app(MetaPieChartInterface::class); | ||||
|         $helper->setAccounts($accounts); | ||||
|         $helper->setTags($tags); | ||||
|         $helper->setStart($start); | ||||
|         $helper->setEnd($end); | ||||
|         $helper->setCollectOtherObjects(false); | ||||
|         $chartData = $helper->generate('expense', 'category'); | ||||
|         $data      = $this->generator->pieChart($chartData); | ||||
|         $result = []; | ||||
|         $spent  = $this->opsRepository->listExpenses($start, $end, $accounts, $tags); | ||||
|  | ||||
|         // loop expenses. | ||||
|         foreach ($spent as $currency) { | ||||
|             /** @var array $tag */ | ||||
|             foreach ($currency['tags'] as $tag) { | ||||
|                 foreach ($tag['transaction_journals'] as $journal) { | ||||
|                     $objectName               = $journal['category_name'] ?? trans('firefly.no_category'); | ||||
|                     $title                    = sprintf('%s (%s)', $objectName, $currency['currency_name']); | ||||
|                     $result[$title]           = $result[$title] ?? [ | ||||
|                             'amount'          => '0', | ||||
|                             'currency_symbol' => $currency['currency_symbol'], | ||||
|                         ]; | ||||
|                     $amount                   = app('steam')->positive($journal['amount']); | ||||
|                     $result[$title]['amount'] = bcadd($result[$title]['amount'], $amount); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         $data = $this->generator->multiCurrencyPieChart($result); | ||||
|  | ||||
|         return response()->json($data); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param Collection $accounts | ||||
|      * @param Collection $tags | ||||
|      * @param Carbon     $start | ||||
|      * @param Carbon     $end | ||||
|      * | ||||
|      * @return JsonResponse | ||||
|      */ | ||||
|     public function categoryIncome(Collection $accounts, Collection $tags, Carbon $start, Carbon $end): JsonResponse | ||||
|     { | ||||
|         $result = []; | ||||
|         $spent  = $this->opsRepository->listIncome($start, $end, $accounts, $tags); | ||||
|  | ||||
|         // loop expenses. | ||||
|         foreach ($spent as $currency) { | ||||
|             /** @var array $tag */ | ||||
|             foreach ($currency['tags'] as $tag) { | ||||
|                 foreach ($tag['transaction_journals'] as $journal) { | ||||
|                     $objectName               = $journal['category_name'] ?? trans('firefly.no_category'); | ||||
|                     $title                    = sprintf('%s (%s)', $objectName, $currency['currency_name']); | ||||
|                     $result[$title]           = $result[$title] ?? [ | ||||
|                             'amount'          => '0', | ||||
|                             'currency_symbol' => $currency['currency_symbol'], | ||||
|                         ]; | ||||
|                     $amount                   = app('steam')->positive($journal['amount']); | ||||
|                     $result[$title]['amount'] = bcadd($result[$title]['amount'], $amount); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         $data = $this->generator->multiCurrencyPieChart($result); | ||||
|  | ||||
|         return response()->json($data); | ||||
|     } | ||||
|  | ||||
|  | ||||
|     /** | ||||
|      * @param Collection $accounts | ||||
|      * @param Collection $tags | ||||
|      * @param Carbon     $start | ||||
|      * @param Carbon     $end | ||||
|      * | ||||
|      * @return JsonResponse | ||||
|      */ | ||||
|     public function destinationExpense(Collection $accounts, Collection $tags, Carbon $start, Carbon $end): JsonResponse | ||||
|     { | ||||
|         $result = []; | ||||
|         $spent  = $this->opsRepository->listExpenses($start, $end, $accounts, $tags); | ||||
|  | ||||
|         // loop expenses. | ||||
|         foreach ($spent as $currency) { | ||||
|             /** @var array $tag */ | ||||
|             foreach ($currency['tags'] as $tag) { | ||||
|                 foreach ($tag['transaction_journals'] as $journal) { | ||||
|                     $objectName               = $journal['destination_account_name'] ?? trans('firefly.empty'); | ||||
|                     $title                    = sprintf('%s (%s)', $objectName, $currency['currency_name']); | ||||
|                     $result[$title]           = $result[$title] ?? [ | ||||
|                             'amount'          => '0', | ||||
|                             'currency_symbol' => $currency['currency_symbol'], | ||||
|                         ]; | ||||
|                     $amount                   = app('steam')->positive($journal['amount']); | ||||
|                     $result[$title]['amount'] = bcadd($result[$title]['amount'], $amount); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         $data = $this->generator->multiCurrencyPieChart($result); | ||||
|  | ||||
|         return response()->json($data); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param Collection $accounts | ||||
|      * @param Collection $tags | ||||
|      * @param Carbon     $start | ||||
|      * @param Carbon     $end | ||||
|      * | ||||
|      * @return JsonResponse | ||||
|      */ | ||||
|     public function destinationIncome(Collection $accounts, Collection $tags, Carbon $start, Carbon $end): JsonResponse | ||||
|     { | ||||
|         $result = []; | ||||
|         $spent  = $this->opsRepository->listIncome($start, $end, $accounts, $tags); | ||||
|  | ||||
|         // loop expenses. | ||||
|         foreach ($spent as $currency) { | ||||
|             /** @var array $tag */ | ||||
|             foreach ($currency['tags'] as $tag) { | ||||
|                 foreach ($tag['transaction_journals'] as $journal) { | ||||
|                     $objectName               = $journal['destination_account_name'] ?? trans('firefly.empty'); | ||||
|                     $title                    = sprintf('%s (%s)', $objectName, $currency['currency_name']); | ||||
|                     $result[$title]           = $result[$title] ?? [ | ||||
|                             'amount'          => '0', | ||||
|                             'currency_symbol' => $currency['currency_symbol'], | ||||
|                         ]; | ||||
|                     $amount                   = app('steam')->positive($journal['amount']); | ||||
|                     $result[$title]['amount'] = bcadd($result[$title]['amount'], $amount); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         $data = $this->generator->multiCurrencyPieChart($result); | ||||
|  | ||||
|         return response()->json($data); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Generate main tag overview chart. | ||||
|      * | ||||
|      * TODO this chart is not multi-currency aware. | ||||
|      * | ||||
|      * @param Collection $accounts | ||||
|      * @param Collection $tags | ||||
|      * @param Tag $tag | ||||
|      * @param Carbon     $start | ||||
|      * @param Carbon     $end | ||||
|      * | ||||
|      * @return JsonResponse | ||||
|      * | ||||
|      */ | ||||
|     public function mainChart(Collection $accounts, Collection $tags, Carbon $start, Carbon $end): JsonResponse | ||||
|     public function mainChart(Collection $accounts, Tag $tag, Carbon $start, Carbon $end): JsonResponse | ||||
|     { | ||||
|         $cache = new CacheProperties; | ||||
|         $cache->addProperty('chart.category.report.main'); | ||||
|         $cache->addProperty($accounts); | ||||
|         $cache->addProperty($tags); | ||||
|         $cache->addProperty($start); | ||||
|         $cache->addProperty($end); | ||||
|         if ($cache->has()) { | ||||
|             return response()->json($cache->get()); // @codeCoverageIgnore | ||||
|         } | ||||
|         $chartData = []; | ||||
|         $spent     = $this->opsRepository->listExpenses($start, $end, $accounts, new Collection([$tag])); | ||||
|         $earned    = $this->opsRepository->listIncome($start, $end, $accounts, new Collection([$tag])); | ||||
|         $format    = app('navigation')->preferredCarbonLocalizedFormat($start, $end); | ||||
|  | ||||
|         $format       = app('navigation')->preferredCarbonLocalizedFormat($start, $end); | ||||
|         $function     = app('navigation')->preferredEndOfPeriod($start, $end); | ||||
|         $chartData    = []; | ||||
|         $currentStart = clone $start; | ||||
|         // loop expenses. | ||||
|         foreach ($spent as $currency) { | ||||
|             // add things to chart Data for each currency: | ||||
|             $spentKey             = sprintf('%d-spent', $currency['currency_id']); | ||||
|             $chartData[$spentKey] = $chartData[$spentKey] ?? [ | ||||
|                     'label'           => sprintf( | ||||
|                         '%s (%s)', (string)trans('firefly.spent_in_specific_tag', ['tag' => $tag->tag]), $currency['currency_name'] | ||||
|                     ), | ||||
|                     'type'            => 'bar', | ||||
|                     'currency_symbol' => $currency['currency_symbol'], | ||||
|                     'currency_id'     => $currency['currency_id'], | ||||
|                     'entries'         => $this->makeEntries($start, $end), | ||||
|                 ]; | ||||
|  | ||||
|         // prep chart data: | ||||
|         foreach ($tags as $tag) { | ||||
|             $chartData[$tag->id . '-in']  = [ | ||||
|                 'label'   => $tag->tag . ' (' . strtolower((string)trans('firefly.income')) . ')', | ||||
|                 'type'    => 'bar', | ||||
|                 'yAxisID' => 'y-axis-0', | ||||
|                 'entries' => [], | ||||
|             ]; | ||||
|             $chartData[$tag->id . '-out'] = [ | ||||
|                 'label'   => $tag->tag . ' (' . strtolower((string)trans('firefly.expenses')) . ')', | ||||
|                 'type'    => 'bar', | ||||
|                 'yAxisID' => 'y-axis-0', | ||||
|                 'entries' => [], | ||||
|             ]; | ||||
|             // total in, total out: | ||||
|             $chartData[$tag->id . '-total-in']  = [ | ||||
|                 'label'   => $tag->tag . ' (' . strtolower((string)trans('firefly.sum_of_income')) . ')', | ||||
|                 'type'    => 'line', | ||||
|                 'fill'    => false, | ||||
|                 'yAxisID' => 'y-axis-1', | ||||
|                 'entries' => [], | ||||
|             ]; | ||||
|             $chartData[$tag->id . '-total-out'] = [ | ||||
|                 'label'   => $tag->tag . ' (' . strtolower((string)trans('firefly.sum_of_expenses')) . ')', | ||||
|                 'type'    => 'line', | ||||
|                 'fill'    => false, | ||||
|                 'yAxisID' => 'y-axis-1', | ||||
|                 'entries' => [], | ||||
|             ]; | ||||
|         } | ||||
|         $sumOfIncome  = []; | ||||
|         $sumOfExpense = []; | ||||
|  | ||||
|         while ($currentStart < $end) { | ||||
|             $currentEnd = clone $currentStart; | ||||
|             $currentEnd = $currentEnd->$function(); | ||||
|             $expenses   = $this->groupByTag($this->getExpensesForTags($accounts, $tags, $currentStart, $currentEnd)); | ||||
|             $income     = $this->groupByTag($this->getIncomeForTags($accounts, $tags, $currentStart, $currentEnd)); | ||||
|             $label      = $currentStart->formatLocalized($format); | ||||
|  | ||||
|             /** @var Tag $tag */ | ||||
|             foreach ($tags as $tag) { | ||||
|                 $labelIn        = $tag->id . '-in'; | ||||
|                 $labelOut       = $tag->id . '-out'; | ||||
|                 $labelSumIn     = $tag->id . '-total-in'; | ||||
|                 $labelSumOut    = $tag->id . '-total-out'; | ||||
|                 $currentIncome  = bcmul($income[$tag->id] ?? '0','-1'); | ||||
|                 $currentExpense = $expenses[$tag->id] ?? '0'; | ||||
|  | ||||
|                 // add to sum: | ||||
|                 $sumOfIncome[$tag->id]  = $sumOfIncome[$tag->id] ?? '0'; | ||||
|                 $sumOfExpense[$tag->id] = $sumOfExpense[$tag->id] ?? '0'; | ||||
|                 $sumOfIncome[$tag->id]  = bcadd($sumOfIncome[$tag->id], $currentIncome); | ||||
|                 $sumOfExpense[$tag->id] = bcadd($sumOfExpense[$tag->id], $currentExpense); | ||||
|  | ||||
|                 // add to chart: | ||||
|                 $chartData[$labelIn]['entries'][$label]     = $currentIncome; | ||||
|                 $chartData[$labelOut]['entries'][$label]    = $currentExpense; | ||||
|                 $chartData[$labelSumIn]['entries'][$label]  = $sumOfIncome[$tag->id]; | ||||
|                 $chartData[$labelSumOut]['entries'][$label] = $sumOfExpense[$tag->id]; | ||||
|             foreach ($currency['tags'] as $currentTag) { | ||||
|                 foreach ($currentTag['transaction_journals'] as $journal) { | ||||
|                     $key                                   = $journal['date']->formatLocalized($format); | ||||
|                     $amount                                = app('steam')->positive($journal['amount']); | ||||
|                     $chartData[$spentKey]['entries'][$key] = $chartData[$spentKey]['entries'][$key] ?? '0'; | ||||
|                     $chartData[$spentKey]['entries'][$key] = bcadd($chartData[$spentKey]['entries'][$key], $amount); | ||||
|                 } | ||||
|             } | ||||
|             /** @var Carbon $currentStart */ | ||||
|         } | ||||
|  | ||||
|         // loop income. | ||||
|         foreach ($earned as $currency) { | ||||
|             // add things to chart Data for each currency: | ||||
|             $spentKey             = sprintf('%d-earned', $currency['currency_id']); | ||||
|             $chartData[$spentKey] = $chartData[$spentKey] ?? [ | ||||
|                     'label'           => sprintf( | ||||
|                         '%s (%s)', (string)trans('firefly.earned_in_specific_tag', ['tag' => $tag->tag]), $currency['currency_name'] | ||||
|                     ), | ||||
|                     'type'            => 'bar', | ||||
|                     'currency_symbol' => $currency['currency_symbol'], | ||||
|                     'currency_id'     => $currency['currency_id'], | ||||
|                     'entries'         => $this->makeEntries($start, $end), | ||||
|                 ]; | ||||
|  | ||||
|             foreach ($currency['tags'] as $currentTag) { | ||||
|                 foreach ($currentTag['transaction_journals'] as $journal) { | ||||
|                     $key                                   = $journal['date']->formatLocalized($format); | ||||
|                     $amount                                = app('steam')->positive($journal['amount']); | ||||
|                     $chartData[$spentKey]['entries'][$key] = $chartData[$spentKey]['entries'][$key] ?? '0'; | ||||
|                     $chartData[$spentKey]['entries'][$key] = bcadd($chartData[$spentKey]['entries'][$key], $amount); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         $data = $this->generator->multiSet($chartData); | ||||
|  | ||||
|         return response()->json($data); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param Collection $accounts | ||||
|      * @param Collection $tags | ||||
|      * @param Carbon     $start | ||||
|      * @param Carbon     $end | ||||
|      * | ||||
|      * @return JsonResponse | ||||
|      */ | ||||
|     public function sourceExpense(Collection $accounts, Collection $tags, Carbon $start, Carbon $end): JsonResponse | ||||
|     { | ||||
|         $result = []; | ||||
|         $spent  = $this->opsRepository->listExpenses($start, $end, $accounts, $tags); | ||||
|  | ||||
|         // loop expenses. | ||||
|         foreach ($spent as $currency) { | ||||
|             /** @var array $tag */ | ||||
|             foreach ($currency['tags'] as $tag) { | ||||
|                 foreach ($tag['transaction_journals'] as $journal) { | ||||
|                     $objectName               = $journal['source_account_name'] ?? trans('firefly.empty'); | ||||
|                     $title                    = sprintf('%s (%s)', $objectName, $currency['currency_name']); | ||||
|                     $result[$title]           = $result[$title] ?? [ | ||||
|                             'amount'          => '0', | ||||
|                             'currency_symbol' => $currency['currency_symbol'], | ||||
|                         ]; | ||||
|                     $amount                   = app('steam')->positive($journal['amount']); | ||||
|                     $result[$title]['amount'] = bcadd($result[$title]['amount'], $amount); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         $data = $this->generator->multiCurrencyPieChart($result); | ||||
|  | ||||
|         return response()->json($data); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param Collection $accounts | ||||
|      * @param Collection $tags | ||||
|      * @param Carbon     $start | ||||
|      * @param Carbon     $end | ||||
|      * | ||||
|      * @return JsonResponse | ||||
|      */ | ||||
|     public function sourceIncome(Collection $accounts, Collection $tags, Carbon $start, Carbon $end): JsonResponse | ||||
|     { | ||||
|         $result = []; | ||||
|         $earned = $this->opsRepository->listIncome($start, $end, $accounts, $tags); | ||||
|  | ||||
|         // loop expenses. | ||||
|         foreach ($earned as $currency) { | ||||
|             /** @var array $tag */ | ||||
|             foreach ($currency['tags'] as $tag) { | ||||
|                 foreach ($tag['transaction_journals'] as $journal) { | ||||
|                     $objectName               = $journal['source_account_name'] ?? trans('firefly.empty'); | ||||
|                     $title                    = sprintf('%s (%s)', $objectName, $currency['currency_name']); | ||||
|                     $result[$title]           = $result[$title] ?? [ | ||||
|                             'amount'          => '0', | ||||
|                             'currency_symbol' => $currency['currency_symbol'], | ||||
|                         ]; | ||||
|                     $amount                   = app('steam')->positive($journal['amount']); | ||||
|                     $result[$title]['amount'] = bcadd($result[$title]['amount'], $amount); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         $data = $this->generator->multiCurrencyPieChart($result); | ||||
|  | ||||
|         return response()->json($data); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param Collection $accounts | ||||
|      * @param Collection $tags | ||||
|      * @param Carbon     $start | ||||
|      * @param Carbon     $end | ||||
|      * | ||||
|      * @return JsonResponse | ||||
|      */ | ||||
|     public function tagExpense(Collection $accounts, Collection $tags, Carbon $start, Carbon $end): JsonResponse | ||||
|     { | ||||
|         $result = []; | ||||
|         $spent  = $this->opsRepository->listExpenses($start, $end, $accounts, $tags); | ||||
|  | ||||
|         // loop expenses. | ||||
|         foreach ($spent as $currency) { | ||||
|             /** @var array $tag */ | ||||
|             foreach ($currency['tags'] as $tag) { | ||||
|                 $title          = sprintf('%s (%s)', $tag['name'], $currency['currency_name']); | ||||
|                 $result[$title] = $result[$title] ?? [ | ||||
|                         'amount'          => '0', | ||||
|                         'currency_symbol' => $currency['currency_symbol'], | ||||
|                     ]; | ||||
|                 foreach ($tag['transaction_journals'] as $journal) { | ||||
|                     $amount                   = app('steam')->positive($journal['amount']); | ||||
|                     $result[$title]['amount'] = bcadd($result[$title]['amount'], $amount); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|         $data = $this->generator->multiCurrencyPieChart($result); | ||||
|  | ||||
|         return response()->json($data); | ||||
|     } | ||||
|  | ||||
|  | ||||
|     /** | ||||
|      * @param Collection $accounts | ||||
|      * @param Collection $tags | ||||
|      * @param Carbon     $start | ||||
|      * @param Carbon     $end | ||||
|      * | ||||
|      * @return JsonResponse | ||||
|      */ | ||||
|     public function tagIncome(Collection $accounts, Collection $tags, Carbon $start, Carbon $end): JsonResponse | ||||
|     { | ||||
|         $result = []; | ||||
|         $earned = $this->opsRepository->listIncome($start, $end, $accounts, $tags); | ||||
|  | ||||
|         // loop expenses. | ||||
|         foreach ($earned as $currency) { | ||||
|             /** @var array $tag */ | ||||
|             foreach ($currency['tags'] as $tag) { | ||||
|                 $title          = sprintf('%s (%s)', $tag['name'], $currency['currency_name']); | ||||
|                 $result[$title] = $result[$title] ?? [ | ||||
|                         'amount'          => '0', | ||||
|                         'currency_symbol' => $currency['currency_symbol'], | ||||
|                     ]; | ||||
|                 foreach ($tag['transaction_journals'] as $journal) { | ||||
|                     $amount                   = app('steam')->positive($journal['amount']); | ||||
|                     $result[$title]['amount'] = bcadd($result[$title]['amount'], $amount); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|         $data = $this->generator->multiCurrencyPieChart($result); | ||||
|  | ||||
|         return response()->json($data); | ||||
|  | ||||
|         return response()->json($data); | ||||
|     } | ||||
|  | ||||
|  | ||||
|     /** | ||||
|      * TODO duplicate function | ||||
|      * | ||||
|      * @param Carbon $start | ||||
|      * @param Carbon $end | ||||
|      * | ||||
|      * @return array | ||||
|      */ | ||||
|     private function makeEntries(Carbon $start, Carbon $end): array | ||||
|     { | ||||
|         $return         = []; | ||||
|         $format         = app('navigation')->preferredCarbonLocalizedFormat($start, $end); | ||||
|         $preferredRange = app('navigation')->preferredRangeFormat($start, $end); | ||||
|         $currentStart   = clone $start; | ||||
|         while ($currentStart <= $end) { | ||||
|             $currentEnd   = app('navigation')->endOfPeriod($currentStart, $preferredRange); | ||||
|             $key          = $currentStart->formatLocalized($format); | ||||
|             $return[$key] = '0'; | ||||
|             $currentStart = clone $currentEnd; | ||||
|             $currentStart->addDay(); | ||||
|             $currentStart->startOfDay(); | ||||
|             $currentStart->addDay()->startOfDay(); | ||||
|         } | ||||
|  | ||||
|         } | ||||
|         // remove all empty entries to prevent cluttering: | ||||
|         $newSet = []; | ||||
|         foreach ($chartData as $key => $entry) { | ||||
|             if (0 === !array_sum($entry['entries'])) { | ||||
|                 $newSet[$key] = $chartData[$key]; // @codeCoverageIgnore | ||||
|             } | ||||
|         } | ||||
|         if (0 === count($newSet)) { | ||||
|             $newSet = $chartData; // @codeCoverageIgnore | ||||
|         } | ||||
|         $data = $this->generator->multiSet($newSet); | ||||
|         $cache->store($data); | ||||
|  | ||||
|         return response()->json($data); | ||||
|         return $return; | ||||
|     } | ||||
|  | ||||
|  | ||||
|     /** | ||||
|      * Show expense grouped by expense account. | ||||
|      * | ||||
|      * TODO this chart is not multi-currency aware. | ||||
|      * | ||||
|      * @param Collection $accounts | ||||
|      * @param Collection $tags | ||||
|      * @param Carbon     $start | ||||
|      * @param Carbon     $end | ||||
|      * @param string     $others | ||||
|      * | ||||
|      * @return JsonResponse | ||||
|      */ | ||||
|     public function tagExpense(Collection $accounts, Collection $tags, Carbon $start, Carbon $end, string $others): JsonResponse | ||||
|     { | ||||
|         /** @var MetaPieChartInterface $helper */ | ||||
|         $helper = app(MetaPieChartInterface::class); | ||||
|         $helper->setAccounts($accounts); | ||||
|         $helper->setTags($tags); | ||||
|         $helper->setStart($start); | ||||
|         $helper->setEnd($end); | ||||
|         $helper->setCollectOtherObjects(1 === (int)$others); | ||||
|         $chartData = $helper->generate('expense', 'tag'); | ||||
|         $data      = $this->generator->pieChart($chartData); | ||||
|  | ||||
|         return response()->json($data); | ||||
|     } | ||||
|  | ||||
|  | ||||
|     /** | ||||
|      * Show income grouped by tag. | ||||
|      * | ||||
|      * TODO this chart is not multi-currency aware. | ||||
|      * | ||||
|      * @param Collection $accounts | ||||
|      * @param Collection $tags | ||||
|      * @param Carbon     $start | ||||
|      * @param Carbon     $end | ||||
|      * @param string     $others | ||||
|      * | ||||
|      * @return JsonResponse | ||||
|      */ | ||||
|     public function tagIncome(Collection $accounts, Collection $tags, Carbon $start, Carbon $end, string $others): JsonResponse | ||||
|     { | ||||
|         /** @var MetaPieChartInterface $helper */ | ||||
|         $helper = app(MetaPieChartInterface::class); | ||||
|         $helper->setAccounts($accounts); | ||||
|         $helper->setTags($tags); | ||||
|         $helper->setStart($start); | ||||
|         $helper->setEnd($end); | ||||
|         $helper->setCollectOtherObjects(1 === (int)$others); | ||||
|         $chartData = $helper->generate('income', 'tag'); | ||||
|         $data      = $this->generator->pieChart($chartData); | ||||
|  | ||||
|         return response()->json($data); | ||||
|     } | ||||
| } | ||||
|   | ||||
							
								
								
									
										573
									
								
								app/Http/Controllers/Report/TagController.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										573
									
								
								app/Http/Controllers/Report/TagController.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,573 @@ | ||||
| <?php | ||||
| /** | ||||
|  * TagController.php | ||||
|  * Copyright (c) 2019 thegrumpydictator@gmail.com | ||||
|  * | ||||
|  * This file is part of Firefly III. | ||||
|  * | ||||
|  * 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 | ||||
|  * along with Firefly III. If not, see <http://www.gnu.org/licenses/>. | ||||
|  */ | ||||
|  | ||||
| declare(strict_types=1); | ||||
|  | ||||
| namespace FireflyIII\Http\Controllers\Report; | ||||
|  | ||||
|  | ||||
| use Carbon\Carbon; | ||||
| use FireflyIII\Http\Controllers\Controller; | ||||
| use FireflyIII\Models\Account; | ||||
| use FireflyIII\Models\Tag; | ||||
| use FireflyIII\Repositories\Tag\OperationsRepositoryInterface; | ||||
| use Illuminate\Support\Collection; | ||||
| use Log; | ||||
| use Throwable; | ||||
|  | ||||
| /** | ||||
|  * | ||||
|  * Class TagController | ||||
|  */ | ||||
| class TagController extends Controller | ||||
| { | ||||
|  | ||||
|  | ||||
|     /** @var OperationsRepositoryInterface */ | ||||
|     private $opsRepository; | ||||
|  | ||||
|     /** | ||||
|      * ExpenseReportController constructor. | ||||
|      * | ||||
|      * @codeCoverageIgnore | ||||
|      */ | ||||
|     public function __construct() | ||||
|     { | ||||
|         parent::__construct(); | ||||
|         $this->middleware( | ||||
|             function ($request, $next) { | ||||
|                 $this->opsRepository = app(OperationsRepositoryInterface::class); | ||||
|  | ||||
|                 return $next($request); | ||||
|             } | ||||
|         ); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param Collection $accounts | ||||
|      * @param Collection $tags | ||||
|      * @param Carbon     $start | ||||
|      * @param Carbon     $end | ||||
|      * | ||||
|      * @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View | ||||
|      */ | ||||
|     public function accountPerTag(Collection $accounts, Collection $tags, Carbon $start, Carbon $end) | ||||
|     { | ||||
|         $spent  = $this->opsRepository->listExpenses($start, $end, $accounts, $tags); | ||||
|         $earned = $this->opsRepository->listIncome($start, $end, $accounts, $tags); | ||||
|         $report = []; | ||||
|         /** @var Account $account */ | ||||
|         foreach ($accounts as $account) { | ||||
|             $accountId          = $account->id; | ||||
|             $report[$accountId] = $report[$accountId] ?? [ | ||||
|                     'name'       => $account->name, | ||||
|                     'id'         => $account->id, | ||||
|                     'iban'       => $account->iban, | ||||
|                     'currencies' => [], | ||||
|                 ]; | ||||
|         } | ||||
|  | ||||
|         // loop expenses. | ||||
|         foreach ($spent as $currency) { | ||||
|             $currencyId = $currency['currency_id']; | ||||
|  | ||||
|             /** @var array $tag */ | ||||
|             foreach ($currency['tags'] as $tag) { | ||||
|                 foreach ($tag['transaction_journals'] as $journal) { | ||||
|                     $sourceAccountId                                     = $journal['source_account_id']; | ||||
|                     $report[$sourceAccountId]['currencies'][$currencyId] = $report[$sourceAccountId]['currencies'][$currencyId] ?? [ | ||||
|                             'currency_id'             => $currency['currency_id'], | ||||
|                             'currency_symbol'         => $currency['currency_symbol'], | ||||
|                             'currency_name'           => $currency['currency_name'], | ||||
|                             'currency_decimal_places' => $currency['currency_decimal_places'], | ||||
|                             'tags'                    => [], | ||||
|                         ]; | ||||
|  | ||||
|                     $report[$sourceAccountId]['currencies'][$currencyId]['tags'][$tag['id']] | ||||
|                                                                                                       = $report[$sourceAccountId]['currencies'][$currencyId]['tags'][$tag['id']] | ||||
|                                                                                                         ?? | ||||
|                                                                                                         [ | ||||
|                                                                                                             'spent'  => '0', | ||||
|                                                                                                             'earned' => '0', | ||||
|                                                                                                             'sum'    => '0', | ||||
|                                                                                                         ]; | ||||
|                     $report[$sourceAccountId]['currencies'][$currencyId]['tags'][$tag['id']]['spent'] = bcadd( | ||||
|                         $report[$sourceAccountId]['currencies'][$currencyId]['tags'][$tag['id']]['spent'], $journal['amount'] | ||||
|                     ); | ||||
|                     $report[$sourceAccountId]['currencies'][$currencyId]['tags'][$tag['id']]['sum']   = bcadd( | ||||
|                         $report[$sourceAccountId]['currencies'][$currencyId]['tags'][$tag['id']]['sum'], $journal['amount'] | ||||
|                     ); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|  | ||||
|  | ||||
|         // loop income. | ||||
|         foreach ($earned as $currency) { | ||||
|             $currencyId = $currency['currency_id']; | ||||
|  | ||||
|             /** @var array $category */ | ||||
|             foreach ($currency['tags'] as $tag) { | ||||
|                 foreach ($tag['transaction_journals'] as $journal) { | ||||
|                     $destinationId = $journal['destination_account_id']; | ||||
|                     $report[$destinationId]['currencies'][$currencyId] | ||||
|                                    = $report[$destinationId]['currencies'][$currencyId] | ||||
|                                      ?? [ | ||||
|                                          'currency_id'             => $currency['currency_id'], | ||||
|                                          'currency_symbol'         => $currency['currency_symbol'], | ||||
|                                          'currency_name'           => $currency['currency_name'], | ||||
|                                          'currency_decimal_places' => $currency['currency_decimal_places'], | ||||
|                                          'tags'                    => [], | ||||
|                                      ]; | ||||
|  | ||||
|  | ||||
|                     $report[$destinationId]['currencies'][$currencyId]['tags'][$tag['id']] | ||||
|                                                                                                      = $report[$destinationId]['currencies'][$currencyId]['tags'][$tag['id']] | ||||
|                                                                                                        ?? | ||||
|                                                                                                        [ | ||||
|                                                                                                            'spent'  => '0', | ||||
|                                                                                                            'earned' => '0', | ||||
|                                                                                                            'sum'    => '0', | ||||
|                                                                                                        ]; | ||||
|                     $report[$destinationId]['currencies'][$currencyId]['tags'][$tag['id']]['earned'] = bcadd( | ||||
|                         $report[$destinationId]['currencies'][$currencyId]['tags'][$tag['id']]['earned'], $journal['amount'] | ||||
|                     ); | ||||
|                     $report[$destinationId]['currencies'][$currencyId]['tags'][$tag['id']]['sum']    = bcadd( | ||||
|                         $report[$destinationId]['currencies'][$currencyId]['tags'][$tag['id']]['sum'], $journal['amount'] | ||||
|                     ); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         return view('reports.tag.partials.account-per-tag', compact('report', 'tags')); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param Collection $accounts | ||||
|      * @param Collection $tags | ||||
|      * @param Carbon     $start | ||||
|      * @param Carbon     $end | ||||
|      * | ||||
|      * @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View | ||||
|      */ | ||||
|     public function accounts(Collection $accounts, Collection $tags, Carbon $start, Carbon $end) | ||||
|     { | ||||
|         $spent  = $this->opsRepository->listExpenses($start, $end, $accounts, $tags); | ||||
|         $earned = $this->opsRepository->listIncome($start, $end, $accounts, $tags); | ||||
|         $report = []; | ||||
|         $sums   = []; | ||||
|         /** @var Account $account */ | ||||
|         foreach ($accounts as $account) { | ||||
|             $accountId          = $account->id; | ||||
|             $report[$accountId] = $report[$accountId] ?? [ | ||||
|                     'name'       => $account->name, | ||||
|                     'id'         => $account->id, | ||||
|                     'iban'       => $account->iban, | ||||
|                     'currencies' => [], | ||||
|                 ]; | ||||
|         } | ||||
|  | ||||
|         // loop expenses. | ||||
|         foreach ($spent as $currency) { | ||||
|             $currencyId        = $currency['currency_id']; | ||||
|             $sums[$currencyId] = $sums[$currencyId] ?? [ | ||||
|                     'currency_id'             => $currency['currency_id'], | ||||
|                     'currency_symbol'         => $currency['currency_symbol'], | ||||
|                     'currency_name'           => $currency['currency_name'], | ||||
|                     'currency_decimal_places' => $currency['currency_decimal_places'], | ||||
|                     'spent_sum'               => '0', | ||||
|                     'earned_sum'              => '0', | ||||
|                     'total_sum'               => '0', | ||||
|                 ]; | ||||
|             foreach ($currency['tags'] as $tag) { | ||||
|                 foreach ($tag['transaction_journals'] as $journal) { | ||||
|                     $sourceAccountId                                              = $journal['source_account_id']; | ||||
|                     $report[$sourceAccountId]['currencies'][$currencyId]          = $report[$sourceAccountId]['currencies'][$currencyId] ?? [ | ||||
|                             'currency_id'             => $currency['currency_id'], | ||||
|                             'currency_symbol'         => $currency['currency_symbol'], | ||||
|                             'currency_name'           => $currency['currency_name'], | ||||
|                             'currency_decimal_places' => $currency['currency_decimal_places'], | ||||
|                             'spent'                   => '0', | ||||
|                             'earned'                  => '0', | ||||
|                             'sum'                     => '0', | ||||
|                         ]; | ||||
|                     $report[$sourceAccountId]['currencies'][$currencyId]['spent'] = bcadd( | ||||
|                         $report[$sourceAccountId]['currencies'][$currencyId]['spent'], $journal['amount'] | ||||
|                     ); | ||||
|                     $report[$sourceAccountId]['currencies'][$currencyId]['sum']   = bcadd( | ||||
|                         $report[$sourceAccountId]['currencies'][$currencyId]['sum'], $journal['amount'] | ||||
|                     ); | ||||
|                     $sums[$currencyId]['spent_sum']                               = bcadd($sums[$currencyId]['spent_sum'], $journal['amount']); | ||||
|                     $sums[$currencyId]['total_sum']                               = bcadd($sums[$currencyId]['total_sum'], $journal['amount']); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         // loop income. | ||||
|         foreach ($earned as $currency) { | ||||
|             $currencyId        = $currency['currency_id']; | ||||
|             $sums[$currencyId] = $sums[$currencyId] ?? [ | ||||
|                     'currency_id'             => $currency['currency_id'], | ||||
|                     'currency_symbol'         => $currency['currency_symbol'], | ||||
|                     'currency_name'           => $currency['currency_name'], | ||||
|                     'currency_decimal_places' => $currency['currency_decimal_places'], | ||||
|                     'spent_sum'               => '0', | ||||
|                     'earned_sum'              => '0', | ||||
|                     'total_sum'               => '0', | ||||
|                 ]; | ||||
|             foreach ($currency['tags'] as $tag) { | ||||
|                 foreach ($tag['transaction_journals'] as $journal) { | ||||
|                     $destinationAccountId                                               = $journal['destination_account_id']; | ||||
|                     $report[$destinationAccountId]['currencies'][$currencyId]           = $report[$destinationAccountId]['currencies'][$currencyId] ?? [ | ||||
|                             'currency_id'             => $currency['currency_id'], | ||||
|                             'currency_symbol'         => $currency['currency_symbol'], | ||||
|                             'currency_name'           => $currency['currency_name'], | ||||
|                             'currency_decimal_places' => $currency['currency_decimal_places'], | ||||
|                             'spent'                   => '0', | ||||
|                             'earned'                  => '0', | ||||
|                             'sum'                     => '0', | ||||
|                         ]; | ||||
|                     $report[$destinationAccountId]['currencies'][$currencyId]['earned'] = bcadd( | ||||
|                         $report[$destinationAccountId]['currencies'][$currencyId]['earned'], $journal['amount'] | ||||
|                     ); | ||||
|                     $report[$destinationAccountId]['currencies'][$currencyId]['sum']    = bcadd( | ||||
|                         $report[$destinationAccountId]['currencies'][$currencyId]['sum'], $journal['amount'] | ||||
|                     ); | ||||
|                     $sums[$currencyId]['earned_sum']                                    = bcadd($sums[$currencyId]['earned_sum'], $journal['amount']); | ||||
|                     $sums[$currencyId]['total_sum']                                     = bcadd($sums[$currencyId]['total_sum'], $journal['amount']); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         return view('reports.tag.partials.accounts', compact('sums', 'report')); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param Collection $accounts | ||||
|      * @param Collection $tags | ||||
|      * @param Carbon     $start | ||||
|      * @param Carbon     $end | ||||
|      * | ||||
|      * @return array|string | ||||
|      */ | ||||
|     public function avgExpenses(Collection $accounts, Collection $tags, Carbon $start, Carbon $end) | ||||
|     { | ||||
|         $spent  = $this->opsRepository->listExpenses($start, $end, $accounts, $tags); | ||||
|         $result = []; | ||||
|         foreach ($spent as $currency) { | ||||
|             $currencyId = $currency['currency_id']; | ||||
|             foreach ($currency['tags'] as $tag) { | ||||
|                 foreach ($tag['transaction_journals'] as $journal) { | ||||
|                     $destinationId = $journal['destination_account_id']; | ||||
|                     $key           = sprintf('%d-%d', $destinationId, $currency['currency_id']); | ||||
|                     $result[$key]  = $result[$key] ?? [ | ||||
|                             'transactions'             => 0, | ||||
|                             'sum'                      => '0', | ||||
|                             'avg'                      => '0', | ||||
|                             'avg_float'                => 0, | ||||
|                             'destination_account_name' => $journal['destination_account_name'], | ||||
|                             'destination_account_id'   => $journal['destination_account_id'], | ||||
|                             'currency_id'              => $currency['currency_id'], | ||||
|                             'currency_name'            => $currency['currency_name'], | ||||
|                             'currency_symbol'          => $currency['currency_symbol'], | ||||
|                             'currency_decimal_places'  => $currency['currency_decimal_places'], | ||||
|                         ]; | ||||
|                     $result[$key]['transactions']++; | ||||
|                     $result[$key]['sum']       = bcadd($journal['amount'], $result[$key]['sum']); | ||||
|                     $result[$key]['avg']       = bcdiv($result[$key]['sum'], (string)$result[$key]['transactions']); | ||||
|                     $result[$key]['avg_float'] = (float)$result[$key]['avg']; | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|         // sort by amount_float | ||||
|         // sort temp array by amount. | ||||
|         $amounts = array_column($result, 'avg_float'); | ||||
|         array_multisort($amounts, SORT_ASC, $result); | ||||
|  | ||||
|         try { | ||||
|             $result = view('reports.tag.partials.avg-expenses', compact('result'))->render(); | ||||
|             // @codeCoverageIgnoreStart | ||||
|         } catch (Throwable $e) { | ||||
|             Log::debug(sprintf('Could not render reports.partials.budget-period: %s', $e->getMessage())); | ||||
|             $result = sprintf('Could not render view: %s', $e->getMessage()); | ||||
|         } | ||||
|  | ||||
|         return $result; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param Collection $accounts | ||||
|      * @param Collection $tags | ||||
|      * @param Carbon     $start | ||||
|      * @param Carbon     $end | ||||
|      * | ||||
|      * @return array|string | ||||
|      */ | ||||
|     public function avgIncome(Collection $accounts, Collection $tags, Carbon $start, Carbon $end) | ||||
|     { | ||||
|         $spent  = $this->opsRepository->listIncome($start, $end, $accounts, $tags); | ||||
|         $result = []; | ||||
|         foreach ($spent as $currency) { | ||||
|             $currencyId = $currency['currency_id']; | ||||
|             foreach ($currency['tags'] as $tag) { | ||||
|                 foreach ($tag['transaction_journals'] as $journal) { | ||||
|                     $sourceId     = $journal['source_account_id']; | ||||
|                     $key          = sprintf('%d-%d', $sourceId, $currency['currency_id']); | ||||
|                     $result[$key] = $result[$key] ?? [ | ||||
|                             'transactions'            => 0, | ||||
|                             'sum'                     => '0', | ||||
|                             'avg'                     => '0', | ||||
|                             'avg_float'               => 0, | ||||
|                             'source_account_name'     => $journal['source_account_name'], | ||||
|                             'source_account_id'       => $journal['source_account_id'], | ||||
|                             'currency_id'             => $currency['currency_id'], | ||||
|                             'currency_name'           => $currency['currency_name'], | ||||
|                             'currency_symbol'         => $currency['currency_symbol'], | ||||
|                             'currency_decimal_places' => $currency['currency_decimal_places'], | ||||
|                         ]; | ||||
|                     $result[$key]['transactions']++; | ||||
|                     $result[$key]['sum']       = bcadd($journal['amount'], $result[$key]['sum']); | ||||
|                     $result[$key]['avg']       = bcdiv($result[$key]['sum'], (string)$result[$key]['transactions']); | ||||
|                     $result[$key]['avg_float'] = (float)$result[$key]['avg']; | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|         // sort by amount_float | ||||
|         // sort temp array by amount. | ||||
|         $amounts = array_column($result, 'avg_float'); | ||||
|         array_multisort($amounts, SORT_DESC, $result); | ||||
|  | ||||
|         try { | ||||
|             $result = view('reports.tag.partials.avg-income', compact('result'))->render(); | ||||
|             // @codeCoverageIgnoreStart | ||||
|         } catch (Throwable $e) { | ||||
|             Log::debug(sprintf('Could not render reports.partials.budget-period: %s', $e->getMessage())); | ||||
|             $result = sprintf('Could not render view: %s', $e->getMessage()); | ||||
|         } | ||||
|  | ||||
|         return $result; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param Collection $accounts | ||||
|      * @param Collection $tags | ||||
|      * @param Carbon     $start | ||||
|      * @param Carbon     $end | ||||
|      * | ||||
|      * @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View | ||||
|      */ | ||||
|     public function tags(Collection $accounts, Collection $tags, Carbon $start, Carbon $end) | ||||
|     { | ||||
|         $spent  = $this->opsRepository->listExpenses($start, $end, $accounts, $tags); | ||||
|         $earned = $this->opsRepository->listIncome($start, $end, $accounts, $tags); | ||||
|         $sums   = []; | ||||
|         $report = []; | ||||
|         /** @var Tag $tag */ | ||||
|         foreach ($tags as $tag) { | ||||
|             $tagId          = $tag->id; | ||||
|             $report[$tagId] = $report[$tagId] ?? [ | ||||
|                     'name'       => $tag->tag, | ||||
|                     'id'         => $tag->id, | ||||
|                     'currencies' => [], | ||||
|                 ]; | ||||
|         } | ||||
|         foreach ($spent as $currency) { | ||||
|             $currencyId        = $currency['currency_id']; | ||||
|             $sums[$currencyId] = $sums[$currencyId] ?? [ | ||||
|                     'currency_id'             => $currency['currency_id'], | ||||
|                     'currency_symbol'         => $currency['currency_symbol'], | ||||
|                     'currency_name'           => $currency['currency_name'], | ||||
|                     'currency_decimal_places' => $currency['currency_decimal_places'], | ||||
|                     'earned_sum'              => '0', | ||||
|                     'spent_sum'               => '0', | ||||
|                     'total_sum'               => '0', | ||||
|                 ]; | ||||
|             /** @var array $tag */ | ||||
|             foreach ($currency['tags'] as $tag) { | ||||
|                 $tagId = $tag['id']; | ||||
|  | ||||
|                 foreach ($tag['transaction_journals'] as $journal) { | ||||
|                     // add currency info to report array: | ||||
|                     $report[$tagId]['currencies'][$currencyId]          = $report[$tagId]['currencies'][$currencyId] ?? [ | ||||
|                             'spent'                   => '0', | ||||
|                             'earned'                  => '0', | ||||
|                             'sum'                     => '0', | ||||
|                             'currency_id'             => $currency['currency_id'], | ||||
|                             'currency_symbol'         => $currency['currency_symbol'], | ||||
|                             'currency_name'           => $currency['currency_name'], | ||||
|                             'currency_decimal_places' => $currency['currency_decimal_places'], | ||||
|                         ]; | ||||
|                     $report[$tagId]['currencies'][$currencyId]['spent'] = bcadd( | ||||
|                         $report[$tagId]['currencies'][$currencyId]['spent'], $journal['amount'] | ||||
|                     ); | ||||
|                     $report[$tagId]['currencies'][$currencyId]['sum']   = bcadd( | ||||
|                         $report[$tagId]['currencies'][$currencyId]['sum'], $journal['amount'] | ||||
|                     ); | ||||
|  | ||||
|                     $sums[$currencyId]['spent_sum'] = bcadd($sums[$currencyId]['spent_sum'], $journal['amount']); | ||||
|                     $sums[$currencyId]['total_sum'] = bcadd($sums[$currencyId]['total_sum'], $journal['amount']); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         foreach ($earned as $currency) { | ||||
|             $currencyId        = $currency['currency_id']; | ||||
|             $sums[$currencyId] = $sums[$currencyId] ?? [ | ||||
|                     'currency_id'             => $currency['currency_id'], | ||||
|                     'currency_symbol'         => $currency['currency_symbol'], | ||||
|                     'currency_name'           => $currency['currency_name'], | ||||
|                     'currency_decimal_places' => $currency['currency_decimal_places'], | ||||
|                     'earned_sum'              => '0', | ||||
|                     'spent_sum'               => '0', | ||||
|                     'total_sum'               => '0', | ||||
|                 ]; | ||||
|             /** @var array $tag */ | ||||
|             foreach ($currency['tags'] as $tag) { | ||||
|                 $tagId = $tag['id']; | ||||
|  | ||||
|                 foreach ($tag['transaction_journals'] as $journal) { | ||||
|                     // add currency info to report array: | ||||
|                     $report[$tagId]['currencies'][$currencyId]           = $report[$tagId]['currencies'][$currencyId] ?? [ | ||||
|                             'spent'                   => '0', | ||||
|                             'earned'                  => '0', | ||||
|                             'sum'                     => '0', | ||||
|                             'currency_id'             => $currency['currency_id'], | ||||
|                             'currency_symbol'         => $currency['currency_symbol'], | ||||
|                             'currency_name'           => $currency['currency_name'], | ||||
|                             'currency_decimal_places' => $currency['currency_decimal_places'], | ||||
|                         ]; | ||||
|                     $report[$tagId]['currencies'][$currencyId]['earned'] = bcadd( | ||||
|                         $report[$tagId]['currencies'][$currencyId]['earned'], $journal['amount'] | ||||
|                     ); | ||||
|                     $report[$tagId]['currencies'][$currencyId]['sum']    = bcadd( | ||||
|                         $report[$tagId]['currencies'][$currencyId]['sum'], $journal['amount'] | ||||
|                     ); | ||||
|  | ||||
|                     $sums[$currencyId]['earned_sum'] = bcadd($sums[$currencyId]['earned_sum'], $journal['amount']); | ||||
|                     $sums[$currencyId]['total_sum']  = bcadd($sums[$currencyId]['total_sum'], $journal['amount']); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         return view('reports.tag.partials.tags', compact('sums', 'report')); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param Collection $accounts | ||||
|      * @param Collection $tags | ||||
|      * @param Carbon     $start | ||||
|      * @param Carbon     $end | ||||
|      * | ||||
|      * @return array|string | ||||
|      */ | ||||
|     public function topExpenses(Collection $accounts, Collection $tags, Carbon $start, Carbon $end) | ||||
|     { | ||||
|         $spent  = $this->opsRepository->listExpenses($start, $end, $accounts, $tags); | ||||
|         $result = []; | ||||
|         foreach ($spent as $currency) { | ||||
|             $currencyId = $currency['currency_id']; | ||||
|             foreach ($currency['tags'] as $tag) { | ||||
|                 foreach ($tag['transaction_journals'] as $journal) { | ||||
|                     $result[] = [ | ||||
|                         'description'              => $journal['description'], | ||||
|                         'transaction_group_id'     => $journal['transaction_group_id'], | ||||
|                         'amount_float'             => (float)$journal['amount'], | ||||
|                         'amount'                   => $journal['amount'], | ||||
|                         'date'                     => $journal['date']->formatLocalized($this->monthAndDayFormat), | ||||
|                         'destination_account_name' => $journal['destination_account_name'], | ||||
|                         'destination_account_id'   => $journal['destination_account_id'], | ||||
|                         'currency_id'              => $currency['currency_id'], | ||||
|                         'currency_name'            => $currency['currency_name'], | ||||
|                         'currency_symbol'          => $currency['currency_symbol'], | ||||
|                         'currency_decimal_places'  => $currency['currency_decimal_places'], | ||||
|                         'tag_id'                   => $tag['id'], | ||||
|                         'tag_name'                 => $tag['name'], | ||||
|                     ]; | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|         // sort by amount_float | ||||
|         // sort temp array by amount. | ||||
|         $amounts = array_column($result, 'amount_float'); | ||||
|         array_multisort($amounts, SORT_ASC, $result); | ||||
|  | ||||
|         try { | ||||
|             $result = view('reports.tag.partials.top-expenses', compact('result'))->render(); | ||||
|             // @codeCoverageIgnoreStart | ||||
|         } catch (Throwable $e) { | ||||
|             Log::debug(sprintf('Could not render reports.partials.budget-period: %s', $e->getMessage())); | ||||
|             $result = sprintf('Could not render view: %s', $e->getMessage()); | ||||
|         } | ||||
|  | ||||
|         return $result; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param Collection $accounts | ||||
|      * @param Collection $tags | ||||
|      * @param Carbon     $start | ||||
|      * @param Carbon     $end | ||||
|      * | ||||
|      * @return array|string | ||||
|      */ | ||||
|     public function topIncome(Collection $accounts, Collection $tags, Carbon $start, Carbon $end) | ||||
|     { | ||||
|         $spent  = $this->opsRepository->listIncome($start, $end, $accounts, $tags); | ||||
|         $result = []; | ||||
|         foreach ($spent as $currency) { | ||||
|             $currencyId = $currency['currency_id']; | ||||
|             foreach ($currency['tags'] as $tag) { | ||||
|                 foreach ($tag['transaction_journals'] as $journal) { | ||||
|                     $result[] = [ | ||||
|                         'description'             => $journal['description'], | ||||
|                         'transaction_group_id'    => $journal['transaction_group_id'], | ||||
|                         'amount_float'            => (float)$journal['amount'], | ||||
|                         'amount'                  => $journal['amount'], | ||||
|                         'date'                    => $journal['date']->formatLocalized($this->monthAndDayFormat), | ||||
|                         'source_account_name'     => $journal['source_account_name'], | ||||
|                         'source_account_id'       => $journal['source_account_id'], | ||||
|                         'currency_id'             => $currency['currency_id'], | ||||
|                         'currency_name'           => $currency['currency_name'], | ||||
|                         'currency_symbol'         => $currency['currency_symbol'], | ||||
|                         'currency_decimal_places' => $currency['currency_decimal_places'], | ||||
|                         'tag_id'                  => $tag['id'], | ||||
|                         'tag_name'                => $tag['name'], | ||||
|                     ]; | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|         // sort by amount_float | ||||
|         // sort temp array by amount. | ||||
|         $amounts = array_column($result, 'amount_float'); | ||||
|         array_multisort($amounts, SORT_DESC, $result); | ||||
|  | ||||
|         try { | ||||
|             $result = view('reports.tag.partials.top-income', compact('result'))->render(); | ||||
|             // @codeCoverageIgnoreStart | ||||
|         } catch (Throwable $e) { | ||||
|             Log::debug(sprintf('Could not render reports.partials.budget-period: %s', $e->getMessage())); | ||||
|             $result = sprintf('Could not render view: %s', $e->getMessage()); | ||||
|         } | ||||
|  | ||||
|         return $result; | ||||
|     } | ||||
|  | ||||
| } | ||||
| @@ -22,6 +22,8 @@ declare(strict_types=1); | ||||
|  | ||||
| namespace FireflyIII\Providers; | ||||
|  | ||||
| use FireflyIII\Repositories\Tag\OperationsRepository; | ||||
| use FireflyIII\Repositories\Tag\OperationsRepositoryInterface; | ||||
| use FireflyIII\Repositories\Tag\TagRepository; | ||||
| use FireflyIII\Repositories\Tag\TagRepositoryInterface; | ||||
| use Illuminate\Foundation\Application; | ||||
| @@ -58,5 +60,19 @@ class TagServiceProvider extends ServiceProvider | ||||
|                 return $repository; | ||||
|             } | ||||
|         ); | ||||
|  | ||||
|         $this->app->bind( | ||||
|             OperationsRepositoryInterface::class, | ||||
|             function (Application $app) { | ||||
|                 /** @var OperationsRepository $repository */ | ||||
|                 $repository = app(OperationsRepository::class); | ||||
|  | ||||
|                 if ($app->auth->check()) { | ||||
|                     $repository->setUser(auth()->user()); | ||||
|                 } | ||||
|  | ||||
|                 return $repository; | ||||
|             } | ||||
|         ); | ||||
|     } | ||||
| } | ||||
|   | ||||
							
								
								
									
										242
									
								
								app/Repositories/Tag/OperationsRepository.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										242
									
								
								app/Repositories/Tag/OperationsRepository.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,242 @@ | ||||
| <?php | ||||
| /** | ||||
|  * OperationsRepository.php | ||||
|  * Copyright (c) 2019 thegrumpydictator@gmail.com | ||||
|  * | ||||
|  * This file is part of Firefly III. | ||||
|  * | ||||
|  * 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 | ||||
|  * along with Firefly III. If not, see <http://www.gnu.org/licenses/>. | ||||
|  */ | ||||
|  | ||||
| declare(strict_types=1); | ||||
|  | ||||
| namespace FireflyIII\Repositories\Tag; | ||||
|  | ||||
| use Carbon\Carbon; | ||||
| use FireflyIII\Exceptions\FireflyException; | ||||
| use FireflyIII\Helpers\Collector\GroupCollectorInterface; | ||||
| use FireflyIII\Models\TransactionType; | ||||
| use FireflyIII\User; | ||||
| use Illuminate\Support\Collection; | ||||
|  | ||||
| /** | ||||
|  * | ||||
|  * Class OperationsRepository | ||||
|  */ | ||||
| class OperationsRepository implements OperationsRepositoryInterface | ||||
| { | ||||
|     /** @var User */ | ||||
|     private $user; | ||||
|  | ||||
|     /** | ||||
|      * This method returns a list of all the withdrawal transaction journals (as arrays) set in that period | ||||
|      * which have the specified tag(s) set to them. It's grouped per currency, with as few details in the array | ||||
|      * as possible. Amounts are always negative. | ||||
|      * | ||||
|      * @param Carbon          $start | ||||
|      * @param Carbon          $end | ||||
|      * @param Collection|null $accounts | ||||
|      * @param Collection|null $tags | ||||
|      * | ||||
|      * @return array | ||||
|      */ | ||||
|     public function listExpenses(Carbon $start, Carbon $end, ?Collection $accounts = null, ?Collection $tags = null): array | ||||
|     { | ||||
|         /** @var GroupCollectorInterface $collector */ | ||||
|         $collector = app(GroupCollectorInterface::class); | ||||
|         $collector->setUser($this->user)->setRange($start, $end)->setTypes([TransactionType::WITHDRAWAL]); | ||||
|         if (null !== $accounts && $accounts->count() > 0) { | ||||
|             $collector->setAccounts($accounts); | ||||
|         } | ||||
|         if (null !== $tags && $tags->count() > 0) { | ||||
|             $collector->setTags($tags); | ||||
|         } | ||||
|         if (null === $tags || (null !== $tags && 0 === $tags->count())) { | ||||
|             $collector->setTags($this->getTags()); | ||||
|         } | ||||
|         $collector->withCategoryInformation()->withAccountInformation()->withBudgetInformation()->withTagInformation(); | ||||
|         $journals       = $collector->getExtractedJournals(); | ||||
|         $array          = []; | ||||
|         $listedJournals = []; | ||||
|  | ||||
|  | ||||
|         foreach ($journals as $journal) { | ||||
|             $currencyId         = (int)$journal['currency_id']; | ||||
|             $array[$currencyId] = $array[$currencyId] ?? [ | ||||
|                     'tags'                    => [], | ||||
|                     'currency_id'             => $currencyId, | ||||
|                     'currency_name'           => $journal['currency_name'], | ||||
|                     'currency_symbol'         => $journal['currency_symbol'], | ||||
|                     'currency_code'           => $journal['currency_code'], | ||||
|                     'currency_decimal_places' => $journal['currency_decimal_places'], | ||||
|                 ]; | ||||
|  | ||||
|             // may have multiple tags: | ||||
|             foreach ($journal['tags'] as $tag) { | ||||
|                 $tagId     = (int)$tag['id']; | ||||
|                 $tagName   = (string)$tag['name']; | ||||
|                 $journalId = (int)$journal['transaction_journal_id']; | ||||
|  | ||||
|                 if (in_array($journalId, $listedJournals, true)) { | ||||
|                     continue; | ||||
|                 } | ||||
|                 $listedJournals[]                   = $journalId; | ||||
|                 $array[$currencyId]['tags'][$tagId] = $array[$currencyId]['tags'][$tagId] ?? [ | ||||
|                         'id'                   => $tagId, | ||||
|                         'name'                 => $tagName, | ||||
|                         'transaction_journals' => [], | ||||
|                     ]; | ||||
|  | ||||
|                 $array[$currencyId]['tags'][$tagId]['transaction_journals'][$journalId] = [ | ||||
|                     'amount'                   => app('steam')->negative($journal['amount']), | ||||
|                     'date'                     => $journal['date'], | ||||
|                     'source_account_id'        => $journal['source_account_id'], | ||||
|                     'budget_name'              => $journal['budget_name'], | ||||
|                     'category_name'            => $journal['category_name'], | ||||
|                     'source_account_name'      => $journal['source_account_name'], | ||||
|                     'destination_account_id'   => $journal['destination_account_id'], | ||||
|                     'destination_account_name' => $journal['destination_account_name'], | ||||
|                     'description'              => $journal['description'], | ||||
|                     'transaction_group_id'     => $journal['transaction_group_id'], | ||||
|                 ]; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         return $array; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * This method returns a list of all the deposit transaction journals (as arrays) set in that period | ||||
|      * which have the specified tag(s) set to them. It's grouped per currency, with as few details in the array | ||||
|      * as possible. Amounts are always positive. | ||||
|      * | ||||
|      * @param Carbon          $start | ||||
|      * @param Carbon          $end | ||||
|      * @param Collection|null $accounts | ||||
|      * @param Collection|null $tags | ||||
|      * | ||||
|      * @return array | ||||
|      */ | ||||
|     public function listIncome(Carbon $start, Carbon $end, ?Collection $accounts = null, ?Collection $tags = null): array | ||||
|     { | ||||
|         /** @var GroupCollectorInterface $collector */ | ||||
|         $collector = app(GroupCollectorInterface::class); | ||||
|         $collector->setUser($this->user)->setRange($start, $end)->setTypes([TransactionType::DEPOSIT]); | ||||
|         if (null !== $accounts && $accounts->count() > 0) { | ||||
|             $collector->setAccounts($accounts); | ||||
|         } | ||||
|         if (null !== $tags && $tags->count() > 0) { | ||||
|             $collector->setTags($tags); | ||||
|         } | ||||
|         if (null === $tags || (null !== $tags && 0 === $tags->count())) { | ||||
|             $collector->setTags($this->getTags()); | ||||
|         } | ||||
|         $collector->withCategoryInformation()->withAccountInformation()->withBudgetInformation()->withTagInformation(); | ||||
|         $journals       = $collector->getExtractedJournals(); | ||||
|         $array          = []; | ||||
|         $listedJournals = []; | ||||
|  | ||||
|         foreach ($journals as $journal) { | ||||
|             $currencyId         = (int)$journal['currency_id']; | ||||
|             $array[$currencyId] = $array[$currencyId] ?? [ | ||||
|                     'tags'                    => [], | ||||
|                     'currency_id'             => $currencyId, | ||||
|                     'currency_name'           => $journal['currency_name'], | ||||
|                     'currency_symbol'         => $journal['currency_symbol'], | ||||
|                     'currency_code'           => $journal['currency_code'], | ||||
|                     'currency_decimal_places' => $journal['currency_decimal_places'], | ||||
|                 ]; | ||||
|  | ||||
|             // may have multiple tags: | ||||
|             foreach ($journal['tags'] as $tag) { | ||||
|                 $tagId     = (int)$tag['id']; | ||||
|                 $tagName   = (string)$tag['name']; | ||||
|                 $journalId = (int)$journal['transaction_journal_id']; | ||||
|  | ||||
|                 if (in_array($journalId, $listedJournals, true)) { | ||||
|                     continue; | ||||
|                 } | ||||
|                 $listedJournals[] = $journalId; | ||||
|  | ||||
|                 $array[$currencyId]['tags'][$tagId]                                     = $array[$currencyId]['tags'][$tagId] ?? [ | ||||
|                         'id'                   => $tagId, | ||||
|                         'name'                 => $tagName, | ||||
|                         'transaction_journals' => [], | ||||
|                     ]; | ||||
|                 $journalId                                                              = (int)$journal['transaction_journal_id']; | ||||
|                 $array[$currencyId]['tags'][$tagId]['transaction_journals'][$journalId] = [ | ||||
|                     'amount'                   => app('steam')->positive($journal['amount']), | ||||
|                     'date'                     => $journal['date'], | ||||
|                     'source_account_id'        => $journal['source_account_id'], | ||||
|                     'budget_name'              => $journal['budget_name'], | ||||
|                     'source_account_name'      => $journal['source_account_name'], | ||||
|                     'destination_account_id'   => $journal['destination_account_id'], | ||||
|                     'destination_account_name' => $journal['destination_account_name'], | ||||
|                     'description'              => $journal['description'], | ||||
|                     'transaction_group_id'     => $journal['transaction_group_id'], | ||||
|                 ]; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         return $array; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param User $user | ||||
|      */ | ||||
|     public function setUser(User $user): void | ||||
|     { | ||||
|         $this->user = $user; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Sum of withdrawal journals in period for a set of tags, grouped per currency. Amounts are always negative. | ||||
|      * | ||||
|      * @param Carbon          $start | ||||
|      * @param Carbon          $end | ||||
|      * @param Collection|null $accounts | ||||
|      * @param Collection|null $tags | ||||
|      * | ||||
|      * @return array | ||||
|      */ | ||||
|     public function sumExpenses(Carbon $start, Carbon $end, ?Collection $accounts = null, ?Collection $tags = null): array | ||||
|     { | ||||
|         throw new FireflyException(sprintf('%s is not yet implemented.', __METHOD__)); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Sum of income journals in period for a set of tags, grouped per currency. Amounts are always positive. | ||||
|      * | ||||
|      * @param Carbon          $start | ||||
|      * @param Carbon          $end | ||||
|      * @param Collection|null $accounts | ||||
|      * @param Collection|null $tags | ||||
|      * | ||||
|      * @return array | ||||
|      */ | ||||
|     public function sumIncome(Carbon $start, Carbon $end, ?Collection $accounts = null, ?Collection $tags = null): array | ||||
|     { | ||||
|         throw new FireflyException(sprintf('%s is not yet implemented.', __METHOD__)); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @return Collection | ||||
|      */ | ||||
|     private function getTags(): Collection | ||||
|     { | ||||
|         $repository = app(TagRepositoryInterface::class); | ||||
|  | ||||
|         return $repository->get(); | ||||
|     } | ||||
| } | ||||
							
								
								
									
										93
									
								
								app/Repositories/Tag/OperationsRepositoryInterface.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										93
									
								
								app/Repositories/Tag/OperationsRepositoryInterface.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,93 @@ | ||||
| <?php | ||||
| /** | ||||
|  * OperationsRepositoryInterface.php | ||||
|  * Copyright (c) 2019 thegrumpydictator@gmail.com | ||||
|  * | ||||
|  * This file is part of Firefly III. | ||||
|  * | ||||
|  * 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 | ||||
|  * along with Firefly III. If not, see <http://www.gnu.org/licenses/>. | ||||
|  */ | ||||
|  | ||||
| declare(strict_types=1); | ||||
|  | ||||
| namespace FireflyIII\Repositories\Tag; | ||||
|  | ||||
| use Carbon\Carbon; | ||||
| use FireflyIII\User; | ||||
| use Illuminate\Support\Collection; | ||||
|  | ||||
| /** | ||||
|  * Interface OperationsRepositoryInterface | ||||
|  * | ||||
|  * @package FireflyIII\Repositories\Tag | ||||
|  */ | ||||
| interface OperationsRepositoryInterface | ||||
| { | ||||
|     /** | ||||
|      * This method returns a list of all the withdrawal transaction journals (as arrays) set in that period | ||||
|      * which have the specified tag(s) set to them. It's grouped per currency, with as few details in the array | ||||
|      * as possible. Amounts are always negative. | ||||
|      * | ||||
|      * @param Carbon          $start | ||||
|      * @param Carbon          $end | ||||
|      * @param Collection|null $accounts | ||||
|      * @param Collection|null $tags | ||||
|      * | ||||
|      * @return array | ||||
|      */ | ||||
|     public function listExpenses(Carbon $start, Carbon $end, ?Collection $accounts = null, ?Collection $tags = null): array; | ||||
|  | ||||
|     /** | ||||
|      * This method returns a list of all the deposit transaction journals (as arrays) set in that period | ||||
|      * which have the specified tag(s) set to them. It's grouped per currency, with as few details in the array | ||||
|      * as possible. Amounts are always positive. | ||||
|      * | ||||
|      * @param Carbon          $start | ||||
|      * @param Carbon          $end | ||||
|      * @param Collection|null $accounts | ||||
|      * @param Collection|null $tags | ||||
|      * | ||||
|      * @return array | ||||
|      */ | ||||
|     public function listIncome(Carbon $start, Carbon $end, ?Collection $accounts = null, ?Collection $tags = null): array; | ||||
|  | ||||
|     /** | ||||
|      * @param User $user | ||||
|      */ | ||||
|     public function setUser(User $user): void; | ||||
|  | ||||
|     /** | ||||
|      * Sum of withdrawal journals in period for a set of tags, grouped per currency. Amounts are always negative. | ||||
|      * | ||||
|      * @param Carbon          $start | ||||
|      * @param Carbon          $end | ||||
|      * @param Collection|null $accounts | ||||
|      * @param Collection|null $tags | ||||
|      * | ||||
|      * @return array | ||||
|      */ | ||||
|     public function sumExpenses(Carbon $start, Carbon $end, ?Collection $accounts = null, ?Collection $tags = null): array; | ||||
|  | ||||
|     /** | ||||
|      * Sum of income journals in period for a set of tags, grouped per currency. Amounts are always positive. | ||||
|      * | ||||
|      * @param Carbon          $start | ||||
|      * @param Carbon          $end | ||||
|      * @param Collection|null $accounts | ||||
|      * @param Collection|null $tags | ||||
|      * | ||||
|      * @return array | ||||
|      */ | ||||
|     public function sumIncome(Carbon $start, Carbon $end, ?Collection $accounts = null, ?Collection $tags = null): array; | ||||
| } | ||||
							
								
								
									
										27
									
								
								public/v1/js/ff/reports/category/month.js
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										27
									
								
								public/v1/js/ff/reports/category/month.js
									
									
									
									
										vendored
									
									
								
							| @@ -19,30 +19,6 @@ | ||||
|  */ | ||||
| $(function () { | ||||
|     "use strict"; | ||||
|     drawChart(); | ||||
|  | ||||
|  | ||||
|     // $('#categories-in-pie-chart-checked').on('change', function () { | ||||
|     //     redrawPieChart(categoryIncomeUri, 'categories-in-pie-chart'); | ||||
|     // }); | ||||
|     // | ||||
|     // $('#categories-out-pie-chart-checked').on('change', function () { | ||||
|     //     redrawPieChart(categoryExpenseUri, 'categories-out-pie-chart'); | ||||
|     // }); | ||||
|     // | ||||
|     // $('#accounts-in-pie-chart-checked').on('change', function () { | ||||
|     //     redrawPieChart(accountIncomeUri, 'accounts-in-pie-chart'); | ||||
|     // }); | ||||
|     // | ||||
|     // $('#accounts-out-pie-chart-checked').on('change', function () { | ||||
|     //     redrawPieChart(accountExpenseUri, 'accounts-out-pie-chart'); | ||||
|     // }); | ||||
|  | ||||
| }); | ||||
|  | ||||
|  | ||||
| function drawChart() { | ||||
|     "use strict"; | ||||
|  | ||||
|     loadAjaxPartial('accountsHolder', accountsUri); | ||||
|     loadAjaxPartial('categoriesHolder', categoriesUri); | ||||
| @@ -65,4 +41,5 @@ function drawChart() { | ||||
|     loadAjaxPartial('avgExpensesHolder', avgExpensesUri); | ||||
|     loadAjaxPartial('topIncomeHolder', topIncomeUri); | ||||
|     loadAjaxPartial('avgIncomeHolder', avgIncomeUri); | ||||
| } | ||||
|  | ||||
| }); | ||||
							
								
								
									
										68
									
								
								public/v1/js/ff/reports/tag/month.js
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										68
									
								
								public/v1/js/ff/reports/tag/month.js
									
									
									
									
										vendored
									
									
								
							| @@ -18,61 +18,31 @@ | ||||
|  * along with Firefly III. If not, see <http://www.gnu.org/licenses/>. | ||||
|  */ | ||||
|  | ||||
| /** global: tagIncomeUri, tagExpenseUri, accountIncomeUri, accountExpenseUri, tagBudgetUri, tagCategoryUri, mainUri */ | ||||
|  | ||||
| $(function () { | ||||
|     "use strict"; | ||||
|     drawChart(); | ||||
|     loadAjaxPartial('accountsHolder', accountsUri); | ||||
|     loadAjaxPartial('tagsHolder', tagsUri); | ||||
|     loadAjaxPartial('accountPerTagHolder', accountPerTagUri); | ||||
|  | ||||
|     $('#tags-in-pie-chart-checked').on('change', function () { | ||||
|         redrawPieChart('tags-in-pie-chart', tagIncomeUri); | ||||
|     $.each($('.main_tag_canvas'), function (i, v) { | ||||
|         var canvas = $(v); | ||||
|         columnChart(canvas.data('url'), canvas.attr('id')); | ||||
|     }); | ||||
|  | ||||
|     $('#tags-out-pie-chart-checked').on('change', function () { | ||||
|         redrawPieChart('tags-out-pie-chart', tagExpenseUri); | ||||
|     }); | ||||
|     multiCurrencyPieChart(tagOutUri, 'tag-out-pie-chart'); | ||||
|     multiCurrencyPieChart(tagInUri, 'tag-in-pie-chart'); | ||||
|     multiCurrencyPieChart(categoryOutUri, 'category-out-pie-chart'); | ||||
|     multiCurrencyPieChart(categoryInUri, 'category-in-pie-chart'); | ||||
|     multiCurrencyPieChart(budgetsOutUri, 'budgets-out-pie-chart'); | ||||
|     multiCurrencyPieChart(sourceOutUri, 'source-out-pie-chart'); | ||||
|     multiCurrencyPieChart(sourceInUri, 'source-in-pie-chart'); | ||||
|     multiCurrencyPieChart(destOutUri, 'dest-out-pie-chart'); | ||||
|     multiCurrencyPieChart(destInUri, 'dest-in-pie-chart'); | ||||
|  | ||||
|     $('#accounts-in-pie-chart-checked').on('change', function () { | ||||
|         redrawPieChart('accounts-in-pie-chart', accountIncomeUri); | ||||
|     }); | ||||
|  | ||||
|     $('#accounts-out-pie-chart-checked').on('change', function () { | ||||
|         redrawPieChart('accounts-out-pie-chart', accountExpenseUri); | ||||
|     }); | ||||
|  | ||||
|     // two extra charts: | ||||
|     pieChart(tagBudgetUri, 'budgets-out-pie-chart'); | ||||
|     pieChart(tagCategoryUri, 'categories-out-pie-chart'); | ||||
|     loadAjaxPartial('topExpensesHolder', topExpensesUri); | ||||
|     loadAjaxPartial('avgExpensesHolder', avgExpensesUri); | ||||
|     loadAjaxPartial('topIncomeHolder', topIncomeUri); | ||||
|     loadAjaxPartial('avgIncomeHolder', avgIncomeUri); | ||||
|  | ||||
| }); | ||||
|  | ||||
|  | ||||
| function drawChart() { | ||||
|     "use strict"; | ||||
|  | ||||
|     // month view: | ||||
|     doubleYChart(mainUri, 'in-out-chart'); | ||||
|  | ||||
|     // draw pie chart of income, depending on "show other transactions too": | ||||
|     redrawPieChart('tags-in-pie-chart', tagIncomeUri); | ||||
|     redrawPieChart('tags-out-pie-chart', tagExpenseUri); | ||||
|     redrawPieChart('accounts-in-pie-chart', accountIncomeUri); | ||||
|     redrawPieChart('accounts-out-pie-chart', accountExpenseUri); | ||||
|  | ||||
|  | ||||
| } | ||||
|  | ||||
| function redrawPieChart(container, uri) { | ||||
|     "use strict"; | ||||
|     var checkbox = $('#' + container + '-checked'); | ||||
|  | ||||
|     var others = '0'; | ||||
|     // check if box is checked: | ||||
|     if (checkbox.prop('checked')) { | ||||
|         others = '1'; | ||||
|     } | ||||
|     uri = uri.replace('OTHERS', others); | ||||
|  | ||||
|     pieChart(uri, container); | ||||
|  | ||||
| } | ||||
|   | ||||
| @@ -897,9 +897,15 @@ return [ | ||||
|     'income_per_destination_account'            => 'Income per destination account', | ||||
|     'spent_in_specific_category'                => 'Spent in category ":category"', | ||||
|     'earned_in_specific_category'               => 'Earned in category ":category"', | ||||
|     'spent_in_specific_tag'                     => 'Spent in tag ":tag"', | ||||
|     'earned_in_specific_tag'                    => 'Earned in tag ":tag"', | ||||
|     'income_per_source_account'                 => 'Income per source account', | ||||
|     'average_spending_per_destination'          => 'Average expense per destination account', | ||||
|     'average_earning_per_source'                => 'Average earning per source account', | ||||
|     'account_per_tag'                           => 'Account per tag', | ||||
|     'tag_report_expenses_listed_once'           => 'Expenses and income are never listed twice. If a transaction has multiple tags, it may only show up under one of its tags. This list may appear to be missing data, but the amounts will be correct.', | ||||
|     'tag_report_chart_single_tag'               => 'This chart applies to a single tag. If a transaction has multiple tags, what you see here may be reflected in the charts of other tags as well.', | ||||
|     'tag'                                       => 'Tag', | ||||
|     'no_budget_squared'                         => '(no budget)', | ||||
|     'perm-delete-many'                          => 'Deleting many items in one go can be very disruptive. Please be cautious. You can delete part of a split transaction from this page, so take care.', | ||||
|     'mass_deleted_transactions_success'         => 'Deleted :amount transaction(s).', | ||||
|   | ||||
| @@ -1,93 +1,114 @@ | ||||
| {% extends "./layout/default" %} | ||||
|  | ||||
| {% block breadcrumbs %} | ||||
|     {{ Breadcrumbs.render(Route.getCurrentRoute.getName, accountIds, tagTags, start, end) }} | ||||
|     {#{{ Breadcrumbs.render(Route.getCurrentRoute.getName, accountIds, tagTags, start, end) }}#} | ||||
| {% endblock %} | ||||
|  | ||||
| {% block content %} | ||||
|  | ||||
|     <div class="row"> | ||||
|         <div class="col-lg-6 col-md-6"> | ||||
|         <div class="col-lg-6 col-md-6 col-sm-12 col-xs-12"> | ||||
|             <div class="box"> | ||||
|                 <div class="box-header with-border"> | ||||
|                     <h3 class="box-title">{{ 'accounts'|_ }}</h3> | ||||
|                 </div> | ||||
|                 <div class="box-body table-responsive no-padding"> | ||||
|                     <table class="table table-hover sortable"> | ||||
|                         <thead> | ||||
|                         <tr> | ||||
|                             <th data-defaultsign="az">{{ 'name'|_ }}</th> | ||||
|                             <th data-defaultsign="_19" style="text-align: right;">{{ 'earned'|_ }}</th> | ||||
|                             <th data-defaultsign="_19" style="text-align: right;">{{ 'spent'|_ }}</th> | ||||
|                         </tr> | ||||
|                         </thead> | ||||
|                         <tbody> | ||||
|                         {% for account in accounts %} | ||||
|                             <tr> | ||||
|                                 <td data-value="{{ account.name }}"> | ||||
|                                     <a href="{{ route('accounts.show', account.id) }}" title="{{ account.name }}">{{ account.name }}</a> | ||||
|                                 </td> | ||||
|                                 {% if accountSummary[account.id] %} | ||||
|                                     <td data-value="{{ accountSummary[account.id].earned }}" | ||||
|                                         style="text-align: right;">{{ accountSummary[account.id].earned|formatAmount }}</td> | ||||
|                                 {% else %} | ||||
|                                     <td data-value="0" style="text-align: right;">{{ 0|formatAmount }}</td> | ||||
|                                 {% endif %} | ||||
|                                 {% if accountSummary[account.id] %} | ||||
|                                     <td data-value="{{ accountSummary[account.id].spent }}" | ||||
|                                         style="text-align: right;">{{ accountSummary[account.id].spent|formatAmount }}</td> | ||||
|                                 {% else %} | ||||
|                                     <td data-value="0" style="text-align: right;">{{ 0|formatAmount }}</td> | ||||
|                                 {% endif %} | ||||
|                             </tr> | ||||
|                         {% endfor %} | ||||
|                         </tbody> | ||||
|                         <tfoot> | ||||
|                         <tr> | ||||
|                             <td>{{ 'sum'|_ }}</td> | ||||
|                             <td style="text-align: right;">{{ accountSummary.sum.earned|formatAmount }}</td> | ||||
|                             <td style="text-align: right;">{{ accountSummary.sum.spent|formatAmount }}</td> | ||||
|                         </tr> | ||||
|                         </tfoot> | ||||
|                     </table> | ||||
|                 <div class="box-body table-responsive no-padding" id="accountsHolder"> | ||||
|                 </div> | ||||
|                 {# loading indicator #} | ||||
|                 <div class="overlay"> | ||||
|                     <i class="fa fa-refresh fa-spin"></i> | ||||
|                 </div> | ||||
|             </div> | ||||
|         </div> | ||||
|         <div class="col-lg-2 col-md-3"> | ||||
|             <div class="box" id="pieCharts"> | ||||
|         <div class="col-lg-6 col-md-6 col-sm-12 col-xs-12"> | ||||
|             <div class="box"> | ||||
|                 <div class="box-header with-border"> | ||||
|                     <h3 class="box-title">{{ 'income_per_tag'|_ }}</h3> | ||||
|                     <h3 class="box-title">{{ 'tags'|_ }}</h3> | ||||
|                 </div> | ||||
|                 <div class="box-body"> | ||||
|                     <canvas id="tags-in-pie-chart" style="margin:0 auto;" height="150" width="150"></canvas> | ||||
|                     <label style="font-weight:normal;"> | ||||
|                         <input type="checkbox" id="tags-in-pie-chart-checked"> | ||||
|                         <small>{{ 'include_income_not_in_tags'|_ }}</small> | ||||
|                     </label> | ||||
|                 <div class="box-body table-responsive no-padding" id="tagsHolder"> | ||||
|                 </div> | ||||
|                 {# loading indicator #} | ||||
|                 <div class="overlay"> | ||||
|                     <i class="fa fa-refresh fa-spin"></i> | ||||
|                 </div> | ||||
|             </div> | ||||
|         </div> | ||||
|         <div class="col-lg-2 col-md-3"> | ||||
|     </div> | ||||
|     <div class="row"> | ||||
|         <div class="col-lg-12 col-md-12 col-sm-12 col-xs-12"> | ||||
|             <div class="box"> | ||||
|                 <div class="box-header with-border"> | ||||
|                     <h3 class="box-title">{{ 'account_per_tag'|_ }}</h3> | ||||
|                 </div> | ||||
|                 <div class="box-body table-responsive no-padding" id="accountPerTagHolder"> | ||||
|                 </div> | ||||
|                 {# loading indicator #} | ||||
|                 <div class="overlay"> | ||||
|                     <i class="fa fa-refresh fa-spin"></i> | ||||
|                 </div> | ||||
|             </div> | ||||
|         </div> | ||||
|     </div> | ||||
|     <div class="row"> | ||||
|         <div class="col-lg-6 col-md-6"> | ||||
|             <div class="box"> | ||||
|                 <div class="box-header with-border"> | ||||
|                     <h3 class="box-title">{{ 'expense_per_tag'|_ }}</h3> | ||||
|                 </div> | ||||
|                 <div class="box-body"> | ||||
|                     <canvas id="tags-out-pie-chart" style="margin:0 auto;" height="150" width="150"></canvas> | ||||
|                     <label style="font-weight:normal;"> | ||||
|                         <input type="checkbox" id="tags-out-pie-chart-checked"> | ||||
|                         <small>{{ 'include_expense_not_in_tags'|_ }}</small> | ||||
|                     </label> | ||||
|                     <div style="width:100%;margin:0 auto;"> | ||||
|                         <canvas id="tag-out-pie-chart" style="width:100%;height:250px;" height="250"></canvas> | ||||
|                     </div> | ||||
|                 </div> | ||||
|             </div> | ||||
|         </div> | ||||
|         <div class="col-lg-2 col-md-3"> | ||||
|         <div class="col-lg-6 col-md-6"> | ||||
|             <div class="box"> | ||||
|                 <div class="box-header with-border"> | ||||
|                     <h3 class="box-title">{{ 'income_per_tag'|_ }}</h3> | ||||
|                 </div> | ||||
|                 <div class="box-body"> | ||||
|                     <div style="width:100%;margin:0 auto;"> | ||||
|                         <canvas id="tag-in-pie-chart" style="width:100%;height:250px;" height="250"></canvas> | ||||
|                     </div> | ||||
|                 </div> | ||||
|             </div> | ||||
|         </div> | ||||
|     </div> | ||||
|     <div class="row"> | ||||
|         <div class="col-lg-4 col-md-6"> | ||||
|             <div class="box"> | ||||
|                 <div class="box-header with-border"> | ||||
|                     <h3 class="box-title">{{ 'expense_per_category'|_ }}</h3> | ||||
|                 </div> | ||||
|                 <div class="box-body"> | ||||
|                     <div style="width:100%;margin:0 auto;"> | ||||
|                         <canvas id="category-out-pie-chart" style="width:100%;height:250px;" height="250"></canvas> | ||||
|                     </div> | ||||
|                 </div> | ||||
|             </div> | ||||
|         </div> | ||||
|         <div class="col-lg-4 col-md-6"> | ||||
|             <div class="box"> | ||||
|                 <div class="box-header with-border"> | ||||
|                     <h3 class="box-title">{{ 'income_per_category'|_ }}</h3> | ||||
|                 </div> | ||||
|                 <div class="box-body"> | ||||
|                     <div style="width:100%;margin:0 auto;"> | ||||
|                         <canvas id="category-in-pie-chart" style="width:100%;height:250px;" height="250"></canvas> | ||||
|                     </div> | ||||
|                 </div> | ||||
|             </div> | ||||
|         </div> | ||||
|         <div class="col-lg-4 col-md-6"> | ||||
|             <div class="box"> | ||||
|                 <div class="box-header with-border"> | ||||
|                     <h3 class="box-title">{{ 'expense_per_budget'|_ }}</h3> | ||||
|                 </div> | ||||
|                 <div class="box-body"> | ||||
|                     <canvas id="budgets-out-pie-chart" style="margin:0 auto;" height="150" width="150"></canvas> | ||||
|                     <div style="width:100%;margin:0 auto;"> | ||||
|                         <canvas id="budgets-out-pie-chart" style="width:100%;height:250px;" height="250"></canvas> | ||||
|                     </div> | ||||
|                 </div> | ||||
|             </div> | ||||
|         </div> | ||||
| @@ -96,355 +117,128 @@ | ||||
|         <div class="col-lg-6 col-md-6"> | ||||
|             <div class="box"> | ||||
|                 <div class="box-header with-border"> | ||||
|                     <h3 class="box-title">{{ 'tags'|_ }}</h3> | ||||
|                     <h3 class="box-title">{{ 'expense_per_source_account'|_ }}</h3> | ||||
|                 </div> | ||||
|                 <div class="box-body table-responsive no-padding"> | ||||
|                     <table class="table table-hover sortable"> | ||||
|                         <thead> | ||||
|                         <tr> | ||||
|                             <th data-defaultsign="az">{{ 'name'|_ }}</th> | ||||
|                             <th data-defaultsign="_19" style="text-align: right;">{{ 'earned'|_ }}</th> | ||||
|                             <th data-defaultsign="_19" style="text-align: right;">{{ 'spent'|_ }}</th> | ||||
|                         </tr> | ||||
|                         </thead> | ||||
|                         <tbody> | ||||
|                         {% for tag in tags %} | ||||
|                             <tr> | ||||
|                                 <td data-value="{{ tag.tag }}"> | ||||
|                                     <a href="{{ route('tags.show', tag.id) }}" title="{{ tag.tag }}">{{ tag.tag }}</a> | ||||
|                                 </td> | ||||
|                                 {% if tagSummary[tag.id] %} | ||||
|                                     <td data-value="{{ tagSummary[tag.id].earned }}" | ||||
|                                         style="text-align: right;">{{ tagSummary[tag.id].earned|formatAmount }}</td> | ||||
|                                 {% else %} | ||||
|                                     <td data-value="0" style="text-align: right;">{{ 0|formatAmount }}</td> | ||||
|                                 {% endif %} | ||||
|                                 {% if tagSummary[tag.id] %} | ||||
|                                     <td data-value="{{ tagSummary[tag.id].spent }}" style="text-align: right;">{{ tagSummary[tag.id].spent|formatAmount }}</td> | ||||
|                                 {% else %} | ||||
|                                     <td data-value="0" style="text-align: right;">{{ 0|formatAmount }}</td> | ||||
|                                 {% endif %} | ||||
|                             </tr> | ||||
|                         {% endfor %} | ||||
|                         </tbody> | ||||
|                         <tfoot> | ||||
|                         <tr> | ||||
|                             <td>{{ 'sum'|_ }}</td> | ||||
|                             <td style="text-align: right;">{{ tagSummary.sum.earned|formatAmount }}</td> | ||||
|                             <td style="text-align: right;">{{ tagSummary.sum.spent|formatAmount }}</td> | ||||
|                         </tr> | ||||
|                         </tfoot> | ||||
|                     </table> | ||||
|                 <div class="box-body"> | ||||
|                     <div style="width:100%;margin:0 auto;"> | ||||
|                         <canvas id="source-out-pie-chart" style="width:100%;height:250px;" height="250"></canvas> | ||||
|                     </div> | ||||
|                 </div> | ||||
|             </div> | ||||
|         </div> | ||||
|         <div class="col-lg-2 col-md-3"> | ||||
|         <div class="col-lg-6 col-md-6"> | ||||
|             <div class="box"> | ||||
|                 <div class="box-header with-border"> | ||||
|                     <h3 class="box-title">{{ 'income_per_account'|_ }}</h3> | ||||
|                     <h3 class="box-title">{{ 'income_per_source_account'|_ }}</h3> | ||||
|                 </div> | ||||
|                 <div class="box-body"> | ||||
|                     <canvas id="accounts-in-pie-chart" style="margin:0 auto;" height="150" width="150"></canvas> | ||||
|                     <label style="font-weight:normal;"> | ||||
|                         <input type="checkbox" id="accounts-in-pie-chart-checked"> | ||||
|                         <small>{{ 'include_income_not_in_account'|_ }}</small> | ||||
|                     </label> | ||||
|                 </div> | ||||
|             </div> | ||||
|         </div> | ||||
|         <div class="col-lg-2 col-md-3"> | ||||
|             <div class="box"> | ||||
|                 <div class="box-header with-border"> | ||||
|                     <h3 class="box-title">{{ 'expense_per_account'|_ }}</h3> | ||||
|                 </div> | ||||
|                 <div class="box-body"> | ||||
|                     <canvas id="accounts-out-pie-chart" style="margin:0 auto;" height="150" width="150"></canvas> | ||||
|                     <label style="font-weight:normal;"> | ||||
|                         <input type="checkbox" id="accounts-out-pie-chart-checked"> | ||||
|                         <small>{{ 'include_income_not_in_account'|_ }}</small> | ||||
|                     </label> | ||||
|                 </div> | ||||
|             </div> | ||||
|         </div> | ||||
|  | ||||
|         <div class="col-lg-2 col-md-3"> | ||||
|             <div class="box"> | ||||
|                 <div class="box-header with-border"> | ||||
|                     <h3 class="box-title">{{ 'expense_per_category'|_ }}</h3> | ||||
|                 </div> | ||||
|                 <div class="box-body"> | ||||
|                     <canvas id="categories-out-pie-chart" style="margin:0 auto;" height="150" width="150"></canvas> | ||||
|                 </div> | ||||
|             </div> | ||||
|         </div> | ||||
|  | ||||
|     </div> | ||||
|  | ||||
|     <div class="row"> | ||||
|         <div class="col-lg-12"> | ||||
|             <div class="box" id="incomeAndExpensesChart"> | ||||
|                 <div class="box-header with-border"> | ||||
|                     <h3 class="box-title">{{ 'income_and_expenses'|_ }}</h3> | ||||
|                 </div> | ||||
|                 <div class="box-body"> | ||||
|                     <canvas id="in-out-chart" style="width:100%;height:400px;" height="400" width="100%"></canvas> | ||||
|                     <div style="width:100%;margin:0 auto;"> | ||||
|                         <canvas id="source-in-pie-chart" style="width:100%;height:250px;" height="250"></canvas> | ||||
|                     </div> | ||||
|                 </div> | ||||
|             </div> | ||||
|         </div> | ||||
|     </div> | ||||
|     <div class="row"> | ||||
|         {% if averageExpenses|length > 0 %} | ||||
|             <div class="col-lg-6"> | ||||
|                 <div class="box"> | ||||
|                     <div class="box-header with-border"> | ||||
|                         <h3 class="box-title">{{ 'average_spending_per_account'|_ }}</h3> | ||||
|                     </div> | ||||
|                     <div class="box-body table-responsive no-padding"> | ||||
|                         <table class="table table-hover sortable"> | ||||
|                             <thead> | ||||
|                             <tr> | ||||
|                                 <th data-defaultsign="az">{{ 'account'|_ }}</th> | ||||
|                                 <th data-defaultsign="_19" style="text-align: right;">{{ 'spent_average'|_ }}</th> | ||||
|                                 <th data-defaultsign="_19" style="text-align: right;">{{ 'total'|_ }}</th> | ||||
|                                 <th data-defaultsign="_19">{{ 'transaction_count'|_ }}</th> | ||||
|                             </tr> | ||||
|                             </thead> | ||||
|                             <tbody> | ||||
|                             {% set totalCount = 0 %} | ||||
|                             {% set totalSum = 0 %} | ||||
|                             {% for row in averageExpenses %} | ||||
|                                 {% set totalCount = totalCount+ row.count %} | ||||
|                                 {% set totalSum = totalSum + row.sum %} | ||||
|                                 {% if loop.index > listLength %} | ||||
|                                     <tr class="overListLength"> | ||||
|                                 {% else %} | ||||
|                                     <tr> | ||||
|                                 {% endif %} | ||||
|                                 <td data-value="{{ row.name }}"> | ||||
|                                     <a href="{{ route('accounts.show', row.id) }}">{{ row.name }}</a> | ||||
|                                 </td> | ||||
|                                 <td data-value="{{ row.average }}" style="text-align: right;"> | ||||
|                                     {{ row.average|formatAmount }} | ||||
|                                 </td> | ||||
|                                 <td data-value="{{ row.sum }}" style="text-align: right;"> | ||||
|                                     {{ row.sum|formatAmount }} | ||||
|                                 </td> | ||||
|                                 <td data-value="{{ row.count }}"> | ||||
|                                     {{ row.count }} | ||||
|                                 </td> | ||||
|                                 </tr> | ||||
|                             {% endfor %} | ||||
|                             </tbody> | ||||
|                             <tfoot> | ||||
|                             {% if averageExpenses|length > listLength %} | ||||
|                                 <tr> | ||||
|                                     <td colspan="4" class="active"> | ||||
|                                         <a href="#" class="listLengthTrigger">{{ trans('firefly.show_full_list',{number:incomeTopLength}) }}</a> | ||||
|                                     </td> | ||||
|                                 </tr> | ||||
|                             {% endif %} | ||||
|                             <tr> | ||||
|                                 <td colspan="2"> | ||||
|                                     {{ 'sum'|_ }} | ||||
|                                 </td> | ||||
|                                 <td style="text-align:right">{{ totalSum|formatAmount }}</td> | ||||
|                                 <td>{{ totalCount }}</td> | ||||
|                             </tr> | ||||
|                             </tfoot> | ||||
|                         </table> | ||||
|         <div class="col-lg-6 col-md-6"> | ||||
|             <div class="box"> | ||||
|                 <div class="box-header with-border"> | ||||
|                     <h3 class="box-title">{{ 'expense_per_destination_account'|_ }}</h3> | ||||
|                 </div> | ||||
|                 <div class="box-body"> | ||||
|                     <div style="width:100%;margin:0 auto;"> | ||||
|                         <canvas id="dest-out-pie-chart" style="width:100%;height:250px;" height="250"></canvas> | ||||
|                     </div> | ||||
|                 </div> | ||||
|             </div> | ||||
|         {% endif %} | ||||
|         {% if topExpenses|length > 0 %} | ||||
|             <div class="col-lg-6"> | ||||
|         </div> | ||||
|         <div class="col-lg-6 col-md-6"> | ||||
|             <div class="box"> | ||||
|                 <div class="box-header with-border"> | ||||
|                     <h3 class="box-title">{{ 'income_per_destination_account'|_ }}</h3> | ||||
|                 </div> | ||||
|                 <div class="box-body"> | ||||
|                     <div style="width:100%;margin:0 auto;"> | ||||
|                         <canvas id="dest-in-pie-chart" style="width:100%;height:250px;" height="250"></canvas> | ||||
|                     </div> | ||||
|                 </div> | ||||
|             </div> | ||||
|         </div> | ||||
|     </div> | ||||
|  | ||||
|                 <div class="box"> | ||||
|     {% for tag in tags %} | ||||
|         <div class="row"> | ||||
|             <div class="col-lg-12"> | ||||
|                 <div class="box main_budget_chart"> | ||||
|                     <div class="box-header with-border"> | ||||
|                         <h3 class="box-title">{{ 'expenses'|_ }} ({{ trans('firefly.topX', {number: listLength}) }})</h3> | ||||
|                         <h3 class="box-title">{{ 'income_and_expenses'|_ }} ({{ tag.tag }})</h3> | ||||
|                     </div> | ||||
|                     <div class="box-body"> | ||||
|                         <table class="table table-hover sortable"> | ||||
|                             <thead> | ||||
|                             <tr> | ||||
|                                 <th data-defaultsort="disabled">{{ 'description'|_ }}</th> | ||||
|                                 <th data-defaultsign="month">{{ 'date'|_ }}</th> | ||||
|                                 <th data-defaultsign="az">{{ 'account'|_ }}</th> | ||||
|                                 <th data-defaultsign="_19" style="text-align: right;">{{ 'amount'|_ }}</th> | ||||
|                             </tr> | ||||
|                             </thead> | ||||
|                             <tbody> | ||||
|                             {% set totalSum = 0 %} | ||||
|                             {% for row in topExpenses %} | ||||
|                                 {% set totalSum = totalSum + row.transaction_amount %} | ||||
|                                 {% if loop.index > listLength %} | ||||
|                                     <tr class="overListLength"> | ||||
|                                 {% else %} | ||||
|                                     <tr> | ||||
|                                 {% endif %} | ||||
|                                 <td data-sortable="false"> | ||||
|                                     <a href="{{ route('transactions.show', 1+row.transaction_group_id) }}"> | ||||
|                                         {% if row.group_title|length > 0 %} | ||||
|                                             {{ row.group_title }} ({{ row.description }}) | ||||
|                                         {% else %} | ||||
|                                             {{ row.description }} | ||||
|                                         {% endif %} | ||||
|                                     </a> | ||||
|                                 </td> | ||||
|                                 <td data-value="{{ row.date.format('Y-m-d') }}"> | ||||
|                                     {{ row.date.formatLocalized(monthAndDayFormat) }} | ||||
|                                 </td> | ||||
|                                 <td data-value="{{ row.destination_account_name }}"> | ||||
|                                     <a href="{{ route('accounts.show', row.destination_account_id) }}"> | ||||
|                                         {{ row.destination_account_name }} | ||||
|                                     </a> | ||||
|                                 </td> | ||||
|                                 <td data-value="{{ row.amount }}" style="text-align: right;"> | ||||
|                                     {{ formatAmountBySymbol(row.amount, row.currency_symbol, row.currency_decimal_places) }} | ||||
|                                 </td> | ||||
|                                 </tr> | ||||
|                             {% endfor %} | ||||
|                             </tbody> | ||||
|                             <tfoot> | ||||
|                             {% if topExpenses|length > listLength %} | ||||
|                                 <tr> | ||||
|                                     <td colspan="3" class="active"> | ||||
|                                         <a href="#" class="listLengthTrigger">{{ trans('firefly.show_full_list',{number:incomeTopLength}) }}</a> | ||||
|                                     </td> | ||||
|                                 </tr> | ||||
|                             {% endif %} | ||||
|                             <tr> | ||||
|                                 <td colspan="3"> | ||||
|                                     {{ 'sum'|_ }} | ||||
|                                 </td> | ||||
|                                 <td style="text-align:right">{{ totalSum|formatAmount }}</td> | ||||
|                             </tr> | ||||
|                             </tfoot> | ||||
|                         </table> | ||||
|                         <canvas class="main_tag_canvas" | ||||
|                                 data-url="{{ route('chart.tag.main', [accountIds, tag.id, start.format('Ymd'), end.format('Ymd')]) }}" | ||||
|                                 id="in-out-chart-{{ tag.id }}" style="width:100%;height:400px;" height="400" width="100%"></canvas> | ||||
|                     </div> | ||||
|                     <div class="box-footer"> | ||||
|                         <p class="text-info"><em>{{ 'tag_report_chart_single_tag'|_ }}</em></p> | ||||
|                     </div> | ||||
|                 </div> | ||||
|             </div> | ||||
|         {% endif %} | ||||
|     </div> | ||||
|         </div> | ||||
|     {% endfor %} | ||||
|     <div class="row"> | ||||
|         {% if averageIncome|length > 0 %} | ||||
|             <div class="col-lg-6"> | ||||
|                 <div class="box"> | ||||
|                     <div class="box-header with-border"> | ||||
|                         <h3 class="box-title">{{ 'average_income_per_account'|_ }}</h3> | ||||
|                     </div> | ||||
|                     <div class="box-body"> | ||||
|                         <table class="table table-hover sortable"> | ||||
|                             <thead> | ||||
|                             <tr> | ||||
|                                 <th data-defaultsign="az">{{ 'account'|_ }}</th> | ||||
|                                 <th data-defaultsign="_19" style="text-align:right;">{{ 'income_average'|_ }}</th> | ||||
|                                 <th data-defaultsign="_19" style="text-align:right;">{{ 'total'|_ }}</th> | ||||
|                                 <th data-defaultsign="_19">{{ 'transaction_count'|_ }}</th> | ||||
|                             </tr> | ||||
|                             </thead> | ||||
|                             <tbody> | ||||
|                             {% set totalCount = 0 %} | ||||
|                             {% set totalSum = 0 %} | ||||
|                             {% for row in averageIncome %} | ||||
|                                 {% set totalCount = totalCount+ row.count %} | ||||
|                                 {% set totalSum = totalSum + row.sum %} | ||||
|                                 <tr> | ||||
|                                     <td data-value="{{ row.name }}"> | ||||
|                                         <a href="{{ route('accounts.show', row.id) }}">{{ row.name }}</a> | ||||
|                                     </td> | ||||
|                                     <td data-value="{{ row.average }}" style="text-align:right;"> | ||||
|                                         {{ (row.average*-1)|formatAmount }} | ||||
|                                     </td> | ||||
|                                     <td data-value="{{ row.sum }}" style="text-align:right;"> | ||||
|                                         {{ (row.sum*-1)|formatAmount }} | ||||
|                                     </td> | ||||
|                                     <td data-value="{{ row.count }}"> | ||||
|                                         {{ row.count }} | ||||
|                                     </td> | ||||
|                                 </tr> | ||||
|                             {% endfor %} | ||||
|                             <tr> | ||||
|                                 <td colspan="2"> | ||||
|                                     {{ 'sum'|_ }} | ||||
|                                 </td> | ||||
|                                 <td style="text-align:right">{{ (totalSum*-1)|formatAmount }}</td> | ||||
|                                 <td>{{ totalCount }}</td> | ||||
|                             </tr> | ||||
|                             </tbody> | ||||
|                         </table> | ||||
|                     </div> | ||||
|                 </div> | ||||
|             </div> | ||||
|         {% endif %} | ||||
|         <div class="col-lg-6"> | ||||
|             {% if topIncome|length > 0 %} | ||||
|                 <div class="box"> | ||||
|                     <div class="box-header with-border"> | ||||
|                         <h3 class="box-title">{{ 'income'|_ }} ({{ trans('firefly.topX', {number: listLength}) }})</h3> | ||||
|                     </div> | ||||
|                     <div class="box-body"> | ||||
|                         <table class="table table-hover sortable"> | ||||
|                             <thead> | ||||
|                             <tr> | ||||
|                                 <th data-defaultsort="disabled">{{ 'description'|_ }}</th> | ||||
|                                 <th data-defaultsign="month">{{ 'date'|_ }}</th> | ||||
|                                 <th data-defaultsign="az">{{ 'account'|_ }}</th> | ||||
|                                 <th data-defaultsign="_19" style="text-align:right;">{{ 'amount'|_ }}</th> | ||||
|                             </tr> | ||||
|                             </thead> | ||||
|                             <tbody> | ||||
|                             {% set totalSum = 0 %} | ||||
|                             {% for row in topIncome %} | ||||
|                                 {% set totalSum = totalSum + row.transaction_amount %} | ||||
|                                 {% if loop.index > listLength %} | ||||
|                                     <tr class="overListLength"> | ||||
|                                 {% else %} | ||||
|                                     <tr> | ||||
|                                 {% endif %} | ||||
|                                 <td> | ||||
|                                     <a href="{{ route('transactions.show', row.transaction_journal_id) }}"> | ||||
|                                         {% if row.transaction_description|length > 0 %} | ||||
|                                             {{ row.transaction_description }} ({{ row.description }}) | ||||
|                                         {% else %} | ||||
|                                             {{ row.description }} | ||||
|                                         {% endif %} | ||||
|                                     </a> | ||||
|                                 </td> | ||||
|                                 <td data-value="{{ row.date.format('Y-m-d H-i-s') }}"> | ||||
|                                     {{ row.date.formatLocalized(monthAndDayFormat) }} | ||||
|                                 </td> | ||||
|                                 <td data-value="{{ row.source_account_name }}"> | ||||
|                                     <a href="{{ route('accounts.show', row.source_account_id) }}"> | ||||
|                                         {{ row.source_account_name }} | ||||
|                                     </a> | ||||
|                                 </td> | ||||
|                                 <td data-value="{{ row.transaction_amount*-1 }}" style="text-align:right;"> | ||||
|                                     {{ (row.transaction_amount*-1)|formatAmount }} | ||||
|                                 </td> | ||||
|  | ||||
|                                 </tr> | ||||
|                             {% endfor %} | ||||
|                             </tbody> | ||||
|                             <tfoot> | ||||
|                             {% if topIncome.count > listLength %} | ||||
|                                 <tr> | ||||
|                                     <td colspan="3" class="active"> | ||||
|                                         <a href="#" class="listLengthTrigger">{{ trans('firefly.show_full_list',{number:incomeTopLength}) }}</a> | ||||
|                                     </td> | ||||
|                                 </tr> | ||||
|                             {% endif %} | ||||
|                             <tr> | ||||
|                                 <td colspan="3"> | ||||
|                                     {{ 'sum'|_ }} | ||||
|                                 </td> | ||||
|                                 <td style="text-align:right">{{ (totalSum*-1)|formatAmount }}</td> | ||||
|                             </tr> | ||||
|                             </tfoot> | ||||
|                         </table> | ||||
|                     </div> | ||||
|             <div class="box"> | ||||
|                 <div class="box-header with-border"> | ||||
|                     <h3 class="box-title">{{ 'average_spending_per_destination'|_ }}</h3> | ||||
|                 </div> | ||||
|             {% endif %} | ||||
|                 <div class="box-body table-responsive no-padding" id="avgExpensesHolder"> | ||||
|                 </div> | ||||
|                 {# loading indicator #} | ||||
|                 <div class="overlay"> | ||||
|                     <i class="fa fa-refresh fa-spin"></i> | ||||
|                 </div> | ||||
|             </div> | ||||
|         </div> | ||||
|         <div class="col-lg-6"> | ||||
|             <div class="box"> | ||||
|                 <div class="box-header with-border"> | ||||
|                     <h3 class="box-title">{{ 'average_earning_per_source'|_ }}</h3> | ||||
|                 </div> | ||||
|                 <div class="box-body table-responsive no-padding" id="avgIncomeHolder"> | ||||
|                 </div> | ||||
|                 {# loading indicator #} | ||||
|                 <div class="overlay"> | ||||
|                     <i class="fa fa-refresh fa-spin"></i> | ||||
|                 </div> | ||||
|             </div> | ||||
|         </div> | ||||
|     </div> | ||||
|     <div class="row"> | ||||
|         <div class="col-lg-6"> | ||||
|             <div class="box"> | ||||
|                 <div class="box-header with-border"> | ||||
|                     <h3 class="box-title">{{ 'expenses'|_ }} ({{ trans('firefly.topX', {number: listLength}) }})</h3> | ||||
|                 </div> | ||||
|                 <div class="box-body table-responsive no-padding" id="topExpensesHolder"> | ||||
|                 </div> | ||||
|                 {# loading indicator #} | ||||
|                 <div class="overlay"> | ||||
|                     <i class="fa fa-refresh fa-spin"></i> | ||||
|                 </div> | ||||
|             </div> | ||||
|         </div> | ||||
|         <div class="col-lg-6"> | ||||
|             <div class="box"> | ||||
|                 <div class="box-header with-border"> | ||||
|                     <h3 class="box-title">{{ 'income'|_ }} ({{ trans('firefly.topX', {number: listLength}) }})</h3> | ||||
|                 </div> | ||||
|                 <div class="box-body table-responsive no-padding" id="topIncomeHolder"> | ||||
|                 </div> | ||||
|                 {# loading indicator #} | ||||
|                 <div class="overlay"> | ||||
|                     <i class="fa fa-refresh fa-spin"></i> | ||||
|                 </div> | ||||
|             </div> | ||||
|         </div> | ||||
|     </div> | ||||
|  | ||||
| @@ -456,26 +250,37 @@ | ||||
|     <script type="text/javascript" src="v1/js/ff/charts.js?v={{ FF_VERSION }}"></script> | ||||
|     <script type="text/javascript" src="v1/js/lib/bootstrap-sortable.js?v={{ FF_VERSION }}"></script> | ||||
|  | ||||
|     <script type="text/javascript" src="v1/js/ff/reports/all.js?v={{ FF_VERSION }}"></script> | ||||
|     <script type="text/javascript"> | ||||
|         // to report another URL: | ||||
|         var startDate = '{{ start.format('Ymd') }}'; | ||||
|         var endDate = '{{ end.format('Ymd') }}'; | ||||
|         var accountIds = '{{ accountIds }}'; | ||||
|         var tagTags = '{{ tagTags }}'; | ||||
|         var tagIds = '{{ tagIds }}'; | ||||
|  | ||||
|         // chart uri's | ||||
|         var tagIncomeUri = '{{ route('chart.tag.tag-income', [accountIds, tagIds, start.format('Ymd'), end.format('Ymd'),'OTHERS']) }}'; | ||||
|         var tagExpenseUri = '{{ route('chart.tag.tag-expense', [accountIds, tagIds, start.format('Ymd'), end.format('Ymd'),'OTHERS']) }}'; | ||||
|         var accountIncomeUri = '{{ route('chart.tag.account-income', [accountIds, tagIds, start.format('Ymd'), end.format('Ymd'),'OTHERS']) }}'; | ||||
|         var accountExpenseUri = '{{ route('chart.tag.account-expense', [accountIds, tagIds, start.format('Ymd'), end.format('Ymd'),'OTHERS']) }}'; | ||||
|         // html block URI's: | ||||
|  | ||||
|         var accountsUri = '{{ route('report-data.tag.accounts', [accountIds, tagIds, start.format('Ymd'), end.format('Ymd')]) }}'; | ||||
|         var tagsUri = '{{ route('report-data.tag.tags', [accountIds, tagIds, start.format('Ymd'), end.format('Ymd')]) }}'; | ||||
|         var accountPerTagUri = '{{ route('report-data.tag.account-per-tag', [accountIds, tagIds, start.format('Ymd'), end.format('Ymd')]) }}'; | ||||
|  | ||||
|         // pie charts: | ||||
|         var tagOutUri = '{{ route('chart.tag.tag-expense', [accountIds, tagIds, start.format('Ymd'), end.format('Ymd')]) }}'; | ||||
|         var tagInUri = '{{ route('chart.tag.tag-income', [accountIds, tagIds, start.format('Ymd'), end.format('Ymd')]) }}'; | ||||
|         var categoryOutUri = '{{ route('chart.tag.category-expense', [accountIds, tagIds, start.format('Ymd'), end.format('Ymd')]) }}'; | ||||
|         var categoryInUri = '{{ route('chart.tag.category-income', [accountIds, tagIds, start.format('Ymd'), end.format('Ymd')]) }}'; | ||||
|         var budgetsOutUri = '{{ route('chart.tag.budget-expense', [accountIds, tagIds, start.format('Ymd'), end.format('Ymd')]) }}'; | ||||
|         var sourceOutUri = '{{ route('chart.tag.source-expense', [accountIds, tagIds, start.format('Ymd'), end.format('Ymd')]) }}'; | ||||
|         var sourceInUri = '{{ route('chart.tag.source-income', [accountIds, tagIds, start.format('Ymd'), end.format('Ymd')]) }}'; | ||||
|         var destOutUri = '{{ route('chart.tag.dest-expense', [accountIds, tagIds, start.format('Ymd'), end.format('Ymd')]) }}'; | ||||
|         var destInUri = '{{ route('chart.tag.dest-income', [accountIds, tagIds, start.format('Ymd'), end.format('Ymd')]) }}'; | ||||
|  | ||||
|         var avgExpensesUri = '{{ route('report-data.tag.avg-expenses', [accountIds, tagIds, start.format('Ymd'), end.format('Ymd')]) }}'; | ||||
|         var topExpensesUri = '{{ route('report-data.tag.top-expenses', [accountIds, tagIds, start.format('Ymd'), end.format('Ymd')]) }}'; | ||||
|         var avgIncomeUri = '{{ route('report-data.tag.avg-income', [accountIds, tagIds, start.format('Ymd'), end.format('Ymd')]) }}'; | ||||
|         var topIncomeUri = '{{ route('report-data.tag.top-income', [accountIds, tagIds, start.format('Ymd'), end.format('Ymd')]) }}'; | ||||
|  | ||||
|         // two new charts | ||||
|         var tagBudgetUri = '{{ route('chart.tag.budget-expense', [accountIds, tagIds, start.format('Ymd'), end.format('Ymd')]) }}'; | ||||
|         var tagCategoryUri = '{{ route('chart.tag.category-expense', [accountIds, tagIds, start.format('Ymd'), end.format('Ymd')]) }}'; | ||||
|         var mainUri = '{{ route('chart.tag.main', [accountIds, tagIds, start.format('Ymd'), end.format('Ymd')]) }}'; | ||||
|     </script> | ||||
|  | ||||
|     <script type="text/javascript" src="v1/js/ff/reports/all.js?v={{ FF_VERSION }}"></script> | ||||
|     <script type="text/javascript" src="v1/js/ff/reports/tag/month.js?v={{ FF_VERSION }}"></script> | ||||
|  | ||||
| {% endblock %} | ||||
|   | ||||
							
								
								
									
										40
									
								
								resources/views/v1/reports/tag/partials/account-per-tag.twig
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										40
									
								
								resources/views/v1/reports/tag/partials/account-per-tag.twig
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,40 @@ | ||||
| <table class="table table-hover sortable"> | ||||
|     <thead> | ||||
|     <tr> | ||||
|         <th data-defaultsign="az">{{ 'name'|_ }}</th> | ||||
|         {% for tag in tags %} | ||||
|             <th data-defaultsign="_19" style="text-align: right;">{{ tag.tag }}</th> | ||||
|         {% endfor %} | ||||
|     </tr> | ||||
|     </thead> | ||||
|     <tbody> | ||||
|     {% for account in report %} | ||||
|         {% for currency in account.currencies %} | ||||
|             <tr> | ||||
|                 <td data-value="{{ account.name }} ({{ currency.currency_name }})"> | ||||
|                     <a href="{{ route('accounts.show', account.id) }}" title="{{ account.iban }}">{{ account.name }} ({{ currency.currency_name }})</a> | ||||
|                 </td> | ||||
|                 {% for tag in tags %} | ||||
|                     <td style="text-align: right;"> | ||||
|                         {% if currency.tags[tag.id] %} | ||||
|                             <span title="{{ 'earned'|_ }}: {{ formatAmountBySymbol(currency.tags[tag.id].earned, currency.currency_symbol, currency.currency_decimal_places, false) }}, {{ 'spent'|_ }}: {{ formatAmountBySymbol(currency.tags[tag.id].spent, currency.currency_symbol, currency.currency_decimal_places, false) }}" | ||||
|                             {{ formatAmountBySymbol(currency.tags[tag.id].sum, currency.currency_symbol, currency.currency_decimal_places) }} | ||||
|                         {% else %} | ||||
|                             — | ||||
|                         {% endif %} | ||||
|                     </td> | ||||
|                 {% endfor %} | ||||
|             </tr> | ||||
|         {% endfor %} | ||||
|     {% endfor %} | ||||
|     </tbody> | ||||
|     <tfoot> | ||||
|     <tr> | ||||
|         <td colspan="{{ 1 + tags|length }}"> | ||||
|             <p class="text-info"> | ||||
|                 <em>{{ 'tag_report_expenses_listed_once'|_ }}</em> | ||||
|             </p> | ||||
|         </td> | ||||
|     </tr> | ||||
|     </tfoot> | ||||
| </table> | ||||
							
								
								
									
										46
									
								
								resources/views/v1/reports/tag/partials/accounts.twig
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										46
									
								
								resources/views/v1/reports/tag/partials/accounts.twig
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,46 @@ | ||||
| <table class="table table-hover sortable"> | ||||
|     <thead> | ||||
|     <tr> | ||||
|         <th data-defaultsign="az">{{ 'name'|_ }}</th> | ||||
|         <th data-defaultsign="_19" style="text-align: right;">{{ 'spent'|_ }}</th> | ||||
|         <th data-defaultsign="_19" style="text-align: right;">{{ 'earned'|_ }}</th> | ||||
|         <th data-defaultsign="_19" style="text-align: right;">{{ 'sum'|_ }}</th> | ||||
|     </tr> | ||||
|     </thead> | ||||
|     <tbody> | ||||
|     {% for account in report %} | ||||
|         {% for currency in account.currencies %} | ||||
|             <tr> | ||||
|                 <td data-value="{{ account.name }} ({{ currency.currency_name }})"> | ||||
|                     <a href="{{ route('accounts.show', account.id) }}" title="{{ account.iban }}">{{ account.name }} ({{ currency.currency_name }})</a> | ||||
|                 </td> | ||||
|                 <td data-value="{{ currency.spent }}" style="text-align: right;"> | ||||
|                     {{ formatAmountBySymbol(currency.spent, currency.currency_symbol, currency.currency_decimal_places) }} | ||||
|                 </td> | ||||
|                 <td data-value="{{ currency.earned }}" style="text-align: right;"> | ||||
|                     {{ formatAmountBySymbol(currency.earned, currency.currency_symbol, currency.currency_decimal_places) }} | ||||
|                 </td> | ||||
|                 <td data-value="{{ currency.sum }}" style="text-align: right;"> | ||||
|                     {{ formatAmountBySymbol(currency.sum, currency.currency_symbol, currency.currency_decimal_places) }} | ||||
|                 </td> | ||||
|             </tr> | ||||
|         {% endfor %} | ||||
|     {% endfor %} | ||||
|     </tbody> | ||||
|     <tfoot> | ||||
|     {% for sum in sums %} | ||||
|         <tr> | ||||
|             <td>{{ 'sum'|_ }} ({{ sum.currency_name }})</td> | ||||
|             <td style="text-align: right;"> | ||||
|                 {{ formatAmountBySymbol(sum.spent_sum, sum.currency_symbol, sum.currency_decimal_places) }} | ||||
|             </td> | ||||
|             <td style="text-align: right;"> | ||||
|                 {{ formatAmountBySymbol(sum.earned_sum, sum.currency_symbol, sum.currency_decimal_places) }} | ||||
|             </td> | ||||
|             <td style="text-align: right;"> | ||||
|                 {{ formatAmountBySymbol(sum.total_sum, sum.currency_symbol, sum.currency_decimal_places) }} | ||||
|             </td> | ||||
|         </tr> | ||||
|     {% endfor %} | ||||
|     </tfoot> | ||||
| </table> | ||||
							
								
								
									
										46
									
								
								resources/views/v1/reports/tag/partials/avg-expenses.twig
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										46
									
								
								resources/views/v1/reports/tag/partials/avg-expenses.twig
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,46 @@ | ||||
| <table class="table table-hover sortable"> | ||||
|     <thead> | ||||
|     <tr> | ||||
|         <th data-defaultsign="az">{{ 'account'|_ }}</th> | ||||
|         <th data-defaultsign="_19" style="text-align: right;">{{ 'spent_average'|_ }}</th> | ||||
|         <th data-defaultsign="_19" style="text-align: right;">{{ 'total'|_ }}</th> | ||||
|         <th data-defaultsign="_19">{{ 'transaction_count'|_ }}</th> | ||||
|     </tr> | ||||
|     </thead> | ||||
|     <tbody> | ||||
|     {% for row in result %} | ||||
|         {% if loop.index > listLength %} | ||||
|             <tr class="overListLength"> | ||||
|         {% else %} | ||||
|             <tr> | ||||
|         {% endif %} | ||||
|         <td data-sortable="false"> | ||||
|             <a href="{{ route('accounts.show', row.destination_account_id) }}"> | ||||
|                 {{ row.destination_account_name }} | ||||
|             </a> | ||||
|         </td> | ||||
|         <td data-value="{{ row.avg }}" style="text-align: right;"> | ||||
|             {{ formatAmountBySymbol(row.avg, row.currency_symbol, row.currency_decimal_places) }} | ||||
|         </td> | ||||
|  | ||||
|         <td data-value="{{ row.sum }}" style="text-align: right;"> | ||||
|             {{ formatAmountBySymbol(row.sum, row.currency_symbol, row.currency_decimal_places) }} | ||||
|         </td> | ||||
|  | ||||
|         <td data-value="{{ row.transactions }}"> | ||||
|             {{ row.transactions }} | ||||
|         </td> | ||||
|  | ||||
|         </tr> | ||||
|     {% endfor %} | ||||
|     </tbody> | ||||
|     <tfoot> | ||||
|     {% if result|length > listLength %} | ||||
|         <tr> | ||||
|             <td colspan="5" class="active"> | ||||
|                 <a href="#" class="listLengthTrigger">{{ trans('firefly.show_full_list',{number:incomeTopLength}) }}</a> | ||||
|             </td> | ||||
|         </tr> | ||||
|     {% endif %} | ||||
|     </tfoot> | ||||
| </table> | ||||
							
								
								
									
										46
									
								
								resources/views/v1/reports/tag/partials/avg-income.twig
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										46
									
								
								resources/views/v1/reports/tag/partials/avg-income.twig
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,46 @@ | ||||
| <table class="table table-hover sortable"> | ||||
|     <thead> | ||||
|     <tr> | ||||
|         <th data-defaultsign="az">{{ 'account'|_ }}</th> | ||||
|         <th data-defaultsign="_19" style="text-align: right;">{{ 'spent_average'|_ }}</th> | ||||
|         <th data-defaultsign="_19" style="text-align: right;">{{ 'total'|_ }}</th> | ||||
|         <th data-defaultsign="_19">{{ 'transaction_count'|_ }}</th> | ||||
|     </tr> | ||||
|     </thead> | ||||
|     <tbody> | ||||
|     {% for row in result %} | ||||
|         {% if loop.index > listLength %} | ||||
|             <tr class="overListLength"> | ||||
|         {% else %} | ||||
|             <tr> | ||||
|         {% endif %} | ||||
|         <td data-sortable="false"> | ||||
|             <a href="{{ route('accounts.show', row.source_account_id) }}"> | ||||
|                 {{ row.source_account_name }} | ||||
|             </a> | ||||
|         </td> | ||||
|         <td data-value="{{ row.avg }}" style="text-align: right;"> | ||||
|             {{ formatAmountBySymbol(row.avg, row.currency_symbol, row.currency_decimal_places) }} | ||||
|         </td> | ||||
|  | ||||
|         <td data-value="{{ row.sum }}" style="text-align: right;"> | ||||
|             {{ formatAmountBySymbol(row.sum, row.currency_symbol, row.currency_decimal_places) }} | ||||
|         </td> | ||||
|  | ||||
|         <td data-value="{{ row.transactions }}"> | ||||
|             {{ row.transactions }} | ||||
|         </td> | ||||
|  | ||||
|         </tr> | ||||
|     {% endfor %} | ||||
|     </tbody> | ||||
|     <tfoot> | ||||
|     {% if result|length > listLength %} | ||||
|         <tr> | ||||
|             <td colspan="5" class="active"> | ||||
|                 <a href="#" class="listLengthTrigger">{{ trans('firefly.show_full_list',{number:incomeTopLength}) }}</a> | ||||
|             </td> | ||||
|         </tr> | ||||
|     {% endif %} | ||||
|     </tfoot> | ||||
| </table> | ||||
							
								
								
									
										64
									
								
								resources/views/v1/reports/tag/partials/tags.twig
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										64
									
								
								resources/views/v1/reports/tag/partials/tags.twig
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,64 @@ | ||||
| <table class="table table-hover sortable"> | ||||
|     <thead> | ||||
|     <tr> | ||||
|         <th data-defaultsign="az">{{ 'name'|_ }}</th> | ||||
|         <th data-defaultsign="_19" style="text-align: right;">{{ 'spent'|_ }}</th> | ||||
|         <th data-defaultsign="_19" style="text-align: right;">{{ 'earned'|_ }}</th> | ||||
|         <th data-defaultsign="_19" style="text-align: right;">{{ 'sum'|_ }}</th> | ||||
|     </tr> | ||||
|     </thead> | ||||
|     <tbody> | ||||
|     {% for tag in report %} | ||||
|         {% if tag.currencies|length == 0 %} | ||||
|             <tr> | ||||
|                 <td data-value="{{ tag.name }}"> | ||||
|                     <a href="{{ route('tags.show', tag.id) }}" title="{{ tag.name }}">{{ tag.name }}</a> | ||||
|                 </td> | ||||
|                 <td style="text-align: right;">—</td> | ||||
|                 <td style="text-align: right;">—</td> | ||||
|                 <td style="text-align: right;">—</td> | ||||
|             </tr> | ||||
|         {% endif %} | ||||
|         {% for currency in tag.currencies %} | ||||
|             <tr> | ||||
|                 <td data-value="{{ tag.name }} ({{ currency.currency_name }})"> | ||||
|                     <a href="{{ route('tags.show', tag.id) }}" title="{{ tag.name }}">{{ tag.name }} ({{ currency.currency_name }})</a> | ||||
|                 </td> | ||||
|                 <td data-value="{{ currency.spent }}" style="text-align: right;"> | ||||
|                     {{ formatAmountBySymbol(currency.spent, currency.currency_symbol, currency.currency_decimal_places) }} | ||||
|                 </td> | ||||
|                 <td data-value="{{ currency.earned }}" style="text-align: right;"> | ||||
|                     {{ formatAmountBySymbol(currency.earned, currency.currency_symbol, currency.currency_decimal_places) }} | ||||
|                 </td> | ||||
|                 <td data-value="{{ currency.sum }}" style="text-align: right;"> | ||||
|                     {{ formatAmountBySymbol(currency.sum, currency.currency_symbol, currency.currency_decimal_places) }} | ||||
|                 </td> | ||||
|             </tr> | ||||
|         {% endfor %} | ||||
|     {% endfor %} | ||||
|     </tbody> | ||||
|     <tfoot> | ||||
|     {% for sum in sums %} | ||||
|         <tr> | ||||
|             <td>{{ 'sum'|_ }} ({{ sum.currency_name }})</td> | ||||
|             <td style="text-align: right;"> | ||||
|                 {{ formatAmountBySymbol(sum.spent_sum, sum.currency_symbol, sum.currency_decimal_places) }} | ||||
|             </td> | ||||
|             <td style="text-align: right;"> | ||||
|                 {{ formatAmountBySymbol(sum.earned_sum, sum.currency_symbol, sum.currency_decimal_places) }} | ||||
|             </td> | ||||
|             <td style="text-align: right;"> | ||||
|                 {{ formatAmountBySymbol(sum.total_sum, sum.currency_symbol, sum.currency_decimal_places) }} | ||||
|             </td> | ||||
|         </tr> | ||||
|     {% endfor %} | ||||
|     <tr> | ||||
|         <td colspan="4"> | ||||
|             <p class="text-info"> | ||||
|                 <em>{{ 'tag_report_expenses_listed_once'|_ }}</em> | ||||
|             </p> | ||||
|         </td> | ||||
|     </tr> | ||||
|     </tfoot> | ||||
| </table> | ||||
|  | ||||
							
								
								
									
										54
									
								
								resources/views/v1/reports/tag/partials/top-expenses.twig
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										54
									
								
								resources/views/v1/reports/tag/partials/top-expenses.twig
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,54 @@ | ||||
| <table class="table table-hover sortable"> | ||||
|     <thead> | ||||
|     <tr> | ||||
|         <th data-defaultsort="disabled">{{ 'description'|_ }}</th> | ||||
|         <th data-defaultsign="month">{{ 'date'|_ }}</th> | ||||
|         <th data-defaultsign="az">{{ 'account'|_ }}</th> | ||||
|         <th data-defaultsign="az">{{ 'tag'|_ }}</th> | ||||
|         <th data-defaultsign="_19" style="text-align: right;">{{ 'amount'|_ }}</th> | ||||
|     </tr> | ||||
|     </thead> | ||||
|     <tbody> | ||||
|     {% for row in result %} | ||||
|         {% if loop.index > listLength %} | ||||
|             <tr class="overListLength"> | ||||
|         {% else %} | ||||
|             <tr> | ||||
|         {% endif %} | ||||
|         <td data-sortable="false"> | ||||
|             <a href="{{ route('transactions.show', row.transaction_group_id) }}"> | ||||
|                 {{ row.description }} | ||||
|             </a> | ||||
|         </td> | ||||
|         <td data-sortable="false"> | ||||
|             {{ row.date }} | ||||
|         </td> | ||||
|  | ||||
|         <td data-sortable="false"> | ||||
|             <a href="{{ route('accounts.show', row.destination_account_id) }}"> | ||||
|                 {{ row.destination_account_name }} | ||||
|             </a> | ||||
|         </td> | ||||
|         <td data-sortable="false"> | ||||
|             <a href="{{ route('tags.show', row.tag_id) }}"> | ||||
|                 {{ row.tag_name }} | ||||
|             </a> | ||||
|         </td> | ||||
|  | ||||
|         <td data-value="{{ row.amount }}" style="text-align: right;"> | ||||
|             {{ formatAmountBySymbol(row.amount, row.currency_symbol, row.currency_decimal_places) }} | ||||
|         </td> | ||||
|  | ||||
|         </tr> | ||||
|     {% endfor %} | ||||
|     </tbody> | ||||
|     <tfoot> | ||||
|     {% if result|length > listLength %} | ||||
|         <tr> | ||||
|             <td colspan="5" class="active"> | ||||
|                 <a href="#" class="listLengthTrigger">{{ trans('firefly.show_full_list',{number:incomeTopLength}) }}</a> | ||||
|             </td> | ||||
|         </tr> | ||||
|     {% endif %} | ||||
|     </tfoot> | ||||
| </table> | ||||
							
								
								
									
										54
									
								
								resources/views/v1/reports/tag/partials/top-income.twig
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										54
									
								
								resources/views/v1/reports/tag/partials/top-income.twig
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,54 @@ | ||||
| <table class="table table-hover sortable"> | ||||
|     <thead> | ||||
|     <tr> | ||||
|         <th data-defaultsort="disabled">{{ 'description'|_ }}</th> | ||||
|         <th data-defaultsign="month">{{ 'date'|_ }}</th> | ||||
|         <th data-defaultsign="az">{{ 'account'|_ }}</th> | ||||
|         <th data-defaultsign="az">{{ 'tag'|_ }}</th> | ||||
|         <th data-defaultsign="_19" style="text-align: right;">{{ 'amount'|_ }}</th> | ||||
|     </tr> | ||||
|     </thead> | ||||
|     <tbody> | ||||
|     {% for row in result %} | ||||
|         {% if loop.index > listLength %} | ||||
|             <tr class="overListLength"> | ||||
|         {% else %} | ||||
|             <tr> | ||||
|         {% endif %} | ||||
|         <td data-sortable="false"> | ||||
|             <a href="{{ route('transactions.show', row.transaction_group_id) }}"> | ||||
|                 {{ row.description }} | ||||
|             </a> | ||||
|         </td> | ||||
|         <td data-sortable="false"> | ||||
|             {{ row.date }} | ||||
|         </td> | ||||
|  | ||||
|         <td data-sortable="false"> | ||||
|             <a href="{{ route('accounts.show', row.source_account_id) }}"> | ||||
|                 {{ row.source_account_name }} | ||||
|             </a> | ||||
|         </td> | ||||
|         <td data-sortable="false"> | ||||
|             <a href="{{ route('tags.show', row.tag_id) }}"> | ||||
|                 {{ row.tag_name }} | ||||
|             </a> | ||||
|         </td> | ||||
|  | ||||
|         <td data-value="{{ row.amount }}" style="text-align: right;"> | ||||
|             {{ formatAmountBySymbol(row.amount, row.currency_symbol, row.currency_decimal_places) }} | ||||
|         </td> | ||||
|  | ||||
|         </tr> | ||||
|     {% endfor %} | ||||
|     </tbody> | ||||
|     <tfoot> | ||||
|     {% if result|length > listLength %} | ||||
|         <tr> | ||||
|             <td colspan="5" class="active"> | ||||
|                 <a href="#" class="listLengthTrigger">{{ trans('firefly.show_full_list',{number:incomeTopLength}) }}</a> | ||||
|             </td> | ||||
|         </tr> | ||||
|     {% endif %} | ||||
|     </tfoot> | ||||
| </table> | ||||
| @@ -423,40 +423,17 @@ Route::group( | ||||
| Route::group( | ||||
|     ['middleware' => 'user-full-auth', 'namespace' => 'FireflyIII\Http\Controllers\Chart', 'prefix' => 'chart/tag', 'as' => 'chart.tag.'], static function () { | ||||
|  | ||||
|     // these charts are used in reports (tag reports): | ||||
|     Route::get( | ||||
|         'tag/income/{accountList}/{tagList}/{start_date}/{end_date}', | ||||
|         ['uses' => 'TagReportController@tagIncome', 'as' => 'tag-income'] | ||||
|     ); | ||||
|     Route::get( | ||||
|         'tag/expense/{accountList}/{tagList}/{start_date}/{end_date}', | ||||
|         ['uses' => 'TagReportController@tagExpense', 'as' => 'tag-expense'] | ||||
|     ); | ||||
|     Route::get( | ||||
|         'account/income/{accountList}/{tagList}/{start_date}/{end_date}', | ||||
|         ['uses' => 'TagReportController@accountIncome', 'as' => 'account-income'] | ||||
|     ); | ||||
|     Route::get( | ||||
|         'account/expense/{accountList}/{tagList}/{start_date}/{end_date}', | ||||
|         ['uses' => 'TagReportController@accountExpense', 'as' => 'account-expense'] | ||||
|     ); | ||||
|     Route::get('tag/expense/{accountList}/{tagList}/{start_date}/{end_date}', ['uses' => 'TagReportController@tagExpense', 'as' => 'tag-expense']); | ||||
|     Route::get('tag/income/{accountList}/{tagList}/{start_date}/{end_date}', ['uses' => 'TagReportController@tagIncome', 'as' => 'tag-income']); | ||||
|     Route::get('category/expense/{accountList}/{tagList}/{start_date}/{end_date}', ['uses' => 'TagReportController@categoryExpense', 'as' => 'category-expense']); | ||||
|     Route::get('category/income/{accountList}/{tagList}/{start_date}/{end_date}', ['uses' => 'TagReportController@categoryIncome', 'as' => 'category-income']); | ||||
|     Route::get('budget/expense/{accountList}/{tagList}/{start_date}/{end_date}', ['uses' => 'TagReportController@budgetExpense', 'as' => 'budget-expense']); | ||||
|     Route::get('source/expense/{accountList}/{tagList}/{start_date}/{end_date}', ['uses' => 'TagReportController@sourceExpense', 'as' => 'source-expense']); | ||||
|     Route::get('source/income/{accountList}/{tagList}/{start_date}/{end_date}', ['uses' => 'TagReportController@sourceIncome', 'as' => 'source-income']); | ||||
|     Route::get('dest/expense/{accountList}/{tagList}/{start_date}/{end_date}', ['uses' => 'tagReportController@destinationExpense', 'as' => 'dest-expense']); | ||||
|     Route::get('dest/income/{accountList}/{tagList}/{start_date}/{end_date}', ['uses' => 'TagReportController@destinationIncome', 'as' => 'dest-income']); | ||||
|  | ||||
|     // new routes | ||||
|     Route::get( | ||||
|         'budget/expense/{accountList}/{tagList}/{start_date}/{end_date}', | ||||
|         ['uses' => 'TagReportController@budgetExpense', 'as' => 'budget-expense'] | ||||
|     ); | ||||
|     Route::get( | ||||
|         'category/expense/{accountList}/{tagList}/{start_date}/{end_date}', | ||||
|         ['uses' => 'TagReportController@categoryExpense', 'as' => 'category-expense'] | ||||
|  | ||||
|     ); | ||||
|  | ||||
|  | ||||
|     Route::get( | ||||
|         'operations/{accountList}/{tagList}/{start_date}/{end_date}', | ||||
|         ['uses' => 'TagReportController@mainChart', 'as' => 'main'] | ||||
|     ); | ||||
|     Route::get('operations/{accountList}/{tag}/{start_date}/{end_date}', ['uses' => 'TagReportController@mainChart', 'as' => 'main']); | ||||
|  | ||||
| } | ||||
| ); | ||||
| @@ -786,10 +763,7 @@ Route::group( | ||||
|  | ||||
|     Route::get('accounts/{accountList}/{categoryList}/{start_date}/{end_date}', ['uses' => 'CategoryController@accounts', 'as' => 'accounts']); | ||||
|     Route::get('categories/{accountList}/{categoryList}/{start_date}/{end_date}', ['uses' => 'CategoryController@categories', 'as' => 'categories']); | ||||
|         Route::get( | ||||
|             'account-per-category/{accountList}/{categoryList}/{start_date}/{end_date}', | ||||
|             ['uses' => 'CategoryController@accountPerCategory', 'as' => 'account-per-category'] | ||||
|         ); | ||||
|     Route::get('account-per-category/{accountList}/{categoryList}/{start_date}/{end_date}', ['uses' => 'CategoryController@accountPerCategory', 'as' => 'account-per-category']); | ||||
|  | ||||
|     Route::get('top-expenses/{accountList}/{categoryList}/{start_date}/{end_date}', ['uses' => 'CategoryController@topExpenses', 'as' => 'top-expenses']); | ||||
|     Route::get('avg-expenses/{accountList}/{categoryList}/{start_date}/{end_date}', ['uses' => 'CategoryController@avgExpenses', 'as' => 'avg-expenses']); | ||||
| @@ -799,6 +773,25 @@ Route::group( | ||||
| } | ||||
| ); | ||||
|  | ||||
| /** | ||||
|  * Report Data TAG Controller | ||||
|  */ | ||||
| Route::group( | ||||
|     ['middleware' => 'user-full-auth', 'namespace' => 'FireflyIII\Http\Controllers\Report', 'prefix' => 'report-data/tag', | ||||
|      'as'         => 'report-data.tag.'], static function () { | ||||
|  | ||||
|     Route::get('accounts/{accountList}/{tagList}/{start_date}/{end_date}', ['uses' => 'TagController@accounts', 'as' => 'accounts']); | ||||
|     Route::get('tags/{accountList}/{tagList}/{start_date}/{end_date}', ['uses' => 'TagController@tags', 'as' => 'tags']); | ||||
|     Route::get('account-per-tag/{accountList}/{tagList}/{start_date}/{end_date}', ['uses' => 'TagController@accountPerTag', 'as' => 'account-per-tag']); | ||||
|  | ||||
|     Route::get('top-expenses/{accountList}/{tagList}/{start_date}/{end_date}', ['uses' => 'TagController@topExpenses', 'as' => 'top-expenses']); | ||||
|     Route::get('avg-expenses/{accountList}/{tagList}/{start_date}/{end_date}', ['uses' => 'TagController@avgExpenses', 'as' => 'avg-expenses']); | ||||
|  | ||||
|     Route::get('top-income/{accountList}/{tagList}/{start_date}/{end_date}', ['uses' => 'TagController@topIncome', 'as' => 'top-income']); | ||||
|     Route::get('avg-income/{accountList}/{tagList}/{start_date}/{end_date}', ['uses' => 'TagController@avgIncome', 'as' => 'avg-income']); | ||||
| } | ||||
| ); | ||||
|  | ||||
| /** | ||||
|  * Report Data Balance Controller | ||||
|  */ | ||||
|   | ||||
		Reference in New Issue
	
	Block a user