Rename journal collector to more fitting transaction collector.

This commit is contained in:
James Cole
2018-08-11 14:33:47 +02:00
parent 96baf5d3c7
commit 9914c0791e
75 changed files with 595 additions and 1833 deletions

View File

@@ -24,7 +24,7 @@ declare(strict_types=1);
namespace FireflyIII\Helpers\Chart;
use Carbon\Carbon;
use FireflyIII\Helpers\Collector\JournalCollectorInterface;
use FireflyIII\Helpers\Collector\TransactionCollectorInterface;
use FireflyIII\Helpers\Filter\NegativeAmountFilter;
use FireflyIII\Helpers\Filter\OpposingAccountFilter;
use FireflyIII\Helpers\Filter\PositiveAmountFilter;
@@ -108,12 +108,12 @@ class MetaPieChart implements MetaPieChartInterface
// also collect all other transactions
if ($this->collectOtherObjects && 'expense' === $direction) {
/** @var JournalCollectorInterface $collector */
$collector = app(JournalCollectorInterface::class);
/** @var TransactionCollectorInterface $collector */
$collector = app(TransactionCollectorInterface::class);
$collector->setUser($this->user);
$collector->setAccounts($this->accounts)->setRange($this->start, $this->end)->setTypes([TransactionType::WITHDRAWAL]);
$journals = $collector->getJournals();
$journals = $collector->getTransactions();
$sum = (string)$journals->sum('transaction_amount');
$sum = bcmul($sum, '-1');
$sum = bcsub($sum, $this->total);
@@ -121,11 +121,11 @@ class MetaPieChart implements MetaPieChartInterface
}
if ($this->collectOtherObjects && 'income' === $direction) {
/** @var JournalCollectorInterface $collector */
$collector = app(JournalCollectorInterface::class);
/** @var TransactionCollectorInterface $collector */
$collector = app(TransactionCollectorInterface::class);
$collector->setUser($this->user);
$collector->setAccounts($this->accounts)->setRange($this->start, $this->end)->setTypes([TransactionType::DEPOSIT]);
$journals = $collector->getJournals();
$journals = $collector->getTransactions();
$sum = (string)$journals->sum('transaction_amount');
$sum = bcsub($sum, $this->total);
$chartData[$key] = $sum;
@@ -271,8 +271,8 @@ class MetaPieChart implements MetaPieChartInterface
*/
protected function getTransactions(string $direction): Collection
{
/** @var JournalCollectorInterface $collector */
$collector = app(JournalCollectorInterface::class);
/** @var TransactionCollectorInterface $collector */
$collector = app(TransactionCollectorInterface::class);
$types = [TransactionType::DEPOSIT, TransactionType::TRANSFER];
$collector->addFilter(NegativeAmountFilter::class);
if ('expense' === $direction) {
@@ -304,7 +304,7 @@ class MetaPieChart implements MetaPieChartInterface
// @codeCoverageIgnoreEnd
return $collector->getJournals();
return $collector->getTransactions();
}
/**

View File

@@ -1,904 +0,0 @@
<?php
/**
* JournalCollector.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 PhpDynamicAsStaticMethodCallInspection */
/** @noinspection PropertyCanBeStaticInspection */
declare(strict_types=1);
namespace FireflyIII\Helpers\Collector;
use Carbon\Carbon;
use DB;
use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Helpers\Filter\CountAttachmentsFilter;
use FireflyIII\Helpers\Filter\FilterInterface;
use FireflyIII\Helpers\Filter\InternalTransferFilter;
use FireflyIII\Helpers\Filter\NegativeAmountFilter;
use FireflyIII\Helpers\Filter\OpposingAccountFilter;
use FireflyIII\Helpers\Filter\PositiveAmountFilter;
use FireflyIII\Helpers\Filter\SplitIndicatorFilter;
use FireflyIII\Helpers\Filter\TransactionViewFilter;
use FireflyIII\Helpers\Filter\TransferFilter;
use FireflyIII\Models\AccountType;
use FireflyIII\Models\Budget;
use FireflyIII\Models\Category;
use FireflyIII\Models\Tag;
use FireflyIII\Models\Transaction;
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
use FireflyIII\Support\CacheProperties;
use FireflyIII\User;
use Illuminate\Database\Eloquent\Builder as EloquentBuilder;
use Illuminate\Database\Query\JoinClause;
use Illuminate\Pagination\LengthAwarePaginator;
use Illuminate\Support\Collection;
use Log;
/**
* Maybe this is a good idea after all...
*
* Class JournalCollector
* @SuppressWarnings(PHPMD.TooManyPublicMethods)
* @SuppressWarnings(PHPMD.ExcessiveClassComplexity)
* @SuppressWarnings(PHPMD.CouplingBetweenObjects)
*
*/
class JournalCollector implements JournalCollectorInterface
{
/** @var array */
private $accountIds = [];
/** @var int */
private $count = 0;
/** @var array */
private $fields
= [
'transaction_journals.id as journal_id',
'transaction_journals.description',
'transaction_journals.date',
'transaction_journals.encrypted',
'transaction_journals.created_at',
'transaction_journals.updated_at',
'transaction_types.type as transaction_type_type',
'transaction_journals.bill_id',
'transaction_journals.updated_at',
'bills.name as bill_name',
'bills.name_encrypted as bill_name_encrypted',
'transactions.id as id',
'transactions.description as transaction_description',
'transactions.account_id',
'transactions.reconciled',
'transactions.identifier',
'transactions.transaction_journal_id',
'transactions.amount as transaction_amount',
'transactions.transaction_currency_id as transaction_currency_id',
'transaction_currencies.code as transaction_currency_code',
'transaction_currencies.symbol as transaction_currency_symbol',
'transaction_currencies.decimal_places as transaction_currency_dp',
'transactions.foreign_amount as transaction_foreign_amount',
'transactions.foreign_currency_id as foreign_currency_id',
'foreign_currencies.code as foreign_currency_code',
'foreign_currencies.symbol as foreign_currency_symbol',
'foreign_currencies.decimal_places as foreign_currency_dp',
'accounts.name as account_name',
'accounts.encrypted as account_encrypted',
'accounts.iban as account_iban',
'account_types.type as account_type',
];
/** @var array */
private $filters = [InternalTransferFilter::class];
/** @var bool */
private $ignoreCache = false;
/** @var bool */
private $joinedBudget = false;
/** @var bool */
private $joinedCategory = false;
/** @var bool */
private $joinedOpposing = false;
/** @var bool */
private $joinedTag = false;
/** @var int */
private $limit;
/** @var int */
private $offset;
/** @var int */
private $page = 1;
/** @var EloquentBuilder */
private $query;
/** @var bool */
private $run = false;
/** @var User */
private $user;
/**
* @param string $filter
*
* @return JournalCollectorInterface
*/
public function addFilter(string $filter): JournalCollectorInterface
{
$interfaces = class_implements($filter);
if (\in_array(FilterInterface::class, $interfaces, true) && !\in_array($filter, $this->filters, true)) {
Log::debug(sprintf('Enabled filter %s', $filter));
$this->filters[] = $filter;
}
return $this;
}
/**
* @param string $amount
*
* @return JournalCollectorInterface
*/
public function amountIs(string $amount): JournalCollectorInterface
{
$this->query->where(
function (EloquentBuilder $q) use ($amount) {
$q->where('transactions.amount', $amount);
$q->orWhere('transactions.amount', bcmul($amount, '-1'));
}
);
return $this;
}
/**
* @param string $amount
*
* @return JournalCollectorInterface
*/
public function amountLess(string $amount): JournalCollectorInterface
{
$this->query->where(
function (EloquentBuilder $q1) use ($amount) {
$q1->where(
function (EloquentBuilder $q2) use ($amount) {
// amount < 0 and .amount > -$amount
$invertedAmount = bcmul($amount, '-1');
$q2->where('transactions.amount', '<', 0)->where('transactions.amount', '>', $invertedAmount);
}
)
->orWhere(
function (EloquentBuilder $q3) use ($amount) {
// amount > 0 and .amount < $amount
$q3->where('transactions.amount', '>', 0)->where('transactions.amount', '<', $amount);
}
);
}
);
return $this;
}
/**
* @param string $amount
*
* @return JournalCollectorInterface
*/
public function amountMore(string $amount): JournalCollectorInterface
{
$this->query->where(
function (EloquentBuilder $q1) use ($amount) {
$q1->where(
function (EloquentBuilder $q2) use ($amount) {
// amount < 0 and .amount < -$amount
$invertedAmount = bcmul($amount, '-1');
$q2->where('transactions.amount', '<', 0)->where('transactions.amount', '<', $invertedAmount);
}
)
->orWhere(
function (EloquentBuilder $q3) use ($amount) {
// amount > 0 and .amount > $amount
$q3->where('transactions.amount', '>', 0)->where('transactions.amount', '>', $amount);
}
);
}
);
return $this;
}
/**
* @return int
*
* @throws FireflyException
*/
public function count(): int
{
if (true === $this->run) {
throw new FireflyException('Cannot count after run in JournalCollector.');
}
$countQuery = clone $this->query;
// dont need some fields:
$countQuery->getQuery()->limit = null;
$countQuery->getQuery()->offset = null;
$countQuery->getQuery()->unionLimit = null;
$countQuery->getQuery()->groups = null;
$countQuery->getQuery()->orders = null;
$countQuery->groupBy('accounts.user_id');
$this->count = (int)$countQuery->count();
return $this->count;
}
/** @noinspection MultipleReturnStatementsInspection */
/**
* @return Collection
* @SuppressWarnings(PHPMD.CyclomaticComplexity)
* @SuppressWarnings(PHPMD.ExcessiveMethodLength)
*/
public function getJournals(): Collection
{
$this->run = true;
// find query set in cache.
$hash = hash('sha256', $this->query->toSql() . serialize($this->query->getBindings()));
$key = 'query-' . substr($hash, -8);
$cache = new CacheProperties;
$cache->addProperty($key);
foreach ($this->filters as $filter) {
$cache->addProperty((string)$filter);
}
if (false === $this->ignoreCache && $cache->has()) {
Log::debug(sprintf('Return cache of query with ID "%s".', $key));
return $cache->get(); // @codeCoverageIgnore
}
/** @var Collection $set */
$set = $this->query->get(array_values($this->fields));
// run all filters:
$set = $this->filter($set);
// loop for decryption.
$set->each(
function (Transaction $transaction) {
$transaction->date = new Carbon($transaction->date);
$transaction->description = app('steam')->decrypt((int)$transaction->encrypted, $transaction->description);
if (null !== $transaction->bill_name) {
$transaction->bill_name = app('steam')->decrypt((int)$transaction->bill_name_encrypted, $transaction->bill_name);
}
$transaction->account_name = app('steam')->tryDecrypt($transaction->account_name);
$transaction->opposing_account_name = app('steam')->tryDecrypt($transaction->opposing_account_name);
$transaction->account_iban = app('steam')->tryDecrypt($transaction->account_iban);
$transaction->opposing_account_iban = app('steam')->tryDecrypt($transaction->opposing_account_iban);
// budget name
$transaction->transaction_journal_budget_name = app('steam')->tryDecrypt($transaction->transaction_journal_budget_name);
$transaction->transaction_budget_name = app('steam')->tryDecrypt($transaction->transaction_budget_name);
// category name:
$transaction->transaction_journal_category_name = app('steam')->tryDecrypt($transaction->transaction_journal_category_name);
$transaction->transaction_category_name = app('steam')->tryDecrypt($transaction->transaction_category_name);
}
);
Log::debug(sprintf('Cached query with ID "%s".', $key));
$cache->store($set);
return $set;
}
/**
* @return LengthAwarePaginator
*
* @throws FireflyException
*/
public function getPaginatedJournals(): LengthAwarePaginator
{
if (true === $this->run) {
throw new FireflyException('Cannot getPaginatedJournals after run in JournalCollector.');
}
$this->count();
$set = $this->getJournals();
$journals = new LengthAwarePaginator($set, $this->count, $this->limit, $this->page);
return $journals;
}
/**
* @return EloquentBuilder
*/
public function getQuery(): EloquentBuilder
{
return $this->query;
}
/**
* @return JournalCollectorInterface
*/
public function ignoreCache(): JournalCollectorInterface
{
$this->ignoreCache = true;
return $this;
}
/**
* @param string $filter
*
* @return JournalCollectorInterface
*/
public function removeFilter(string $filter): JournalCollectorInterface
{
$key = array_search($filter, $this->filters, true);
if (!(false === $key)) {
Log::debug(sprintf('Removed filter %s', $filter));
unset($this->filters[$key]);
}
return $this;
}
/**
* @param Collection $accounts
*
* @return JournalCollectorInterface
*/
public function setAccounts(Collection $accounts): JournalCollectorInterface
{
if ($accounts->count() > 0) {
$accountIds = $accounts->pluck('id')->toArray();
$this->query->whereIn('transactions.account_id', $accountIds);
Log::debug(sprintf('setAccounts: %s', implode(', ', $accountIds)));
$this->accountIds = $accountIds;
}
if ($accounts->count() > 1) {
$this->addFilter(TransferFilter::class);
}
return $this;
}
/**
* @param Carbon $after
*
* @return JournalCollectorInterface
*/
public function setAfter(Carbon $after): JournalCollectorInterface
{
$afterStr = $after->format('Y-m-d 00:00:00');
$this->query->where('transaction_journals.date', '>=', $afterStr);
Log::debug(sprintf('JournalCollector range is now after %s (inclusive)', $afterStr));
return $this;
}
/**
* @return JournalCollectorInterface
*/
public function setAllAssetAccounts(): JournalCollectorInterface
{
/** @var AccountRepositoryInterface $repository */
$repository = app(AccountRepositoryInterface::class);
$repository->setUser($this->user);
$accounts = $repository->getAccountsByType([AccountType::ASSET, AccountType::DEFAULT]);
if ($accounts->count() > 0) {
$accountIds = $accounts->pluck('id')->toArray();
$this->query->whereIn('transactions.account_id', $accountIds);
$this->accountIds = $accountIds;
}
if ($accounts->count() > 1) {
$this->addFilter(TransferFilter::class);
}
return $this;
}
/**
* @param Carbon $before
*
* @return JournalCollectorInterface
*/
public function setBefore(Carbon $before): JournalCollectorInterface
{
$beforeStr = $before->format('Y-m-d 00:00:00');
$this->query->where('transaction_journals.date', '<=', $beforeStr);
Log::debug(sprintf('JournalCollector range is now before %s (inclusive)', $beforeStr));
return $this;
}
/**
* @param Collection $bills
*
* @return JournalCollectorInterface
*/
public function setBills(Collection $bills): JournalCollectorInterface
{
if ($bills->count() > 0) {
$billIds = $bills->pluck('id')->toArray();
$this->query->whereIn('transaction_journals.bill_id', $billIds);
}
return $this;
}
/**
* @param Budget $budget
*
* @return JournalCollectorInterface
*/
public function setBudget(Budget $budget): JournalCollectorInterface
{
$this->joinBudgetTables();
$this->query->where(
function (EloquentBuilder $q) use ($budget) {
$q->where('budget_transaction.budget_id', $budget->id);
$q->orWhere('budget_transaction_journal.budget_id', $budget->id);
}
);
return $this;
}
/**
* @param Collection $budgets
*
* @return JournalCollectorInterface
*/
public function setBudgets(Collection $budgets): JournalCollectorInterface
{
$budgetIds = $budgets->pluck('id')->toArray();
if (0 !== \count($budgetIds)) {
$this->joinBudgetTables();
Log::debug('Journal collector will filter for budgets', $budgetIds);
$this->query->where(
function (EloquentBuilder $q) use ($budgetIds) {
$q->whereIn('budget_transaction.budget_id', $budgetIds);
$q->orWhereIn('budget_transaction_journal.budget_id', $budgetIds);
}
);
}
return $this;
}
/**
* @param Collection $categories
*
* @return JournalCollectorInterface
*/
public function setCategories(Collection $categories): JournalCollectorInterface
{
$categoryIds = $categories->pluck('id')->toArray();
if (0 !== \count($categoryIds)) {
$this->joinCategoryTables();
$this->query->where(
function (EloquentBuilder $q) use ($categoryIds) {
$q->whereIn('category_transaction.category_id', $categoryIds);
$q->orWhereIn('category_transaction_journal.category_id', $categoryIds);
}
);
}
return $this;
}
/**
* @param Category $category
*
* @return JournalCollectorInterface
*/
public function setCategory(Category $category): JournalCollectorInterface
{
$this->joinCategoryTables();
$this->query->where(
function (EloquentBuilder $q) use ($category) {
$q->where('category_transaction.category_id', $category->id);
$q->orWhere('category_transaction_journal.category_id', $category->id);
}
);
return $this;
}
/**
* @param Collection $journals
*
* @return JournalCollectorInterface
*/
public function setJournals(Collection $journals): JournalCollectorInterface
{
$ids = $journals->pluck('id')->toArray();
$this->query->where(
function (EloquentBuilder $q) use ($ids) {
$q->whereIn('transaction_journals.id', $ids);
}
);
return $this;
}
/**
* @param int $limit
*
* @return JournalCollectorInterface
*/
public function setLimit(int $limit): JournalCollectorInterface
{
$this->limit = $limit;
$this->query->limit($limit);
Log::debug(sprintf('Set limit to %d', $limit));
return $this;
}
/**
* @param int $offset
*
* @return JournalCollectorInterface
*/
public function setOffset(int $offset): JournalCollectorInterface
{
$this->offset = $offset;
return $this;
}
/**
* @param Collection $accounts
*
* @return JournalCollectorInterface
*/
public function setOpposingAccounts(Collection $accounts): JournalCollectorInterface
{
$this->withOpposingAccount();
$this->query->whereIn('opposing.account_id', $accounts->pluck('id')->toArray());
return $this;
}
/**
* @param int $page
*
* @return JournalCollectorInterface
*/
public function setPage(int $page): JournalCollectorInterface
{
if ($page < 1) {
$page = 1;
}
$this->page = $page;
if ($page > 0) {
--$page;
}
Log::debug(sprintf('Page is %d', $page));
if (null !== $this->limit) {
$offset = ($this->limit * $page);
$this->offset = $offset;
$this->query->skip($offset);
Log::debug(sprintf('Changed offset to %d', $offset));
}
return $this;
}
/**
* @param Carbon $start
* @param Carbon $end
*
* @return JournalCollectorInterface
*/
public function setRange(Carbon $start, Carbon $end): JournalCollectorInterface
{
if ($start <= $end) {
$startStr = $start->format('Y-m-d 00:00:00');
$endStr = $end->format('Y-m-d 23:59:59');
$this->query->where('transaction_journals.date', '>=', $startStr);
$this->query->where('transaction_journals.date', '<=', $endStr);
Log::debug(sprintf('JournalCollector range is now %s - %s (inclusive)', $startStr, $endStr));
}
return $this;
}
/**
* @param Tag $tag
*
* @return JournalCollectorInterface
*/
public function setTag(Tag $tag): JournalCollectorInterface
{
$this->joinTagTables();
$this->query->where('tag_transaction_journal.tag_id', $tag->id);
return $this;
}
/**
* @param Collection $tags
*
* @return JournalCollectorInterface
*/
public function setTags(Collection $tags): JournalCollectorInterface
{
$this->joinTagTables();
$tagIds = $tags->pluck('id')->toArray();
$this->query->whereIn('tag_transaction_journal.tag_id', $tagIds);
return $this;
}
/**
* @param array $types
*
* @return JournalCollectorInterface
*/
public function setTypes(array $types): JournalCollectorInterface
{
if (\count($types) > 0) {
Log::debug('Set query collector types', $types);
$this->query->whereIn('transaction_types.type', $types);
}
return $this;
}
/**
* @param User $user
*/
public function setUser(User $user)
{
Log::debug(sprintf('Journal collector now collecting for user #%d', $user->id));
$this->user = $user;
$this->startQuery();
}
/**
*
*/
public function startQuery(): void
{
Log::debug('journalCollector::startQuery');
/** @var EloquentBuilder $query */
$query = Transaction::leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id')
->leftJoin('transaction_types', 'transaction_types.id', 'transaction_journals.transaction_type_id')
->leftJoin('bills', 'bills.id', 'transaction_journals.bill_id')
->leftJoin('accounts', 'accounts.id', '=', 'transactions.account_id')
->leftJoin('account_types', 'accounts.account_type_id', 'account_types.id')
->leftJoin('transaction_currencies', 'transaction_currencies.id', 'transactions.transaction_currency_id')
->leftJoin('transaction_currencies as foreign_currencies', 'foreign_currencies.id', 'transactions.foreign_currency_id')
->whereNull('transactions.deleted_at')
->whereNull('transaction_journals.deleted_at')
->where('transaction_journals.user_id', $this->user->id)
->orderBy('transaction_journals.date', 'DESC')
->orderBy('transaction_journals.order', 'ASC')
->orderBy('transaction_journals.id', 'DESC')
->orderBy('transaction_journals.description', 'DESC')
->orderBy('transactions.identifier', 'ASC')
->orderBy('transactions.amount', 'DESC');
$this->query = $query;
}
/**
* @return JournalCollectorInterface
*/
public function withBudgetInformation(): JournalCollectorInterface
{
$this->joinBudgetTables();
return $this;
}
/**
* @return JournalCollectorInterface
*/
public function withCategoryInformation(): JournalCollectorInterface
{
$this->joinCategoryTables();
return $this;
}
/**
* @return JournalCollectorInterface
*/
public function withOpposingAccount(): JournalCollectorInterface
{
$this->joinOpposingTables();
return $this;
}
/**
* @return JournalCollectorInterface
*/
public function withoutBudget(): JournalCollectorInterface
{
$this->joinBudgetTables();
$this->query->where(
function (EloquentBuilder $q) {
$q->whereNull('budget_transaction.budget_id');
$q->whereNull('budget_transaction_journal.budget_id');
}
);
return $this;
}
/**
* @return JournalCollectorInterface
*/
public function withoutCategory(): JournalCollectorInterface
{
$this->joinCategoryTables();
$this->query->where(
function (EloquentBuilder $q) {
$q->whereNull('category_transaction.category_id');
$q->whereNull('category_transaction_journal.category_id');
}
);
return $this;
}
/**
* @param Collection $set
*
* @return Collection
*/
private function filter(Collection $set): Collection
{
// create all possible filters:
$filters = [
InternalTransferFilter::class => new InternalTransferFilter($this->accountIds),
OpposingAccountFilter::class => new OpposingAccountFilter($this->accountIds),
TransferFilter::class => new TransferFilter,
PositiveAmountFilter::class => new PositiveAmountFilter,
NegativeAmountFilter::class => new NegativeAmountFilter,
SplitIndicatorFilter::class => new SplitIndicatorFilter,
CountAttachmentsFilter::class => new CountAttachmentsFilter,
TransactionViewFilter::class => new TransactionViewFilter,
];
Log::debug(sprintf('Will run %d filters on the set.', \count($this->filters)));
foreach ($this->filters as $enabled) {
if (isset($filters[$enabled])) {
Log::debug(sprintf('Before filter %s: %d', $enabled, $set->count()));
/** @var Collection $set */
$set = $filters[$enabled]->filter($set);
Log::debug(sprintf('After filter %s: %d', $enabled, $set->count()));
}
}
return $set;
}
/**
*
*/
private function joinBudgetTables(): void
{
if (!$this->joinedBudget) {
// join some extra tables:
$this->joinedBudget = true;
$this->query->leftJoin('budget_transaction_journal', 'budget_transaction_journal.transaction_journal_id', '=', 'transaction_journals.id');
$this->query->leftJoin('budgets as transaction_journal_budgets', 'transaction_journal_budgets.id', '=', 'budget_transaction_journal.budget_id');
$this->query->leftJoin('budget_transaction', 'budget_transaction.transaction_id', '=', 'transactions.id');
$this->query->leftJoin('budgets as transaction_budgets', 'transaction_budgets.id', '=', 'budget_transaction.budget_id');
$this->query->whereNull('transaction_journal_budgets.deleted_at');
$this->query->whereNull('transaction_budgets.deleted_at');
$this->fields[] = 'budget_transaction_journal.budget_id as transaction_journal_budget_id';
$this->fields[] = 'transaction_journal_budgets.encrypted as transaction_journal_budget_encrypted';
$this->fields[] = 'transaction_journal_budgets.name as transaction_journal_budget_name';
$this->fields[] = 'budget_transaction.budget_id as transaction_budget_id';
$this->fields[] = 'transaction_budgets.encrypted as transaction_budget_encrypted';
$this->fields[] = 'transaction_budgets.name as transaction_budget_name';
}
}
/**
*
*/
private function joinCategoryTables(): void
{
if (!$this->joinedCategory) {
// join some extra tables:
$this->joinedCategory = true;
$this->query->leftJoin('category_transaction_journal', 'category_transaction_journal.transaction_journal_id', '=', 'transaction_journals.id');
$this->query->leftJoin(
'categories as transaction_journal_categories',
'transaction_journal_categories.id',
'=',
'category_transaction_journal.category_id'
);
$this->query->leftJoin('category_transaction', 'category_transaction.transaction_id', '=', 'transactions.id');
$this->query->leftJoin('categories as transaction_categories', 'transaction_categories.id', '=', 'category_transaction.category_id');
$this->query->whereNull('transaction_journal_categories.deleted_at');
$this->query->whereNull('transaction_categories.deleted_at');
$this->fields[] = 'category_transaction_journal.category_id as transaction_journal_category_id';
$this->fields[] = 'transaction_journal_categories.encrypted as transaction_journal_category_encrypted';
$this->fields[] = 'transaction_journal_categories.name as transaction_journal_category_name';
$this->fields[] = 'category_transaction.category_id as transaction_category_id';
$this->fields[] = 'transaction_categories.encrypted as transaction_category_encrypted';
$this->fields[] = 'transaction_categories.name as transaction_category_name';
}
}
/**
*
*/
private function joinOpposingTables(): void
{
if (!$this->joinedOpposing) {
Log::debug('joinedOpposing is false');
// join opposing transaction (hard):
$this->query->leftJoin(
'transactions as opposing',
function (JoinClause $join) {
$join->on('opposing.transaction_journal_id', '=', 'transactions.transaction_journal_id')
->where('opposing.identifier', '=', DB::raw('transactions.identifier'))
->where('opposing.amount', '=', DB::raw('transactions.amount * -1'));
}
);
$this->query->leftJoin('accounts as opposing_accounts', 'opposing.account_id', '=', 'opposing_accounts.id');
$this->query->leftJoin('account_types as opposing_account_types', 'opposing_accounts.account_type_id', '=', 'opposing_account_types.id');
$this->query->whereNull('opposing.deleted_at');
$this->fields[] = 'opposing.id as opposing_id';
$this->fields[] = 'opposing.account_id as opposing_account_id';
$this->fields[] = 'opposing_accounts.name as opposing_account_name';
$this->fields[] = 'opposing_accounts.encrypted as opposing_account_encrypted';
$this->fields[] = 'opposing_accounts.iban as opposing_account_iban';
$this->fields[] = 'opposing_account_types.type as opposing_account_type';
$this->joinedOpposing = true;
Log::debug('joinedOpposing is now true!');
}
}
/**
*
*/
private function joinTagTables(): void
{
if (!$this->joinedTag) {
// join some extra tables:
$this->joinedTag = true;
$this->query->leftJoin('tag_transaction_journal', 'tag_transaction_journal.transaction_journal_id', '=', 'transaction_journals.id');
}
}
}

View File

@@ -1,329 +0,0 @@
<?php
/**
* JournalCollectorInterface.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/>.
*/
declare(strict_types=1);
namespace FireflyIII\Helpers\Collector;
use Carbon\Carbon;
use FireflyIII\Models\Budget;
use FireflyIII\Models\Category;
use FireflyIII\Models\Tag;
use FireflyIII\User;
use Illuminate\Database\Eloquent\Builder as EloquentBuilder;
use Illuminate\Pagination\LengthAwarePaginator;
use Illuminate\Support\Collection;
/**
* Interface JournalCollectorInterface.
*/
interface JournalCollectorInterface
{
/**
* Add a specific filter.
*
* @param string $filter
*
* @return JournalCollectorInterface
*/
public function addFilter(string $filter): JournalCollectorInterface;
/**
* Get transactions with a specific amount.
*
* @param string $amount
*
* @return JournalCollectorInterface
*/
public function amountIs(string $amount): JournalCollectorInterface;
/**
* Get transactions where the amount is less than.
*
* @param string $amount
*
* @return JournalCollectorInterface
*/
public function amountLess(string $amount): JournalCollectorInterface;
/**
* Get transactions where the amount is more than.
*
* @param string $amount
*
* @return JournalCollectorInterface
*/
public function amountMore(string $amount): JournalCollectorInterface;
/**
* Count the result.
*
* @return int
*/
public function count(): int;
/**
* Get all journals.
*
* @return Collection
*/
public function getJournals(): Collection;
/**
* Get a paginated result.
*
* @return LengthAwarePaginator
*/
public function getPaginatedJournals(): LengthAwarePaginator;
/**
* Get the query.
*
* @return EloquentBuilder
*/
public function getQuery(): EloquentBuilder;
/**
* Set to ignore the cache.
*
* @return JournalCollectorInterface
*/
public function ignoreCache(): JournalCollectorInterface;
/**
* Remove a filter.
*
* @param string $filter
*
* @return JournalCollectorInterface
*/
public function removeFilter(string $filter): JournalCollectorInterface;
/**
* Set the accounts to collect from.
*
* @param Collection $accounts
*
* @return JournalCollectorInterface
*/
public function setAccounts(Collection $accounts): JournalCollectorInterface;
/**
* Collect journals after a specific date.
*
* @param Carbon $after
*
* @return JournalCollectorInterface
*/
public function setAfter(Carbon $after): JournalCollectorInterface;
/**
* Include all asset accounts.
*
* @return JournalCollectorInterface
*/
public function setAllAssetAccounts(): JournalCollectorInterface;
/**
* Collect journals before a specific date.
*
* @param Carbon $before
*
* @return JournalCollectorInterface
*/
public function setBefore(Carbon $before): JournalCollectorInterface;
/**
* Set the bills to filter on.
*
* @param Collection $bills
*
* @return JournalCollectorInterface
*/
public function setBills(Collection $bills): JournalCollectorInterface;
/**
* Set the budget to filter on.
*
* @param Budget $budget
*
* @return JournalCollectorInterface
*/
public function setBudget(Budget $budget): JournalCollectorInterface;
/**
* Set the budgets to filter on.
*
* @param Collection $budgets
*
* @return JournalCollectorInterface
*/
public function setBudgets(Collection $budgets): JournalCollectorInterface;
/**
* Set the categories to filter on.
*
* @param Collection $categories
*
* @return JournalCollectorInterface
*/
public function setCategories(Collection $categories): JournalCollectorInterface;
/**
* Set the category to filter on.
*
* @param Category $category
*
* @return JournalCollectorInterface
*/
public function setCategory(Category $category): JournalCollectorInterface;
/**
* Set the journals to filter on.
*
* @param Collection $journals
*
* @return JournalCollectorInterface
*/
public function setJournals(Collection $journals): JournalCollectorInterface;
/**
* Set the page limit.
*
* @param int $limit
*
* @return JournalCollectorInterface
*/
public function setLimit(int $limit): JournalCollectorInterface;
/**
* Set the offset.
*
* @param int $offset
*
* @return JournalCollectorInterface
*/
public function setOffset(int $offset): JournalCollectorInterface;
/**
* Set the opposing accounts to collect from.
*
* @param Collection $accounts
*
* @return JournalCollectorInterface
*/
public function setOpposingAccounts(Collection $accounts): JournalCollectorInterface;
/**
* Set the page to get.
*
* @param int $page
*
* @return JournalCollectorInterface
*/
public function setPage(int $page): JournalCollectorInterface;
/**
* Set the date range.
*
* @param Carbon $start
* @param Carbon $end
*
* @return JournalCollectorInterface
*/
public function setRange(Carbon $start, Carbon $end): JournalCollectorInterface;
/**
* Set the tag to collect from.
*
* @param Tag $tag
*
* @return JournalCollectorInterface
*/
public function setTag(Tag $tag): JournalCollectorInterface;
/**
* Set the tags to collect from.
*
* @param Collection $tags
*
* @return JournalCollectorInterface
*/
public function setTags(Collection $tags): JournalCollectorInterface;
/**
* Set the types to collect.
*
* @param array $types
*
* @return JournalCollectorInterface
*/
public function setTypes(array $types): JournalCollectorInterface;
/**
* Set the user.
*
* @param User $user
*
* @return mixed
*/
public function setUser(User $user);
/**
* Start the query.
*/
public function startQuery();
/**
* Include budget information.
*
* @return JournalCollectorInterface
*/
public function withBudgetInformation(): JournalCollectorInterface;
/**
* Include category information.
*
* @return JournalCollectorInterface
*/
public function withCategoryInformation(): JournalCollectorInterface;
/**
* Include opposing account information.
*
* @return JournalCollectorInterface
*/
public function withOpposingAccount(): JournalCollectorInterface;
/**
* Include tranactions without a budget.
*
* @return JournalCollectorInterface
*/
public function withoutBudget(): JournalCollectorInterface;
/**
* Include tranactions without a category.
*
* @return JournalCollectorInterface
*/
public function withoutCategory(): JournalCollectorInterface;
}

View File

@@ -223,7 +223,7 @@ class TransactionCollector implements TransactionCollectorInterface
public function count(): int
{
if (true === $this->run) {
throw new FireflyException('Cannot count after run in JournalCollector.');
throw new FireflyException('Cannot count after run in TransactionCollector.');
}
$countQuery = clone $this->query;
@@ -240,13 +240,10 @@ class TransactionCollector implements TransactionCollectorInterface
return $this->count;
}
/** @noinspection MultipleReturnStatementsInspection */
/**
* @return Collection
* @SuppressWarnings(PHPMD.CyclomaticComplexity)
* @SuppressWarnings(PHPMD.ExcessiveMethodLength)
*/
public function getJournals(): Collection
public function getTransactions(): Collection
{
$this->run = true;
@@ -301,16 +298,15 @@ class TransactionCollector implements TransactionCollectorInterface
/**
* @return LengthAwarePaginator
*
* @throws FireflyException
*/
public function getPaginatedJournals(): LengthAwarePaginator
public function getPaginatedTransactions(): LengthAwarePaginator
{
if (true === $this->run) {
throw new FireflyException('Cannot getPaginatedJournals after run in JournalCollector.');
throw new FireflyException('Cannot getPaginatedTransactions after run in TransactionCollector.');
}
$this->count();
$set = $this->getJournals();
$set = $this->getTransactions();
$journals = new LengthAwarePaginator($set, $this->count, $this->limit, $this->page);
return $journals;
@@ -380,7 +376,7 @@ class TransactionCollector implements TransactionCollectorInterface
{
$afterStr = $after->format('Y-m-d 00:00:00');
$this->query->where('transaction_journals.date', '>=', $afterStr);
Log::debug(sprintf('JournalCollector range is now after %s (inclusive)', $afterStr));
Log::debug(sprintf('TransactionCollector range is now after %s (inclusive)', $afterStr));
return $this;
}
@@ -416,7 +412,7 @@ class TransactionCollector implements TransactionCollectorInterface
{
$beforeStr = $before->format('Y-m-d 00:00:00');
$this->query->where('transaction_journals.date', '<=', $beforeStr);
Log::debug(sprintf('JournalCollector range is now before %s (inclusive)', $beforeStr));
Log::debug(sprintf('TransactionCollector range is now before %s (inclusive)', $beforeStr));
return $this;
}
@@ -617,7 +613,7 @@ class TransactionCollector implements TransactionCollectorInterface
$endStr = $end->format('Y-m-d 23:59:59');
$this->query->where('transaction_journals.date', '>=', $startStr);
$this->query->where('transaction_journals.date', '<=', $endStr);
Log::debug(sprintf('JournalCollector range is now %s - %s (inclusive)', $startStr, $endStr));
Log::debug(sprintf('TransactionCollector range is now %s - %s (inclusive)', $startStr, $endStr));
}
return $this;
@@ -680,7 +676,7 @@ class TransactionCollector implements TransactionCollectorInterface
*/
public function startQuery(): void
{
Log::debug('journalCollector::startQuery');
Log::debug('TransactionCollector::startQuery');
/** @var EloquentBuilder $query */
$query = Transaction::leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id')
->leftJoin('transaction_types', 'transaction_types.id', 'transaction_journals.transaction_type_id')

View File

@@ -83,18 +83,18 @@ interface TransactionCollectorInterface
public function count(): int;
/**
* Get all journals.
* Get all transactions.
*
* @return Collection
*/
public function getJournals(): Collection;
public function getTransactions(): Collection;
/**
* Get a paginated result.
*
* @return LengthAwarePaginator
*/
public function getPaginatedJournals(): LengthAwarePaginator;
public function getPaginatedTransactions(): LengthAwarePaginator;
/**
* Get the query.
@@ -129,7 +129,7 @@ interface TransactionCollectorInterface
public function setAccounts(Collection $accounts): TransactionCollectorInterface;
/**
* Collect journals after a specific date.
* Collect transactions after a specific date.
*
* @param Carbon $after
*
@@ -145,7 +145,7 @@ interface TransactionCollectorInterface
public function setAllAssetAccounts(): TransactionCollectorInterface;
/**
* Collect journals before a specific date.
* Collect transactions before a specific date.
*
* @param Carbon $before
*

View File

@@ -22,7 +22,7 @@ declare(strict_types=1);
namespace FireflyIII\Helpers\Report;
use FireflyIII\Helpers\Collector\JournalCollectorInterface;
use FireflyIII\Helpers\Collector\TransactionCollectorInterface;
use FireflyIII\Models\Account;
use FireflyIII\Models\Budget;
use FireflyIII\Models\Category;
@@ -47,11 +47,11 @@ class PopupReport implements PopupReportInterface
*/
public function balanceForBudget(Budget $budget, Account $account, array $attributes): Collection
{
/** @var JournalCollectorInterface $collector */
$collector = app(JournalCollectorInterface::class);
/** @var TransactionCollectorInterface $collector */
$collector = app(TransactionCollectorInterface::class);
$collector->setAccounts(new Collection([$account]))->setRange($attributes['startDate'], $attributes['endDate'])->setBudget($budget);
return $collector->getJournals();
return $collector->getTransactions();
}
/**
@@ -64,15 +64,15 @@ class PopupReport implements PopupReportInterface
*/
public function balanceForNoBudget(Account $account, array $attributes): Collection
{
/** @var JournalCollectorInterface $collector */
$collector = app(JournalCollectorInterface::class);
/** @var TransactionCollectorInterface $collector */
$collector = app(TransactionCollectorInterface::class);
$collector
->setAccounts(new Collection([$account]))
->setTypes([TransactionType::WITHDRAWAL])
->setRange($attributes['startDate'], $attributes['endDate'])
->withoutBudget();
return $collector->getJournals();
return $collector->getTransactions();
}
/**
@@ -85,8 +85,8 @@ class PopupReport implements PopupReportInterface
*/
public function byBudget(Budget $budget, array $attributes): Collection
{
/** @var JournalCollectorInterface $collector */
$collector = app(JournalCollectorInterface::class);
/** @var TransactionCollectorInterface $collector */
$collector = app(TransactionCollectorInterface::class);
$collector->setAccounts($attributes['accounts'])->setRange($attributes['startDate'], $attributes['endDate']);
@@ -97,7 +97,7 @@ class PopupReport implements PopupReportInterface
$collector->setBudget($budget);
}
return $collector->getJournals();
return $collector->getTransactions();
}
/**
@@ -110,13 +110,13 @@ class PopupReport implements PopupReportInterface
*/
public function byCategory(Category $category, array $attributes): Collection
{
/** @var JournalCollectorInterface $collector */
$collector = app(JournalCollectorInterface::class);
/** @var TransactionCollectorInterface $collector */
$collector = app(TransactionCollectorInterface::class);
$collector->setAccounts($attributes['accounts'])->setTypes([TransactionType::WITHDRAWAL, TransactionType::TRANSFER])
->setRange($attributes['startDate'], $attributes['endDate'])->withOpposingAccount()
->setCategory($category);
return $collector->getJournals();
return $collector->getTransactions();
}
/**
@@ -133,12 +133,12 @@ class PopupReport implements PopupReportInterface
$repository = app(JournalRepositoryInterface::class);
$repository->setUser($account->user);
/** @var JournalCollectorInterface $collector */
$collector = app(JournalCollectorInterface::class);
/** @var TransactionCollectorInterface $collector */
$collector = app(TransactionCollectorInterface::class);
$collector->setAccounts(new Collection([$account]))->setRange($attributes['startDate'], $attributes['endDate'])
->setTypes([TransactionType::WITHDRAWAL, TransactionType::TRANSFER]);
$journals = $collector->getJournals();
$journals = $collector->getTransactions();
$report = $attributes['accounts']->pluck('id')->toArray(); // accounts used in this report
@@ -169,11 +169,11 @@ class PopupReport implements PopupReportInterface
/** @var JournalRepositoryInterface $repository */
$repository = app(JournalRepositoryInterface::class);
$repository->setUser($account->user);
/** @var JournalCollectorInterface $collector */
$collector = app(JournalCollectorInterface::class);
/** @var TransactionCollectorInterface $collector */
$collector = app(TransactionCollectorInterface::class);
$collector->setAccounts(new Collection([$account]))->setRange($attributes['startDate'], $attributes['endDate'])
->setTypes([TransactionType::DEPOSIT, TransactionType::TRANSFER]);
$journals = $collector->getJournals();
$journals = $collector->getTransactions();
$report = $attributes['accounts']->pluck('id')->toArray(); // accounts used in this report
// filter the set so the destinations outside of $attributes['accounts'] are not included.

View File

@@ -25,7 +25,7 @@ namespace FireflyIII\Helpers\Report;
use Carbon\Carbon;
use FireflyIII\Helpers\Collection\Bill as BillCollection;
use FireflyIII\Helpers\Collection\BillLine;
use FireflyIII\Helpers\Collector\JournalCollectorInterface;
use FireflyIII\Helpers\Collector\TransactionCollectorInterface;
use FireflyIII\Helpers\FiscalHelperInterface;
use FireflyIII\Models\Bill;
use FireflyIII\Models\Transaction;
@@ -83,10 +83,10 @@ class ReportHelper implements ReportHelperInterface
foreach ($expectedDates as $payDate) {
$endOfPayPeriod = app('navigation')->endOfX($payDate, $bill->repeat_freq, null);
/** @var JournalCollectorInterface $collector */
$collector = app(JournalCollectorInterface::class);
/** @var TransactionCollectorInterface $collector */
$collector = app(TransactionCollectorInterface::class);
$collector->setAccounts($accounts)->setRange($payDate, $endOfPayPeriod)->setBills($bills);
$journals = $collector->getJournals();
$journals = $collector->getTransactions();
$billLine = new BillLine;
$billLine->setBill($bill);