Removed a lot of references to the old collector.

This commit is contained in:
James Cole 2019-05-29 21:52:08 +02:00
parent 280b2efee6
commit 7e2159d12c
No known key found for this signature in database
GPG Key ID: C16961E655E74B5E
17 changed files with 876 additions and 664 deletions

View File

@ -25,7 +25,6 @@ declare(strict_types=1);
namespace FireflyIII\Console\Commands;
use Carbon\Carbon;
use FireflyIII\Helpers\Collector\TransactionCollectorInterface;
use FireflyIII\Models\AccountType;
use FireflyIII\Models\Rule;
use FireflyIII\Models\RuleGroup;
@ -41,8 +40,6 @@ use Illuminate\Support\Collection;
/**
*
* Class ApplyRules
*
* @codeCoverageIgnore
*/
class ApplyRules extends Command
{
@ -176,96 +173,6 @@ class ApplyRules extends Command
return 0;
}
/**
* @param Collection $rules
* @param Collection $transactions
* @param bool $breakProcessing
*
* @throws \FireflyIII\Exceptions\FireflyException
*/
private function applyRuleSelection(Collection $rules, Collection $transactions, bool $breakProcessing): void
{
$bar = $this->output->createProgressBar($rules->count() * $transactions->count());
/** @var Rule $rule */
foreach ($rules as $rule) {
/** @var Processor $processor */
$processor = app(Processor::class);
$processor->make($rule, true);
/** @var Transaction $transaction */
foreach ($transactions as $transaction) {
/** @noinspection DisconnectedForeachInstructionInspection */
$bar->advance();
$result = $processor->handleTransaction($transaction);
if (true === $result) {
$this->results->push($transaction);
}
}
if (true === $rule->stop_processing && true === $breakProcessing) {
$this->line('');
$this->line(sprintf('Rule #%d ("%s") says to stop processing.', $rule->id, $rule->title));
return;
}
}
$this->line('');
}
/**
*
* @throws \FireflyIII\Exceptions\FireflyException
*/
private function grabAllRules(): void
{
if (true === $this->option('all_rules')) {
/** @var RuleRepositoryInterface $ruleRepos */
$ruleRepos = app(RuleRepositoryInterface::class);
$ruleRepos->setUser($this->getUser());
$this->rules = $ruleRepos->getAll();
// reset rule groups.
$this->ruleGroups = new Collection;
}
}
/**
*
* @throws \FireflyIII\Exceptions\FireflyException
*/
private function parseDates(): void
{
// parse start date.
$startDate = Carbon::now()->startOfMonth();
$startString = $this->option('start_date');
if (null === $startString) {
/** @var JournalRepositoryInterface $repository */
$repository = app(JournalRepositoryInterface::class);
$repository->setUser($this->getUser());
$first = $repository->firstNull();
if (null !== $first) {
$startDate = $first->date;
}
}
if (null !== $startString && '' !== $startString) {
$startDate = Carbon::createFromFormat('Y-m-d', $startString);
}
// parse end date
$endDate = Carbon::now();
$endString = $this->option('end_date');
if (null !== $endString && '' !== $endString) {
$endDate = Carbon::createFromFormat('Y-m-d', $endString);
}
if ($startDate > $endDate) {
[$endDate, $startDate] = [$startDate, $endDate];
}
$this->startDate = $startDate;
$this->endDate = $endDate;
}
/**
* @return bool
* @throws \FireflyIII\Exceptions\FireflyException
@ -417,5 +324,95 @@ class ApplyRules extends Command
return true;
}
/**
*
* @throws \FireflyIII\Exceptions\FireflyException
*/
private function grabAllRules(): void
{
if (true === $this->option('all_rules')) {
/** @var RuleRepositoryInterface $ruleRepos */
$ruleRepos = app(RuleRepositoryInterface::class);
$ruleRepos->setUser($this->getUser());
$this->rules = $ruleRepos->getAll();
// reset rule groups.
$this->ruleGroups = new Collection;
}
}
/**
*
* @throws \FireflyIII\Exceptions\FireflyException
*/
private function parseDates(): void
{
// parse start date.
$startDate = Carbon::now()->startOfMonth();
$startString = $this->option('start_date');
if (null === $startString) {
/** @var JournalRepositoryInterface $repository */
$repository = app(JournalRepositoryInterface::class);
$repository->setUser($this->getUser());
$first = $repository->firstNull();
if (null !== $first) {
$startDate = $first->date;
}
}
if (null !== $startString && '' !== $startString) {
$startDate = Carbon::createFromFormat('Y-m-d', $startString);
}
// parse end date
$endDate = Carbon::now();
$endString = $this->option('end_date');
if (null !== $endString && '' !== $endString) {
$endDate = Carbon::createFromFormat('Y-m-d', $endString);
}
if ($startDate > $endDate) {
[$endDate, $startDate] = [$startDate, $endDate];
}
$this->startDate = $startDate;
$this->endDate = $endDate;
}
/**
* @param Collection $rules
* @param Collection $transactions
* @param bool $breakProcessing
*
* @throws \FireflyIII\Exceptions\FireflyException
*/
private function applyRuleSelection(Collection $rules, Collection $transactions, bool $breakProcessing): void
{
$bar = $this->output->createProgressBar($rules->count() * $transactions->count());
/** @var Rule $rule */
foreach ($rules as $rule) {
/** @var Processor $processor */
$processor = app(Processor::class);
$processor->make($rule, true);
/** @var Transaction $transaction */
foreach ($transactions as $transaction) {
/** @noinspection DisconnectedForeachInstructionInspection */
$bar->advance();
$result = $processor->handleTransaction($transaction);
if (true === $result) {
$this->results->push($transaction);
}
}
if (true === $rule->stop_processing && true === $breakProcessing) {
$this->line('');
$this->line(sprintf('Rule #%d ("%s") says to stop processing.', $rule->id, $rule->title));
return;
}
}
$this->line('');
}
}

View File

@ -152,7 +152,7 @@ class ApplyRules extends Command
$this->line(sprintf('Will apply %d rules to %d transactions.', $count, count($journals)));
// start looping.
$bar = $this->output->createProgressBar(count($journals) * $count);
$bar = $this->output->createProgressBar(count($journals));
Log::debug(sprintf('Now looping %d transactions.', count($journals)));
/** @var array $journal */
foreach ($journals as $journal) {
@ -168,7 +168,7 @@ class ApplyRules extends Command
$processor = app(Processor::class);
$processor->make($rule, true);
$ruleTriggered = $processor->handleJournalArray($journal);
$bar->advance();
if ($ruleTriggered) {
$groupTriggered = true;
}
@ -187,6 +187,7 @@ class ApplyRules extends Command
}
}
Log::debug('Done with all rules for this group + done with journal.');
$bar->advance();
}
$this->line('');
$this->line('Done!');

View File

@ -31,7 +31,6 @@ use FireflyIII\Export\Collector\AttachmentCollector;
use FireflyIII\Export\Collector\UploadCollector;
use FireflyIII\Export\Entry\Entry;
use FireflyIII\Export\Exporter\ExporterInterface;
use FireflyIII\Helpers\Collector\TransactionCollectorInterface;
use FireflyIII\Helpers\Filter\InternalTransferFilter;
use FireflyIII\Models\AccountMeta;
use FireflyIII\Models\ExportJob;
@ -149,6 +148,109 @@ class ExpandedProcessor implements ProcessorInterface
return true;
}
/**
* Returns, if present, for the given journal ID's the notes.
*
* @param array $array
*
* @return array
*/
private function getNotes(array $array): array
{
$array = array_unique($array);
$notes = Note::where('notes.noteable_type', TransactionJournal::class)
->whereIn('notes.noteable_id', $array)
->get(['notes.*']);
$return = [];
/** @var Note $note */
foreach ($notes as $note) {
if ('' !== trim((string)$note->text)) {
$id = (int)$note->noteable_id;
$return[$id] = $note->text;
}
}
return $return;
}
/**
* Returns a comma joined list of all the users tags linked to these journals.
*
* @param array $array
*
* @return array
* @throws \Illuminate\Contracts\Encryption\DecryptException
*/
private function getTags(array $array): array
{
$set = DB::table('tag_transaction_journal')
->whereIn('tag_transaction_journal.transaction_journal_id', $array)
->leftJoin('tags', 'tag_transaction_journal.tag_id', '=', 'tags.id')
->leftJoin('transaction_journals', 'transaction_journals.id', '=', 'tag_transaction_journal.transaction_journal_id')
->where('transaction_journals.user_id', $this->job->user_id)
->get(['tag_transaction_journal.transaction_journal_id', 'tags.tag']);
$result = [];
foreach ($set as $entry) {
$id = (int)$entry->transaction_journal_id;
$result[$id] = $result[$id] ?? [];
$result[$id][] = $entry->tag;
}
return $result;
}
/**
* Get all IBAN / SWIFT / account numbers.
*
* @param array $array
*
* @return array
*/
private function getIbans(array $array): array
{
$array = array_unique($array);
$return = [];
$set = AccountMeta::whereIn('account_id', $array)
->leftJoin('accounts', 'accounts.id', 'account_meta.account_id')
->where('accounts.user_id', $this->job->user_id)
->whereIn('account_meta.name', ['accountNumber', 'BIC', 'currency_id'])
->get(['account_meta.id', 'account_meta.account_id', 'account_meta.name', 'account_meta.data']);
/** @var AccountMeta $meta */
foreach ($set as $meta) {
$id = (int)$meta->account_id;
$return[$id][$meta->name] = $meta->data;
}
return $return;
}
/**
* Get currencies.
*
* @param array $array
*
* @return array
*/
private function getAccountCurrencies(array $array): array
{
/** @var CurrencyRepositoryInterface $repository */
$repository = app(CurrencyRepositoryInterface::class);
$return = [];
$ids = [];
$repository->setUser($this->job->user);
foreach ($array as $value) {
$ids[] = (int)($value['currency_id'] ?? 0.0);
}
$ids = array_unique($ids);
$result = $repository->getByIds($ids);
foreach ($result as $currency) {
$return[$currency->id] = $currency->code;
}
return $return;
}
/**
* Get old oploads.
*
@ -216,6 +318,27 @@ class ExpandedProcessor implements ProcessorInterface
return true;
}
/**
* Get files.
*
* @return Collection
*/
public function getFiles(): Collection
{
return $this->files;
}
/**
* Delete files.
*/
private function deleteFiles(): void
{
$disk = Storage::disk('export');
foreach ($this->getFiles() as $file) {
$disk->delete($file);
}
}
/**
* Export the journals.
*
@ -234,16 +357,6 @@ class ExpandedProcessor implements ProcessorInterface
return true;
}
/**
* Get files.
*
* @return Collection
*/
public function getFiles(): Collection
{
return $this->files;
}
/**
* Save export job settings to class.
*
@ -259,118 +372,4 @@ class ExpandedProcessor implements ProcessorInterface
$this->includeOldUploads = $settings['includeOldUploads'];
$this->job = $settings['job'];
}
/**
* Delete files.
*/
private function deleteFiles(): void
{
$disk = Storage::disk('export');
foreach ($this->getFiles() as $file) {
$disk->delete($file);
}
}
/**
* Get currencies.
*
* @param array $array
*
* @return array
*/
private function getAccountCurrencies(array $array): array
{
/** @var CurrencyRepositoryInterface $repository */
$repository = app(CurrencyRepositoryInterface::class);
$return = [];
$ids = [];
$repository->setUser($this->job->user);
foreach ($array as $value) {
$ids[] = (int)($value['currency_id'] ?? 0.0);
}
$ids = array_unique($ids);
$result = $repository->getByIds($ids);
foreach ($result as $currency) {
$return[$currency->id] = $currency->code;
}
return $return;
}
/**
* Get all IBAN / SWIFT / account numbers.
*
* @param array $array
*
* @return array
*/
private function getIbans(array $array): array
{
$array = array_unique($array);
$return = [];
$set = AccountMeta::whereIn('account_id', $array)
->leftJoin('accounts', 'accounts.id', 'account_meta.account_id')
->where('accounts.user_id', $this->job->user_id)
->whereIn('account_meta.name', ['accountNumber', 'BIC', 'currency_id'])
->get(['account_meta.id', 'account_meta.account_id', 'account_meta.name', 'account_meta.data']);
/** @var AccountMeta $meta */
foreach ($set as $meta) {
$id = (int)$meta->account_id;
$return[$id][$meta->name] = $meta->data;
}
return $return;
}
/**
* Returns, if present, for the given journal ID's the notes.
*
* @param array $array
*
* @return array
*/
private function getNotes(array $array): array
{
$array = array_unique($array);
$notes = Note::where('notes.noteable_type', TransactionJournal::class)
->whereIn('notes.noteable_id', $array)
->get(['notes.*']);
$return = [];
/** @var Note $note */
foreach ($notes as $note) {
if ('' !== trim((string)$note->text)) {
$id = (int)$note->noteable_id;
$return[$id] = $note->text;
}
}
return $return;
}
/**
* Returns a comma joined list of all the users tags linked to these journals.
*
* @param array $array
*
* @return array
* @throws \Illuminate\Contracts\Encryption\DecryptException
*/
private function getTags(array $array): array
{
$set = DB::table('tag_transaction_journal')
->whereIn('tag_transaction_journal.transaction_journal_id', $array)
->leftJoin('tags', 'tag_transaction_journal.tag_id', '=', 'tags.id')
->leftJoin('transaction_journals', 'transaction_journals.id', '=', 'tag_transaction_journal.transaction_journal_id')
->where('transaction_journals.user_id', $this->job->user_id)
->get(['tag_transaction_journal.transaction_journal_id', 'tags.tag']);
$result = [];
foreach ($set as $entry) {
$id = (int)$entry->transaction_journal_id;
$result[$id] = $result[$id] ?? [];
$result[$id][] = $entry->tag;
}
return $result;
}
}

View File

@ -28,9 +28,8 @@ namespace FireflyIII\Generator\Report\Audit;
use Carbon\Carbon;
use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Generator\Report\ReportGeneratorInterface;
use FireflyIII\Helpers\Collector\TransactionCollectorInterface;
use FireflyIII\Helpers\Collector\GroupCollectorInterface;
use FireflyIII\Models\Account;
use FireflyIII\Models\Transaction;
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
use FireflyIII\Repositories\Currency\CurrencyRepositoryInterface;
use Illuminate\Support\Collection;
@ -72,12 +71,10 @@ class MonthReportGenerator implements ReportGeneratorInterface
$reportType = 'audit';
$accountIds = implode(',', $this->accounts->pluck('id')->toArray());
$hideable = ['buttons', 'icon', 'description', 'balance_before', 'amount', 'balance_after', 'date',
'interest_date', 'book_date', 'process_date',
// three new optional fields.
'due_date', 'payment_date', 'invoice_date',
'from', 'to', 'budget', 'category', 'bill',
// more new optional fields
'internal_reference', 'notes',
'create_date', 'update_date',
];
try {
@ -96,7 +93,7 @@ class MonthReportGenerator implements ReportGeneratorInterface
* Get the audit report.
*
* @param Account $account
* @param Carbon $date
* @param Carbon $date
*
* @return array
*
@ -112,11 +109,11 @@ class MonthReportGenerator implements ReportGeneratorInterface
$accountRepository = app(AccountRepositoryInterface::class);
$accountRepository->setUser($account->user);
/** @var TransactionCollectorInterface $collector */
$collector = app(TransactionCollectorInterface::class);
$collector->setAccounts(new Collection([$account]))->setRange($this->start, $this->end);
$journals = $collector->getTransactions();
$journals = $journals->reverse();
/** @var GroupCollectorInterface $collector */
$collector = app(GroupCollectorInterface::class);
$collector->setAccounts(new Collection([$account]))->setRange($this->start, $this->end)
->withAccountInformation();
$journals = $collector->getExtractedJournals();
$dayBeforeBalance = app('steam')->balance($account, $date);
$startBalance = $dayBeforeBalance;
$currency = $currencyRepos->findNull((int)$accountRepository->getMetaValue($account, 'currency_id'));
@ -125,23 +122,23 @@ class MonthReportGenerator implements ReportGeneratorInterface
throw new FireflyException('Unexpected NULL value in account currency preference.');
}
/** @var Transaction $transaction */
foreach ($journals as $transaction) {
$transaction->before = $startBalance;
$transactionAmount = $transaction->transaction_amount;
foreach ($journals as $index => $journal) {
$journals[$index]['balance_before'] = $startBalance;
$transactionAmount = $journal['amount'];
if ($currency->id === $transaction->foreign_currency_id) {
$transactionAmount = $transaction->transaction_foreign_amount;
if ($currency->id === $journal['foreign_currency_id']) {
$transactionAmount = $journal['foreign_amount'];
}
$newBalance = bcadd($startBalance, $transactionAmount);
$transaction->after = $newBalance;
$startBalance = $newBalance;
$newBalance = bcadd($startBalance, $transactionAmount);
$journals[$index]['balance_after'] = $newBalance;
$startBalance = $newBalance;
}
$return = [
'journals' => $journals->reverse(),
'exists' => $journals->count() > 0,
'journals' => $journals,
'exists' => count($journals) > 0,
'end' => $this->end->formatLocalized((string)trans('config.month_and_day')),
'endBalance' => app('steam')->balance($account, $this->end),
'dayBefore' => $date->formatLocalized((string)trans('config.month_and_day')),

View File

@ -27,11 +27,7 @@ namespace FireflyIII\Generator\Report\Budget;
use Carbon\Carbon;
use FireflyIII\Generator\Report\ReportGeneratorInterface;
use FireflyIII\Generator\Report\Support;
use FireflyIII\Helpers\Collector\TransactionCollectorInterface;
use FireflyIII\Helpers\Filter\OpposingAccountFilter;
use FireflyIII\Helpers\Filter\PositiveAmountFilter;
use FireflyIII\Helpers\Filter\TransferFilter;
use FireflyIII\Models\Transaction;
use FireflyIII\Helpers\Collector\GroupCollectorInterface;
use FireflyIII\Models\TransactionType;
use Illuminate\Support\Collection;
use Log;
@ -50,7 +46,7 @@ class MonthReportGenerator extends Support implements ReportGeneratorInterface
private $budgets;
/** @var Carbon The end date. */
private $end;
/** @var Collection The expenses in the report. */
/** @var array The expenses in the report. */
private $expenses;
/** @var Carbon The start date. */
private $start;
@ -93,6 +89,57 @@ class MonthReportGenerator extends Support implements ReportGeneratorInterface
return $result;
}
/**
* Get the expenses.
*
* @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])
->withAccountInformation()
->withBudgetInformation()
->setBudgets($this->budgets);
$journals = $collector->getExtractedJournals();
$this->expenses = $journals;
return $journals;
}
/**
* Summarize a collection by its budget.
*
* @param array $array
*
* @return array
*/
private function summarizeByBudget(array $array): array
{
$result = [
'sum' => '0',
];
/** @var array $journal */
foreach ($array as $journal) {
$budgetId = (int)$journal['budget_id'];
$result[$budgetId] = $result[$budgetId] ?? '0';
$result[$budgetId] = bcadd($journal['amount'], $result[$budgetId]);
$result['sum'] = bcadd($result['sum'], $journal['amount']);
}
return $result;
}
/**
* Set the involved accounts.
*
@ -184,58 +231,4 @@ class MonthReportGenerator extends Support implements ReportGeneratorInterface
{
return $this;
}
/**
* Get the expenses.
*
* @return Collection
*/
protected function getExpenses(): Collection
{
if ($this->expenses->count() > 0) {
Log::debug('Return previous set of expenses.');
return $this->expenses;
}
/** @var TransactionCollectorInterface $collector */
$collector = app(TransactionCollectorInterface::class);
$collector->setAccounts($this->accounts)->setRange($this->start, $this->end)
->setTypes([TransactionType::WITHDRAWAL])
->setBudgets($this->budgets)->withOpposingAccount();
$collector->removeFilter(TransferFilter::class);
$collector->addFilter(OpposingAccountFilter::class);
$collector->addFilter(PositiveAmountFilter::class);
$transactions = $collector->getTransactions();
$this->expenses = $transactions;
return $transactions;
}
/**
* Summarize a collection by its budget.
*
* @param Collection $collection
*
* @return array
*/
private function summarizeByBudget(Collection $collection): array
{
$result = [
'sum' => '0',
];
/** @var Transaction $transaction */
foreach ($collection as $transaction) {
$jrnlBudId = (int)$transaction->transaction_journal_budget_id;
$transBudId = (int)$transaction->transaction_budget_id;
$budgetId = max($jrnlBudId, $transBudId);
$result[$budgetId] = $result[$budgetId] ?? '0';
$result[$budgetId] = bcadd($transaction->transaction_amount, $result[$budgetId]);
$result['sum'] = bcadd($result['sum'], $transaction->transaction_amount);
}
return $result;
}
}

View File

@ -27,6 +27,7 @@ namespace FireflyIII\Generator\Report\Category;
use Carbon\Carbon;
use FireflyIII\Generator\Report\ReportGeneratorInterface;
use FireflyIII\Generator\Report\Support;
use FireflyIII\Helpers\Collector\GroupCollectorInterface;
use FireflyIII\Helpers\Collector\TransactionCollectorInterface;
use FireflyIII\Helpers\Filter\NegativeAmountFilter;
use FireflyIII\Helpers\Filter\OpposingAccountFilter;
@ -51,9 +52,9 @@ class MonthReportGenerator extends Support implements ReportGeneratorInterface
private $categories;
/** @var Carbon The end date */
private $end;
/** @var Collection The expenses */
/** @var array The expenses */
private $expenses;
/** @var Collection The income in the report. */
/** @var array The income in the report. */
private $income;
/** @var Carbon The start date. */
private $start;
@ -201,27 +202,23 @@ class MonthReportGenerator extends Support implements ReportGeneratorInterface
/**
* Get the expenses for this report.
*
* @return Collection
* @return array
*/
protected function getExpenses(): Collection
protected function getExpenses(): array
{
if ($this->expenses->count() > 0) {
if (count($this->expenses) > 0) {
Log::debug('Return previous set of expenses.');
return $this->expenses;
}
/** @var TransactionCollectorInterface $collector */
$collector = app(TransactionCollectorInterface::class);
/** @var GroupCollectorInterface $collector */
$collector = app(GroupCollectorInterface::class);
$collector->setAccounts($this->accounts)->setRange($this->start, $this->end)
->setTypes([TransactionType::WITHDRAWAL, TransactionType::TRANSFER])
->setCategories($this->categories)->withOpposingAccount();
$collector->removeFilter(TransferFilter::class);
->setCategories($this->categories)->withAccountInformation();
$collector->addFilter(OpposingAccountFilter::class);
$collector->addFilter(PositiveAmountFilter::class);
$transactions = $collector->getTransactions();
$transactions = $collector->getExtractedJournals();
$this->expenses = $transactions;
return $transactions;
@ -230,24 +227,22 @@ class MonthReportGenerator extends Support implements ReportGeneratorInterface
/**
* Get the income for this report.
*
* @return Collection
* @return array
*/
protected function getIncome(): Collection
protected function getIncome(): array
{
if ($this->income->count() > 0) {
if (count($this->income) > 0) {
return $this->income;
}
/** @var TransactionCollectorInterface $collector */
$collector = app(TransactionCollectorInterface::class);
/** @var GroupCollectorInterface $collector */
$collector = app(GroupCollectorInterface::class);
$collector->setAccounts($this->accounts)->setRange($this->start, $this->end)
->setTypes([TransactionType::DEPOSIT, TransactionType::TRANSFER])
->setCategories($this->categories)->withOpposingAccount();
->setCategories($this->categories)->withAccountInformation();
$collector->addFilter(OpposingAccountFilter::class);
$collector->addFilter(NegativeAmountFilter::class);
$transactions = $collector->getTransactions();
$transactions = $collector->getExtractedJournals();
$this->income = $transactions;
return $transactions;
@ -256,20 +251,18 @@ class MonthReportGenerator extends Support implements ReportGeneratorInterface
/**
* Summarize the category.
*
* @param Collection $collection
* @param array $array
*
* @return array
*/
private function summarizeByCategory(Collection $collection): array
private function summarizeByCategory(array $array): array
{
$result = [];
/** @var Transaction $transaction */
foreach ($collection as $transaction) {
$jrnlCatId = (int)$transaction->transaction_journal_category_id;
$transCatId = (int)$transaction->transaction_category_id;
$categoryId = max($jrnlCatId, $transCatId);
/** @var array $journal */
foreach ($array as $journal) {
$categoryId = (int)$journal['category_id'];
$result[$categoryId] = $result[$categoryId] ?? '0';
$result[$categoryId] = bcadd($transaction->transaction_amount, $result[$categoryId]);
$result[$categoryId] = bcadd($journal['amount'], $result[$categoryId]);
}
return $result;

View File

@ -24,13 +24,12 @@ declare(strict_types=1);
namespace FireflyIII\Generator\Report;
use FireflyIII\Models\Transaction;
use Illuminate\Support\Collection;
/**
* Class Support.
* @method Collection getExpenses()
* @method Collection getIncome()
* @method array getExpenses()
* @method array getIncome()
*
* @codeCoverageIgnore
*/
@ -39,53 +38,63 @@ class Support
/**
* Get the top expenses.
*
* @return Collection
* @return array
*/
public function getTopExpenses(): Collection
public function getTopExpenses(): array
{
return $this->getExpenses()->sortBy('transaction_amount');
$expenses = $this->getExpenses();
usort($expenses, function ($a, $b) {
return $a['amount'] <=> $b['amount'];
});
return $expenses;
}
/**
* Get the top income.
*
* @return Collection
* @return array
*/
public function getTopIncome(): Collection
public function getTopIncome(): array
{
return $this->getIncome()->sortByDesc('transaction_amount');
$income = $this->getIncome();
usort($income, function ($a, $b) {
return $b['amount'] <=> $a['amount'];
});
return $income;
}
/**
* Get averages from a collection.
*
* @param Collection $collection
* @param int $sortFlag
* @param array $array
* @param int $sortFlag
*
* @return array
*/
protected function getAverages(Collection $collection, int $sortFlag): array
protected function getAverages(array $array, int $sortFlag): array
{
$result = [];
/** @var Transaction $transaction */
foreach ($collection as $transaction) {
/** @var array $journal */
foreach ($array as $journal) {
// opposing name and ID:
$opposingId = $transaction->opposing_account_id;
$opposingId = $journal['destination_account_id'];
// is not set?
if (!isset($result[$opposingId])) {
$name = $transaction->opposing_account_name;
$name = $journal['destination_account_name'];
$result[$opposingId] = [
'name' => $name,
'count' => 1,
'id' => $opposingId,
'average' => $transaction->transaction_amount,
'sum' => $transaction->transaction_amount,
'average' => $journal['amount'],
'sum' => $journal['amount'],
];
continue;
}
++$result[$opposingId]['count'];
$result[$opposingId]['sum'] = bcadd($result[$opposingId]['sum'], $transaction->transaction_amount);
$result[$opposingId]['sum'] = bcadd($result[$opposingId]['sum'], $journal['amount']);
$result[$opposingId]['average'] = bcdiv($result[$opposingId]['sum'], (string)$result[$opposingId]['count']);
}
@ -151,18 +160,18 @@ class Support
/**
* Summarize the data by account.
*
* @param Collection $collection
* @param array $array
*
* @return array
*/
protected function summarizeByAccount(Collection $collection): array
protected function summarizeByAccount(array $array): array
{
$result = [];
/** @var Transaction $transaction */
foreach ($collection as $transaction) {
$accountId = $transaction->account_id;
/** @var array $journal */
foreach ($array as $journal) {
$accountId = $journal['source_account_id'] ?? 0;
$result[$accountId] = $result[$accountId] ?? '0';
$result[$accountId] = bcadd($transaction->transaction_amount, $result[$accountId]);
$result[$accountId] = bcadd($journal['amount'], $result[$accountId]);
}
return $result;

View File

@ -28,12 +28,7 @@ namespace FireflyIII\Generator\Report\Tag;
use Carbon\Carbon;
use FireflyIII\Generator\Report\ReportGeneratorInterface;
use FireflyIII\Generator\Report\Support;
use FireflyIII\Helpers\Collector\TransactionCollectorInterface;
use FireflyIII\Helpers\Filter\DoubleTransactionFilter;
use FireflyIII\Helpers\Filter\NegativeAmountFilter;
use FireflyIII\Helpers\Filter\OpposingAccountFilter;
use FireflyIII\Helpers\Filter\PositiveAmountFilter;
use FireflyIII\Helpers\Filter\TransferFilter;
use FireflyIII\Helpers\Collector\GroupCollectorInterface;
use FireflyIII\Models\Tag;
use FireflyIII\Models\Transaction;
use FireflyIII\Models\TransactionType;
@ -52,9 +47,9 @@ class MonthReportGenerator extends Support implements ReportGeneratorInterface
private $accounts;
/** @var Carbon The end date */
private $end;
/** @var Collection The expenses involved */
/** @var array The expenses involved */
private $expenses;
/** @var Collection The income involved */
/** @var array The income involved */
private $income;
/** @var Carbon The start date */
private $start;
@ -107,6 +102,80 @@ class MonthReportGenerator extends Support implements ReportGeneratorInterface
return $result;
}
/**
* 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 Tag $journalTag */
foreach ($journal['tag_ids'] as $journalTag) {
$journalTagId = (int)$journalTag;
if (\in_array($journalTagId, $tagIds, true)) {
$result[$journalTagId] = $result[$journalTagId] ?? '0';
$result[$journalTagId] = bcadd($journal['amount'], $result[$journalTagId]);
}
}
}
return $result;
}
/**
* Set the accounts.
*
@ -198,88 +267,4 @@ class MonthReportGenerator extends Support implements ReportGeneratorInterface
return $this;
}
/**
* Get expense collection for report.
*
* @return Collection
*/
protected function getExpenses(): Collection
{
if ($this->expenses->count() > 0) {
Log::debug('Return previous set of expenses.');
return $this->expenses;
}
/** @var TransactionCollectorInterface $collector */
$collector = app(TransactionCollectorInterface::class);
$collector->setAccounts($this->accounts)->setRange($this->start, $this->end)
->setTypes([TransactionType::WITHDRAWAL, TransactionType::TRANSFER])
->setTags($this->tags)->withOpposingAccount();
$collector->removeFilter(TransferFilter::class);
$collector->addFilter(OpposingAccountFilter::class);
$collector->addFilter(PositiveAmountFilter::class);
$collector->addFilter(DoubleTransactionFilter::class);
$transactions = $collector->getTransactions();
$this->expenses = $transactions;
return $transactions;
}
/**
* Get the income for this report.
*
* @return Collection
*/
protected function getIncome(): Collection
{
if ($this->income->count() > 0) {
return $this->income;
}
/** @var TransactionCollectorInterface $collector */
$collector = app(TransactionCollectorInterface::class);
$collector->setAccounts($this->accounts)->setRange($this->start, $this->end)
->setTypes([TransactionType::DEPOSIT, TransactionType::TRANSFER])
->setTags($this->tags)->withOpposingAccount();
$collector->addFilter(OpposingAccountFilter::class);
$collector->addFilter(NegativeAmountFilter::class);
$collector->addFilter(DoubleTransactionFilter::class);
$transactions = $collector->getTransactions();
$this->income = $transactions;
return $transactions;
}
/**
* Summarize by tag.
*
* @param Collection $collection
*
* @return array
*/
protected function summarizeByTag(Collection $collection): array
{
$tagIds = array_map('\intval', $this->tags->pluck('id')->toArray());
$result = [];
/** @var Transaction $transaction */
foreach ($collection as $transaction) {
$journal = $transaction->transactionJournal;
$journalTags = $journal->tags;
/** @var Tag $journalTag */
foreach ($journalTags as $journalTag) {
$journalTagId = (int)$journalTag->id;
if (\in_array($journalTagId, $tagIds, true)) {
$result[$journalTagId] = $result[$journalTagId] ?? '0';
$result[$journalTagId] = bcadd($transaction->transaction_amount, $result[$journalTagId]);
}
}
}
return $result;
}
}

View File

@ -37,6 +37,8 @@ use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Database\Query\JoinClause;
use Illuminate\Pagination\LengthAwarePaginator;
use Illuminate\Support\Collection;
use function count;
use function get_class;
/**
* Class GroupCollector
@ -74,7 +76,7 @@ class GroupCollector implements GroupCollectorInterface
public function __construct()
{
if ('testing' === config('app.env')) {
app('log')->warning(sprintf('%s should not be instantiated in the TEST environment!', \get_class($this)));
app('log')->warning(sprintf('%s should not be instantiated in the TEST environment!', get_class($this)));
}
$this->hasAccountInformation = false;
$this->hasCatInformation = false;
@ -139,7 +141,8 @@ class GroupCollector implements GroupCollectorInterface
/** @var array $group */
foreach ($selection as $group) {
foreach ($group['transactions'] as $journalId => $journal) {
$return[$journalId] = $journal;
$journal['group_title'] = $group['title'];
$return[$journalId] = $journal;
}
}
@ -161,11 +164,149 @@ class GroupCollector implements GroupCollectorInterface
$this->total = $collection->count();
// now filter the array according to the page and the
$offset = $this->page * $this->limit;
$offset = $this->page * $this->limit;
return $collection->slice($offset, $this->limit);
}
/**
* @param Collection $collection
*
* @return Collection
* @throws Exception
*/
private function parseArray(Collection $collection): Collection
{
$groups = [];
/** @var TransactionGroup $augmentedGroup */
foreach ($collection as $augmentedGroup) {
$groupId = $augmentedGroup->transaction_group_id;
if (!isset($groups[$groupId])) {
// make new array
$groupArray = [
'id' => $augmentedGroup->transaction_group_id,
'user_id' => $augmentedGroup->user_id,
'title' => $augmentedGroup->transaction_group_title,
'count' => 1,
'sums' => [],
'transactions' => [],
];
$journalId = (int)$augmentedGroup->transaction_journal_id;
$groupArray['transactions'][$journalId] = $this->parseAugmentedGroup($augmentedGroup);
$groups[$groupId] = $groupArray;
continue;
}
// or parse the rest.
$journalId = (int)$augmentedGroup->transaction_journal_id;
$groups[$groupId]['count']++;
if (isset($groups[$groupId]['transactions'][$journalId])) {
$groups[$groupId]['transactions'][$journalId] =
$this->mergeTags($groups[$groupId]['transactions'][$journalId], $augmentedGroup);
}
if (!isset($groups[$groupId]['transactions'][$journalId])) {
$groups[$groupId]['transactions'][$journalId] = $this->parseAugmentedGroup($augmentedGroup);
}
}
$groups = $this->parseSums($groups);
return new Collection($groups);
}
/**
* @param TransactionGroup $augmentedGroup
*
* @return array
* @throws Exception
*/
private function parseAugmentedGroup(TransactionGroup $augmentedGroup): array
{
$result = $augmentedGroup->toArray();
$result['tags'] = [];
$result['tag_ids'] = [];
$result['date'] = new Carbon($result['date']);
$result['created_at'] = new Carbon($result['created_at']);
$result['updated_at'] = new Carbon($result['updated_at']);
$result['reconciled'] = 1 === (int)$result['reconciled'];
if (isset($augmentedGroup['tag'])) {
$result['tags'][] = $augmentedGroup['tag'];
}
if (isset($augmentedGroup['tag_id'])) {
$result['tag_ids'][] = $augmentedGroup['tag_id'];
}
return $result;
}
/**
* @param array $existingJournal
* @param TransactionGroup $newGroup
* @return array
*/
private function mergeTags(array $existingJournal, TransactionGroup $newGroup): array
{
$newArray = $newGroup->toArray();
if (isset($newArray['tag_id'])) {
$existingJournal['tag_ids'][] = (int)$newArray['tag_id'];
}
if (isset($newArray['tag'])) {
$existingJournal['tags'][] = $newArray['tag'];
}
$existingJournal['tags'] = array_unique($existingJournal['tags']);
$existingJournal['tag_ids'] = array_unique($existingJournal['tag_ids']);
return $existingJournal;
}
/**
* @param array $groups
*
* @return array
*/
private function parseSums(array $groups): array
{
/**
* @var int $groudId
* @var array $group
*/
foreach ($groups as $groudId => $group) {
/** @var array $transaction */
foreach ($group['transactions'] as $transaction) {
$currencyId = (int)$transaction['currency_id'];
// set default:
if (!isset($groups[$groudId]['sums'][$currencyId])) {
$groups[$groudId]['sums'][$currencyId]['currency_id'] = $currencyId;
$groups[$groudId]['sums'][$currencyId]['currency_code'] = $transaction['currency_code'];
$groups[$groudId]['sums'][$currencyId]['currency_symbol'] = $transaction['currency_symbol'];
$groups[$groudId]['sums'][$currencyId]['currency_decimal_places'] = $transaction['currency_decimal_places'];
$groups[$groudId]['sums'][$currencyId]['amount'] = '0';
}
$groups[$groudId]['sums'][$currencyId]['amount'] = bcadd($groups[$groudId]['sums'][$currencyId]['amount'], $transaction['amount']);
if (null !== $transaction['foreign_amount'] && null !== $transaction['foreign_currency_id']) {
$currencyId = (int)$transaction['foreign_currency_id'];
// set default:
if (!isset($groups[$groudId]['sums'][$currencyId])) {
$groups[$groudId]['sums'][$currencyId]['currency_id'] = $currencyId;
$groups[$groudId]['sums'][$currencyId]['currency_code'] = $transaction['foreign_currency_code'];
$groups[$groudId]['sums'][$currencyId]['currency_symbol'] = $transaction['foreign_currency_symbol'];
$groups[$groudId]['sums'][$currencyId]['currency_decimal_places'] = $transaction['foreign_currency_decimal_places'];
$groups[$groudId]['sums'][$currencyId]['amount'] = '0';
}
$groups[$groudId]['sums'][$currencyId]['amount'] = bcadd($groups[$groudId]['sums'][$currencyId]['amount'], $transaction['foreign_amount']);
}
}
}
return $groups;
}
/**
* Same as getGroups but everything is in a paginator.
*
@ -217,6 +358,27 @@ class GroupCollector implements GroupCollectorInterface
return $this;
}
/**
* Will include budget ID + name, if any.
*
* @return GroupCollectorInterface
*/
public function withBudgetInformation(): GroupCollectorInterface
{
if (false === $this->hasBudgetInformation) {
// join link table
$this->query->leftJoin('budget_transaction_journal', 'budget_transaction_journal.transaction_journal_id', '=', 'transaction_journals.id');
// join cat table
$this->query->leftJoin('budgets', 'budget_transaction_journal.budget_id', '=', 'budgets.id');
// add fields
$this->fields[] = 'budgets.id as budget_id';
$this->fields[] = 'budgets.name as budget_name';
$this->hasBudgetInformation = true;
}
return $this;
}
/**
* Limit the search to a specific budget.
*
@ -262,6 +424,27 @@ class GroupCollector implements GroupCollectorInterface
return $this;
}
/**
* Will include category ID + name, if any.
*
* @return GroupCollectorInterface
*/
public function withCategoryInformation(): GroupCollectorInterface
{
if (false === $this->hasCatInformation) {
// join link table
$this->query->leftJoin('category_transaction_journal', 'category_transaction_journal.transaction_journal_id', '=', 'transaction_journals.id');
// join cat table
$this->query->leftJoin('categories', 'category_transaction_journal.category_id', '=', 'categories.id');
// add fields
$this->fields[] = 'categories.id as category_id';
$this->fields[] = 'categories.name as category_name';
$this->hasCatInformation = true;
}
return $this;
}
/**
* Limit results to a specific currency, either foreign or normal one.
*
@ -290,7 +473,7 @@ class GroupCollector implements GroupCollectorInterface
*/
public function setJournalIds(array $journalIds): GroupCollectorInterface
{
if (\count($journalIds) > 0) {
if (count($journalIds) > 0) {
$this->query->whereIn('transaction_journals.id', $journalIds);
}
@ -366,6 +549,21 @@ class GroupCollector implements GroupCollectorInterface
return $this;
}
/**
* Join table to get tag information.
*/
private function joinTagTables(): void
{
if (false === $this->hasJoinedTagTables) {
// join some extra tables:
$this->hasJoinedTagTables = true;
$this->query->leftJoin('tag_transaction_journal', 'tag_transaction_journal.transaction_journal_id', '=', 'transaction_journals.id');
$this->query->leftJoin('tags', 'tag_transaction_journal.tag_id', '=', 'tags.id');
$this->fields[] = 'tags.id as tag_id';
$this->fields[] = 'tags.tag as tag';
}
}
/**
* Limit the search to one specific transaction group.
*
@ -409,6 +607,44 @@ class GroupCollector implements GroupCollectorInterface
return $this;
}
/**
* Build the query.
*/
private function startQuery(): void
{
app('log')->debug('TransactionCollector::startQuery');
$this->query = $this->user
->transactionGroups()
->leftJoin('transaction_journals', 'transaction_journals.transaction_group_id', 'transaction_groups.id')
// join source transaction.
->leftJoin(
'transactions as source', function (JoinClause $join) {
$join->on('source.transaction_journal_id', '=', 'transaction_journals.id')
->where('source.amount', '<', 0);
}
)
// join destination transaction
->leftJoin(
'transactions as destination', function (JoinClause $join) {
$join->on('destination.transaction_journal_id', '=', 'transaction_journals.id')
->where('destination.amount', '>', 0);
}
)
// left join transaction type.
->leftJoin('transaction_types', 'transaction_types.id', '=', 'transaction_journals.transaction_type_id')
->leftJoin('transaction_currencies as currency', 'currency.id', '=', 'source.transaction_currency_id')
->leftJoin('transaction_currencies as foreign_currency', 'foreign_currency.id', '=', 'source.foreign_currency_id')
->whereNull('transaction_groups.deleted_at')
->whereNull('transaction_journals.deleted_at')
->whereNull('source.deleted_at')
->whereNull('destination.deleted_at')
->orderBy('transaction_journals.date', 'DESC')
->orderBy('transaction_journals.order', 'ASC')
->orderBy('transaction_journals.id', 'DESC')
->orderBy('transaction_journals.description', 'DESC')
->orderBy('source.amount', 'DESC');
}
/**
* Automatically include all stuff required to make API calls work.
*
@ -482,193 +718,32 @@ class GroupCollector implements GroupCollectorInterface
}
/**
* Will include budget ID + name, if any.
* Limit the search to a specific bunch of categories.
*
* @param Collection $categories
*
* @return GroupCollectorInterface
*/
public function withBudgetInformation(): GroupCollectorInterface
public function setCategories(Collection $categories): GroupCollectorInterface
{
if (false === $this->hasBudgetInformation) {
// join link table
$this->query->leftJoin('budget_transaction_journal', 'budget_transaction_journal.transaction_journal_id', '=', 'transaction_journals.id');
// join cat table
$this->query->leftJoin('budgets', 'budget_transaction_journal.budget_id', '=', 'budgets.id');
// add fields
$this->fields[] = 'budgets.id as budget_id';
$this->fields[] = 'budgets.name as budget_name';
$this->hasBudgetInformation = true;
}
$this->withCategoryInformation();
$this->query->where('categories.id', $categories->pluck('id')->toArray());
return $this;
}
/**
* Will include category ID + name, if any.
* Limit results to a specific set of tags.
*
* @param Collection $tags
*
* @return GroupCollectorInterface
*/
public function withCategoryInformation(): GroupCollectorInterface
public function setTags(Collection $tags): GroupCollectorInterface
{
if (false === $this->hasCatInformation) {
// join link table
$this->query->leftJoin('category_transaction_journal', 'category_transaction_journal.transaction_journal_id', '=', 'transaction_journals.id');
// join cat table
$this->query->leftJoin('categories', 'category_transaction_journal.category_id', '=', 'categories.id');
// add fields
$this->fields[] = 'categories.id as category_id';
$this->fields[] = 'categories.name as category_name';
$this->hasCatInformation = true;
}
$this->joinTagTables();
$this->query->whereIn('tag_transaction_journal.tag_id', $tags->pluck('id')->toArray());
return $this;
}
/**
* Join table to get tag information.
*/
private function joinTagTables(): void
{
if (false === $this->hasJoinedTagTables) {
// join some extra tables:
$this->hasJoinedTagTables = true;
$this->query->leftJoin('tag_transaction_journal', 'tag_transaction_journal.transaction_journal_id', '=', 'transaction_journals.id');
}
}
/**
* @param Collection $collection
*
* @return Collection
* @throws Exception
*/
private function parseArray(Collection $collection): Collection
{
$groups = [];
/** @var TransactionGroup $augmentedGroup */
foreach ($collection as $augmentedGroup) {
$groupId = $augmentedGroup->transaction_group_id;
if (!isset($groups[$groupId])) {
// make new array
$groupArray = [
'id' => $augmentedGroup->transaction_group_id,
'user_id' => $augmentedGroup->user_id,
'title' => $augmentedGroup->transaction_group_title,
'count' => 1,
'sums' => [],
'transactions' => [],
];
$journalId = (int)$augmentedGroup->transaction_journal_id;
$groupArray['transactions'][$journalId] = $this->parseAugmentedGroup($augmentedGroup);
$groups[$groupId] = $groupArray;
continue;
}
// or parse the rest.
$journalId = (int)$augmentedGroup->transaction_journal_id;
$groups[$groupId]['count']++;
$groups[$groupId]['transactions'][$journalId] = $this->parseAugmentedGroup($augmentedGroup);
}
$groups = $this->parseSums($groups);
return new Collection($groups);
}
/**
* @param TransactionGroup $augmentedGroup
*
* @return array
* @throws Exception
*/
private function parseAugmentedGroup(TransactionGroup $augmentedGroup): array
{
$result = $augmentedGroup->toArray();
$result['date'] = new Carbon($result['date']);
$result['created_at'] = new Carbon($result['created_at']);
$result['updated_at'] = new Carbon($result['updated_at']);
$result['reconciled'] = 1 === (int)$result['reconciled'];
return $result;
}
/**
* @param array $groups
*
* @return array
*/
private function parseSums(array $groups): array
{
/**
* @var int $groudId
* @var array $group
*/
foreach ($groups as $groudId => $group) {
/** @var array $transaction */
foreach ($group['transactions'] as $transaction) {
$currencyId = (int)$transaction['currency_id'];
// set default:
if (!isset($groups[$groudId]['sums'][$currencyId])) {
$groups[$groudId]['sums'][$currencyId]['currency_id'] = $currencyId;
$groups[$groudId]['sums'][$currencyId]['currency_code'] = $transaction['currency_code'];
$groups[$groudId]['sums'][$currencyId]['currency_symbol'] = $transaction['currency_symbol'];
$groups[$groudId]['sums'][$currencyId]['currency_decimal_places'] = $transaction['currency_decimal_places'];
$groups[$groudId]['sums'][$currencyId]['amount'] = '0';
}
$groups[$groudId]['sums'][$currencyId]['amount'] = bcadd($groups[$groudId]['sums'][$currencyId]['amount'], $transaction['amount']);
if (null !== $transaction['foreign_amount'] && null !== $transaction['foreign_currency_id']) {
$currencyId = (int)$transaction['foreign_currency_id'];
// set default:
if (!isset($groups[$groudId]['sums'][$currencyId])) {
$groups[$groudId]['sums'][$currencyId]['currency_id'] = $currencyId;
$groups[$groudId]['sums'][$currencyId]['currency_code'] = $transaction['foreign_currency_code'];
$groups[$groudId]['sums'][$currencyId]['currency_symbol'] = $transaction['foreign_currency_symbol'];
$groups[$groudId]['sums'][$currencyId]['currency_decimal_places'] = $transaction['foreign_currency_decimal_places'];
$groups[$groudId]['sums'][$currencyId]['amount'] = '0';
}
$groups[$groudId]['sums'][$currencyId]['amount'] = bcadd($groups[$groudId]['sums'][$currencyId]['amount'], $transaction['foreign_amount']);
}
}
}
return $groups;
}
/**
* Build the query.
*/
private function startQuery(): void
{
app('log')->debug('TransactionCollector::startQuery');
$this->query = $this->user
->transactionGroups()
->leftJoin('transaction_journals', 'transaction_journals.transaction_group_id', 'transaction_groups.id')
// join source transaction.
->leftJoin(
'transactions as source', function (JoinClause $join) {
$join->on('source.transaction_journal_id', '=', 'transaction_journals.id')
->where('source.amount', '<', 0);
}
)
// join destination transaction
->leftJoin(
'transactions as destination', function (JoinClause $join) {
$join->on('destination.transaction_journal_id', '=', 'transaction_journals.id')
->where('destination.amount', '>', 0);
}
)
// left join transaction type.
->leftJoin('transaction_types', 'transaction_types.id', '=', 'transaction_journals.transaction_type_id')
->leftJoin('transaction_currencies as currency', 'currency.id', '=', 'source.transaction_currency_id')
->leftJoin('transaction_currencies as foreign_currency', 'foreign_currency.id', '=', 'source.foreign_currency_id')
->whereNull('transaction_groups.deleted_at')
->whereNull('transaction_journals.deleted_at')
->whereNull('source.deleted_at')
->whereNull('destination.deleted_at')
->orderBy('transaction_journals.date', 'DESC')
->orderBy('transaction_journals.order', 'ASC')
->orderBy('transaction_journals.id', 'DESC')
->orderBy('transaction_journals.description', 'DESC')
->orderBy('source.amount', 'DESC');
}
}

View File

@ -105,6 +105,7 @@ interface GroupCollectorInterface
*/
public function setCategory(Category $category): GroupCollectorInterface;
/**
* Limit results to a specific currency, either foreign or normal one.
*
@ -160,6 +161,15 @@ interface GroupCollectorInterface
*/
public function setTag(Tag $tag): GroupCollectorInterface;
/**
* Limit results to a specific set of tags.
*
* @param Collection $tags
*
* @return GroupCollectorInterface
*/
public function setTags(Collection $tags): GroupCollectorInterface;
/**
* Limit the search to one specific transaction group.
*
@ -201,6 +211,15 @@ interface GroupCollectorInterface
*/
public function withAccountInformation(): GroupCollectorInterface;
/**
* Limit the search to a specific bunch of categories.
*
* @param Collection $categories
*
* @return GroupCollectorInterface
*/
public function setCategories(Collection $categories): GroupCollectorInterface;
/**
* Include bill name + ID.
*

10
public/v1/js/app.js vendored
View File

@ -54641,7 +54641,7 @@ exports = module.exports = __webpack_require__(0)(false);
// module
exports.push([module.i, "\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n", ""]);
exports.push([module.i, "\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n", ""]);
// exports
@ -54961,6 +54961,14 @@ Object.defineProperty(__webpack_exports__, "__esModule", { value: true });
foreignCurrency = null;
}
// correct some id's
if (0 === destId) {
destId = null;
}
if (0 === sourceId) {
sourceId = null;
}
currentArray = {
type: transactionType,
date: date,

View File

@ -309,6 +309,14 @@
foreignCurrency = null;
}
// correct some id's
if(0 === destId) {
destId = null;
}
if(0 === sourceId) {
sourceId = null;
}
currentArray =
{
type: transactionType,

View File

@ -47,6 +47,7 @@
start: start.formatLocalized(monthAndDayFormat),
end: end.formatLocalized(monthAndDayFormat),
})|raw }}
</em>
</div>
{% else %}
@ -60,6 +61,7 @@
end: auditData[account.id].end,
balance: formatAmountByAccount(account,auditData[account.id].endBalance)
})|raw }}
<!-- TODO VERIFY THAT STEAM BALANCE ACTUALLY WORKS -->
</p>
{% include 'reports.partials.journals-audit' with {'journals': auditData[account.id].journals,'account':account} %}
<p style="padding:10px;">

View File

@ -201,7 +201,7 @@
</div>
</div>
{% endif %}
{% if topExpenses.count > 0 %}
{% if topExpenses|length > 0 %}
<div class="col-lg-6">
<div class="box">
@ -221,16 +221,18 @@
<tbody>
{% set totalSum = 0 %}
{% for row in topExpenses %}
{% set totalSum = totalSum + row.transaction_amount %}
{% set totalSum = totalSum + row.amount %}
{% if loop.index > listLength %}
<tr class="overListLength">
{% else %}
<tr>
{% endif %}
<td data-sortable="false">
<a href="{{ route('transactions.show', row.journal_id) }}">
{% if row.transaction_description|length > 0 %}
{{ row.transaction_description }} ({{ row.description }})
<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 %}
@ -239,14 +241,18 @@
<td data-value="{{ row.date.format('Y-m-d') }}">
{{ row.date.formatLocalized(monthAndDayFormat) }}
</td>
<td data-value="{{ row.opposing_account_name }}">
<a href="{{ route('accounts.show', row.opposing_account_id) }}">
{{ row.opposing_account_name }}
<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.transaction_amount }}" style="text-align: right;">
{{ row.transaction_amount|formatAmount }}
<td data-value="{{ row.amount}}" style="text-align: right;">
{{ formatAmountBySymbol(row.amount, row.currency_symbol, row.currency_symbol_decimal_places) }}
</td>
</tr>
{% endfor %}
</tbody>

View File

@ -25,7 +25,8 @@
{% for account in accounts %}
<tr>
<td data-value="{{ account.name }}">
<a href="{{ route('accounts.show', account.id) }}" title="{{ account.name }}">{{ account.name }}</a>
<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 }}"
@ -70,7 +71,8 @@
{% for category in categories %}
<tr>
<td data-value="{{ category.name }}">
<a href="{{ route('categories.show', category.id) }}" title="{{ category.name }}">{{ category.name }}</a>
<a href="{{ route('categories.show', category.id) }}"
title="{{ category.name }}">{{ category.name }}</a>
</td>
{% if categorySummary[category.id] %}
<td data-value="{{ categorySummary[category.id].earned }}"
@ -171,6 +173,7 @@
</div>
</div>
<div class="row">
{% if averageExpenses|length > 0 %}
<div class="col-lg-6">
<div class="box">
@ -217,7 +220,8 @@
{% if averageExpenses|length > listLength %}
<tr>
<td colspan="4" class="active">
<a href="#" class="listLengthTrigger">{{ trans('firefly.show_full_list',{number:incomeTopLength}) }}</a>
<a href="#"
class="listLengthTrigger">{{ trans('firefly.show_full_list',{number:incomeTopLength}) }}</a>
</td>
</tr>
{% endif %}
@ -234,12 +238,13 @@
</div>
</div>
{% endif %}
{% if topExpenses.count > 0 %}
{% if topExpenses|length > 0 %}
<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>
<h3 class="box-title">{{ 'expenses'|_ }} ({{ trans('firefly.topX', {number: listLength}) }}
)</h3>
</div>
<div class="box-body">
<table class="table table-hover sortable">
@ -261,9 +266,9 @@
<tr>
{% endif %}
<td data-sortable="false">
<a href="{{ route('transactions.show', row.journal_id) }}">
{% if row.transaction_description|length > 0 %}
{{ row.transaction_description }} ({{ row.description }})
<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 %}
@ -272,13 +277,13 @@
<td data-value="{{ row.date.format('Y-m-d') }}">
{{ row.date.formatLocalized(monthAndDayFormat) }}
</td>
<td data-value="{{ row.opposing_account_name }}">
<a href="{{ route('accounts.show', row.opposing_account_id) }}">
{{ row.opposing_account_name }}
<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.transaction_amount }}" style="text-align: right;">
{{ row.transaction_amount|formatAmount }}
<td data-value="{{ row.amount }}" style="text-align: right;">
{{ formatAmountBySymbol(row.amount, row.currency_symbol, row.currency_symbol_decimal_places) }}
</td>
</tr>
{% endfor %}
@ -287,7 +292,8 @@
{% if topExpenses|length > listLength %}
<tr>
<td colspan="3" class="active">
<a href="#" class="listLengthTrigger">{{ trans('firefly.show_full_list',{number:incomeTopLength}) }}</a>
<a href="#"
class="listLengthTrigger">{{ trans('firefly.show_full_list',{number:incomeTopLength}) }}</a>
</td>
</tr>
{% endif %}
@ -407,7 +413,8 @@
{% if topIncome.count > listLength %}
<tr>
<td colspan="3" class="active">
<a href="#" class="listLengthTrigger">{{ trans('firefly.show_full_list',{number:incomeTopLength}) }}</a>
<a href="#"
class="listLengthTrigger">{{ trans('firefly.show_full_list',{number:incomeTopLength}) }}</a>
</td>
</tr>
{% endif %}

View File

@ -1 +1,114 @@
<h1 style="color:red;">REPLACE ME</h1>
<table class="table table-hover table-compressed">
<thead>
<tr class="ignore">
<th class="hide-buttons">&nbsp;</th>
<th class="hide-icon">&nbsp;</th>
<th class="hide-description">{{ trans('list.description') }}</th>
<th class="hide-balance_before" style="text-align: right;">{{ trans('list.balance_before') }}</th>
<th class="hide-amount" style="text-align: right;">{{ trans('list.amount') }}</th>
<th class="hide-balance_after" style="text-align: right;">{{ trans('list.balance_after') }}</th>
<th class="hide-date">{{ trans('list.date') }}</th>
{# new optional fields (3x) #}
<th class="hide-from">{{ trans('list.from') }}</th>
<th class="hide-to">{{ trans('list.to') }}</th>
<th class="hide-budget"><i class="fa fa-tasks fa-fw" title="{{ trans('list.budget') }}"></i></th>
<th class="hide-category"><i class="fa fa-bar-chart fa-fw" title="{{ trans('list.category') }}"></i></th>
<th class="hide-bill">{{ trans('list.bill') }}</th>
{# more optional fields (2x) #}
<th class="hide-create_date">{{ trans('list.create_date') }}</th>
<th class="hide-update_date">{{ trans('list.update_date') }}</th>
</tr>
</thead>
<tbody>
{% for journal in journals %}
<tr data-date="{{ journal.date.format('Y-m-d') }}" data-id="{{ journal.id }}">
<td class="hide-buttons">
<div class="btn-group btn-group-xs">
<a href="{{ route('transactions.edit',journal.transaction_group_id) }}" class="btn btn-xs btn-default"><i class="fa fa-fw fa-pencil"></i></a>
<a href="{{ route('transactions.delete',journal.transaction_group_id) }}" class="btn btn-xs btn-danger"><i class="fa fa-fw fa-trash-o"></i></a>
</div>
</td>
<td class="hide-icon">
{% if journal.transaction_type_type == 'Withdrawal' %}
<i class="fa fa-long-arrow-left fa-fw" title="{{ trans('firefly.Withdrawal') }}"></i>
{% endif %}
{% if journal.transaction_type_type == 'Deposit' %}
<i class="fa fa-long-arrow-right fa-fw" title="{{ trans('firefly.Deposit') }}"></i>
{% endif %}
{% if journal.transaction_type_type == 'Transfer' %}
<i class="fa fa-exchange fa-fw" title="{{ trans('firefly.Deposit') }}"></i>
{% endif %}
{% if journal.transaction_type_type == 'Reconciliation' %}
XX
{% endif %}
{% if journal.transaction_type_type == 'Opening balance' %}
XX
{% endif %}
</td>
<td class="hide-description">
<a href="{{ route('transactions.show',journal.transaction_group_id) }}">
{% if journal.group_title|length > 0 %}
{{ journal.group_title }} ({{ journal.description }})
{% else %}
{{ journal.description }}
{% endif %}
</a>
</td>
<td class="hide-balance_before" style="text-align: right;">
{{ formatAmountBySymbol(journal.balance_before, journal.currency_symbol, journal.currency_symbol_decimal_places) }}
</td>
<td class="hide-amount" style="text-align: right;">
{{ formatAmountBySymbol(journal.amount, journal.currency_symbol, journal.currency_symbol_decimal_places) }}
</td>
<td class="hide-balance_after" style="text-align: right;">
{{ formatAmountBySymbol(journal.balance_after, journal.currency_symbol, journal.currency_symbol_decimal_places) }}
</td>
<td class="hide-date">{{ journal.date.formatLocalized(monthAndDayFormat) }}</td>
<td class="hide-from">
<a href="{{ route('accounts.show', [journal.source_account_id]) }}" title="{{ journal.source_account_iban|default(journal.source_account_name) }}">{{ journal.source_account_name }}</a>
</td>
<td class="hide-to">
<a href="{{ route('accounts.show', [journal.destination_account_id]) }}" title="{{ journal.destination_account_iban|default(journal.destination_account_name) }}">{{ journal.destination_account_name }}</a>
</td>
<td class="hide-budget">
TODO BUDGET
</td>
<td class="hide-category">
TODO CATEGORY
</td>
<td class="hide-bill">
TODO BILL
</td>
<!-- new optional fields (2x) -->
<td class="hide-create_date">
{{ journal.created_at.formatLocalized(dateTimeFormat) }}
</td>
<td class="hide-update_date">
{{ journal.updated_at.formatLocalized(dateTimeFormat) }}
</td>
</tr>
{% endfor %}
</tbody>
</table>

View File

@ -256,7 +256,7 @@
</div>
</div>
{% endif %}
{% if topExpenses.count > 0 %}
{% if topExpenses|length > 0 %}
<div class="col-lg-6">
<div class="box">
@ -283,9 +283,9 @@
<tr>
{% endif %}
<td data-sortable="false">
<a href="{{ route('transactions.show', row.journal_id) }}">
{% if row.transaction_description|length > 0 %}
{{ row.transaction_description }} ({{ row.description }})
<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 %}
@ -294,13 +294,13 @@
<td data-value="{{ row.date.format('Y-m-d') }}">
{{ row.date.formatLocalized(monthAndDayFormat) }}
</td>
<td data-value="{{ row.opposing_account_name }}">
<a href="{{ route('accounts.show', row.opposing_account_id) }}">
{{ row.opposing_account_name }}
<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.transaction_amount }}" style="text-align: right;">
{{ row.transaction_amount|formatAmount }}
<td data-value="{{ row.amount }}" style="text-align: right;">
{{ formatAmountBySymbol(row.amount, row.currency_symbol, row.currency_symbol_decimal_places) }}
</td>
</tr>
{% endfor %}