diff --git a/app/Helpers/Collector/GroupCollector.php b/app/Helpers/Collector/GroupCollector.php index 72209c3fea..f96a25f33a 100644 --- a/app/Helpers/Collector/GroupCollector.php +++ b/app/Helpers/Collector/GroupCollector.php @@ -188,6 +188,46 @@ class GroupCollector implements GroupCollectorInterface return $this; } + /** + * Define which accounts can be part of the source and destination transactions. + * + * @param Collection $accounts + * + * @return GroupCollectorInterface + */ + public function setSourceAccounts(Collection $accounts): GroupCollectorInterface + { + if ($accounts->count() > 0) { + $accountIds = $accounts->pluck('id')->toArray(); + $this->query->whereIn('source.account_id', $accountIds); + + app('log')->debug(sprintf('GroupCollector: setSourceAccounts: %s', implode(', ', $accountIds))); + $this->accountIds = $accountIds; + } + + return $this; + } + + /** + * Define which accounts can be part of the source and destination transactions. + * + * @param Collection $accounts + * + * @return GroupCollectorInterface + */ + public function setDestinationAccounts(Collection $accounts): GroupCollectorInterface + { + if ($accounts->count() > 0) { + $accountIds = $accounts->pluck('id')->toArray(); + $this->query->whereIn('destination.account_id', $accountIds); + + app('log')->debug(sprintf('GroupCollector: setSourceAccounts: %s', implode(', ', $accountIds))); + $this->accountIds = $accountIds; + } + + return $this; + } + /** * Limit the search to a specific bill. * @@ -1010,4 +1050,44 @@ class GroupCollector implements GroupCollectorInterface ->orderBy('transaction_journals.description', 'DESC') ->orderBy('source.amount', 'DESC'); } + + /** + * These accounts must not be source accounts. + * + * @param Collection $accounts + * + * @return GroupCollectorInterface + */ + public function excludeSourceAccounts(Collection $accounts): GroupCollectorInterface + { + if ($accounts->count() > 0) { + $accountIds = $accounts->pluck('id')->toArray(); + $this->query->whereNotIn('source.account_id', $accountIds); + + app('log')->debug(sprintf('GroupCollector: excludeSourceAccounts: %s', implode(', ', $accountIds))); + $this->accountIds = $accountIds; + } + + return $this; + } + + /** + * These accounts must not be destination accounts. + * + * @param Collection $accounts + * + * @return GroupCollectorInterface + */ + public function excludeDestinationAccounts(Collection $accounts): GroupCollectorInterface + { + if ($accounts->count() > 0) { + $accountIds = $accounts->pluck('id')->toArray(); + $this->query->whereNotIn('destination.account_id', $accountIds); + + app('log')->debug(sprintf('GroupCollector: excludeDestinationAccounts: %s', implode(', ', $accountIds))); + $this->accountIds = $accountIds; + } + + return $this; + } } \ No newline at end of file diff --git a/app/Helpers/Collector/GroupCollectorInterface.php b/app/Helpers/Collector/GroupCollectorInterface.php index 2880576c3c..e573a6f9a0 100644 --- a/app/Helpers/Collector/GroupCollectorInterface.php +++ b/app/Helpers/Collector/GroupCollectorInterface.php @@ -46,6 +46,41 @@ interface GroupCollectorInterface */ public function getExtractedJournals(): array; + /** + * Set source accounts. + * + * @param Collection $accounts + * + * @return GroupCollectorInterface + */ + public function setSourceAccounts(Collection $accounts): GroupCollectorInterface; + + /** + * These accounts must not be source accounts. + * + * @param Collection $accounts + * + * @return GroupCollectorInterface + */ + public function excludeSourceAccounts(Collection $accounts): GroupCollectorInterface; + + /** + * Exclude destination accounts. + * @param Collection $accounts + * + * @return GroupCollectorInterface + */ + public function excludeDestinationAccounts(Collection $accounts): GroupCollectorInterface; + + /** + * Set destination accounts. + * + * @param Collection $accounts + * + * @return GroupCollectorInterface + */ + public function setDestinationAccounts(Collection $accounts): GroupCollectorInterface; + /** * Return the sum of all journals. * diff --git a/app/Http/Controllers/Report/OperationsController.php b/app/Http/Controllers/Report/OperationsController.php index b38df55624..72f197bc0d 100644 --- a/app/Http/Controllers/Report/OperationsController.php +++ b/app/Http/Controllers/Report/OperationsController.php @@ -155,7 +155,7 @@ class OperationsController extends Controller $incomeSum = array_sum( array_map( static function ($item) { - return bcmul($item['sum'], '-1'); + return $item['sum']; }, $incomes ) diff --git a/app/Repositories/Account/AccountTasker.php b/app/Repositories/Account/AccountTasker.php index 9d7a9d2c68..fa75324ce8 100644 --- a/app/Repositories/Account/AccountTasker.php +++ b/app/Repositories/Account/AccountTasker.php @@ -133,11 +133,12 @@ class AccountTasker implements AccountTaskerInterface /** @var GroupCollectorInterface $collector */ $collector = app(GroupCollectorInterface::class); - $collector->setAccounts($accounts)->setRange($start, $end); + $collector->setSourceAccounts($accounts)->setRange($start, $end); + $collector->excludeDestinationAccounts($accounts); $collector->setTypes([TransactionType::WITHDRAWAL, TransactionType::TRANSFER]) ->withAccountInformation(); $journals = $collector->getExtractedJournals(); - $expenses = $this->groupByDestination($journals); + $expenses = $this->groupExpenseByDestination($journals); // sort the result // Obtain a list of columns @@ -166,11 +167,11 @@ class AccountTasker implements AccountTaskerInterface /** @var GroupCollectorInterface $collector */ $collector = app(GroupCollectorInterface::class); - - $collector->setAccounts($accounts)->setRange($start, $end); + $collector->setDestinationAccounts($accounts)->setRange($start, $end); + $collector->excludeSourceAccounts($accounts); $collector->setTypes([TransactionType::DEPOSIT, TransactionType::TRANSFER]) ->withAccountInformation(); - $income = $this->groupByDestination($collector->getExtractedJournals()); + $income = $this->groupIncomeBySource($collector->getExtractedJournals()); // sort the result // Obtain a list of columns @@ -196,9 +197,8 @@ class AccountTasker implements AccountTaskerInterface * @param array $array * * @return array - * @SuppressWarnings(PHPMD.CyclomaticComplexity) */ - private function groupByDestination(array $array): array + private function groupExpenseByDestination(array $array): array { $defaultCurrency = app('amount')->getDefaultCurrencyByUser($this->user); /** @var CurrencyRepositoryInterface $currencyRepos */ @@ -207,18 +207,18 @@ class AccountTasker implements AccountTaskerInterface $expenses = []; $countAccounts = []; // if count remains 0 use original name, not the name with the currency. - /** @var array $journal */ foreach ($array as $journal) { - $opposingId = (int)$journal['destination_account_id']; - $currencyId = (int)$journal['currency_id']; - $key = sprintf('%s-%s', $opposingId, $currencyId); - $name = sprintf('%s (%s)', $journal['destination_account_name'], $journal['currency_name']); - $countAccounts[$opposingId] = isset($countAccounts[$opposingId]) ? $countAccounts[$opposingId] + 1 : 1; + $destinationId = (int)$journal['destination_account_id']; + $currencyId = (int)$journal['currency_id']; + $key = sprintf('%s-%s', $destinationId, $currencyId); + $name = sprintf('%s (%s)', $journal['destination_account_name'], $journal['currency_name']); + $countAccounts[$destinationId] = isset($countAccounts[$destinationId]) ? $countAccounts[$destinationId] + 1 : 1; + if (!isset($expenses[$key])) { $currencies[$currencyId] = $currencies[$currencyId] ?? $currencyRepos->findNull($currencyId); $expenses[$key] = [ - 'id' => $opposingId, + 'id' => $destinationId, 'name' => $name, 'original' => $journal['destination_account_name'], 'sum' => '0', @@ -232,6 +232,7 @@ class AccountTasker implements AccountTaskerInterface $expenses[$key]['sum'] = bcadd($expenses[$key]['sum'], $journal['amount']); ++$expenses[$key]['count']; } + // do averages: $keys = array_keys($expenses); foreach ($keys as $key) { @@ -249,4 +250,62 @@ class AccountTasker implements AccountTaskerInterface return $expenses; } + + /** + * @param array $array + * + * @return array + */ + private function groupIncomeBySource(array $array): array + { + $defaultCurrency = app('amount')->getDefaultCurrencyByUser($this->user); + /** @var CurrencyRepositoryInterface $currencyRepos */ + $currencyRepos = app(CurrencyRepositoryInterface::class); + $currencies = [$defaultCurrency->id => $defaultCurrency,]; + $income = []; + $countAccounts = []; // if count remains 0 use original name, not the name with the currency. + + /** @var array $journal */ + foreach ($array as $journal) { + $sourceId = (int)$journal['source_account_id']; + $currencyId = (int)$journal['currency_id']; + $key = sprintf('%s-%s', $sourceId, $currencyId); + $name = sprintf('%s (%s)', $journal['source_account_name'], $journal['currency_name']); + $countAccounts[$sourceId] = isset($countAccounts[$sourceId]) ? $countAccounts[$sourceId] + 1 : 1; + + if (!isset($income[$key])) { + $currencies[$currencyId] = $currencies[$currencyId] ?? $currencyRepos->findNull($currencyId); + $income[$key] = [ + 'id' => $sourceId, + 'name' => $name, + 'original' => $journal['source_account_name'], + 'sum' => '0', + 'average' => '0', + 'currencies' => [], + 'single_currency' => $currencies[$currencyId], + 'count' => 0, + ]; + } + $income[$key]['currencies'][] = (int)$journal['currency_id']; + $income[$key]['sum'] = bcadd($income[$key]['sum'], bcmul($journal['amount'], '-1')); + ++$income[$key]['count']; + } + + // do averages: + $keys = array_keys($income); + foreach ($keys as $key) { + $opposingId = $income[$key]['id']; + if (1 === $countAccounts[$opposingId]) { + $income[$key]['name'] = $income[$key]['original']; + } + + if ($income[$key]['count'] > 1) { + $income[$key]['average'] = bcdiv($income[$key]['sum'], (string)$income[$key]['count']); + } + $income[$key]['currencies'] = count(array_unique($income[$key]['currencies'])); + $income[$key]['all_currencies'] = count($currencies); + } + + return $income; + } }