2016-10-23 02:44:14 -05:00
|
|
|
<?php
|
|
|
|
/**
|
|
|
|
* JournalCollector.php
|
|
|
|
* Copyright (C) 2016 thegrumpydictator@gmail.com
|
|
|
|
*
|
|
|
|
* This software may be modified and distributed under the terms of the
|
|
|
|
* Creative Commons Attribution-ShareAlike 4.0 International License.
|
|
|
|
*
|
|
|
|
* See the LICENSE file for details.
|
|
|
|
*/
|
|
|
|
|
|
|
|
declare(strict_types = 1);
|
|
|
|
|
|
|
|
namespace FireflyIII\Export\Collector;
|
|
|
|
|
|
|
|
use Carbon\Carbon;
|
|
|
|
use Crypt;
|
|
|
|
use DB;
|
|
|
|
use FireflyIII\Models\Transaction;
|
|
|
|
use Illuminate\Database\Query\JoinClause;
|
|
|
|
use Illuminate\Support\Collection;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Class JournalCollector
|
|
|
|
*
|
|
|
|
* @package FireflyIII\Export\Collector
|
|
|
|
*/
|
|
|
|
class JournalCollector extends BasicCollector implements CollectorInterface
|
|
|
|
{
|
|
|
|
/** @var Collection */
|
|
|
|
private $accounts;
|
|
|
|
/** @var Carbon */
|
|
|
|
private $end;
|
|
|
|
/** @var Carbon */
|
|
|
|
private $start;
|
|
|
|
|
|
|
|
/** @var Collection */
|
|
|
|
private $workSet;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @return bool
|
|
|
|
*/
|
|
|
|
public function run(): bool
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* Instead of collecting journals we collect transactions for the given accounts.
|
|
|
|
* We left join the OPPOSING transaction AND some journal data.
|
|
|
|
* After that we complement this info with budgets, categories, etc.
|
|
|
|
*
|
|
|
|
* This is way more efficient and will also work on split journals.
|
|
|
|
*/
|
|
|
|
$this->getWorkSet();
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Extract:
|
|
|
|
* possible budget ids for journals
|
|
|
|
* possible category ids journals
|
|
|
|
* possible budget ids for transactions
|
|
|
|
* possible category ids for transactions
|
2016-10-23 05:42:44 -05:00
|
|
|
*
|
|
|
|
* possible IBAN and account numbers?
|
|
|
|
*
|
2016-10-23 02:44:14 -05:00
|
|
|
*/
|
|
|
|
$journals = $this->extractJournalIds();
|
|
|
|
$transactions = $this->extractTransactionIds();
|
|
|
|
|
2016-10-23 05:42:44 -05:00
|
|
|
|
2016-10-23 02:44:14 -05:00
|
|
|
// extend work set with category data from journals:
|
|
|
|
$this->categoryDataForJournals($journals);
|
|
|
|
|
|
|
|
// extend work set with category cate from transactions (overrules journals):
|
|
|
|
$this->categoryDataForTransactions($transactions);
|
|
|
|
|
|
|
|
// same for budgets:
|
|
|
|
$this->budgetDataForJournals($journals);
|
|
|
|
$this->budgetDataForTransactions($transactions);
|
|
|
|
|
|
|
|
$this->setEntries($this->workSet);
|
2016-10-23 02:57:04 -05:00
|
|
|
|
2016-10-23 02:44:14 -05:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @param Collection $accounts
|
|
|
|
*/
|
|
|
|
public function setAccounts(Collection $accounts)
|
|
|
|
{
|
|
|
|
$this->accounts = $accounts;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @param Carbon $start
|
|
|
|
* @param Carbon $end
|
|
|
|
*/
|
|
|
|
public function setDates(Carbon $start, Carbon $end)
|
|
|
|
{
|
|
|
|
$this->start = $start;
|
|
|
|
$this->end = $end;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @param array $journals
|
|
|
|
*
|
|
|
|
* @return bool
|
|
|
|
*/
|
|
|
|
private function budgetDataForJournals(array $journals): bool
|
|
|
|
{
|
|
|
|
$set = DB::table('budget_transaction_journal')
|
|
|
|
->leftJoin('budgets', 'budgets.id', '=', 'budget_transaction_journal.budget_id')
|
|
|
|
->whereIn('budget_transaction_journal.transaction_journal_id', $journals)
|
|
|
|
->get(
|
|
|
|
[
|
|
|
|
'budget_transaction_journal.budget_id',
|
|
|
|
'budget_transaction_journal.transaction_journal_id',
|
|
|
|
'budgets.name',
|
|
|
|
'budgets.encrypted',
|
|
|
|
]
|
|
|
|
);
|
|
|
|
$set->each(
|
|
|
|
function ($obj) {
|
|
|
|
$obj->name = $obj->encrypted === 1 ? Crypt::decrypt($obj->name) : $obj->name;
|
|
|
|
}
|
|
|
|
);
|
|
|
|
$array = [];
|
|
|
|
foreach ($set as $obj) {
|
|
|
|
$array[$obj->transaction_journal_id] = ['id' => $obj->budget_id, 'name' => $obj->name];
|
|
|
|
}
|
|
|
|
|
|
|
|
$this->workSet->each(
|
|
|
|
function ($obj) use ($array) {
|
|
|
|
if (isset($array[$obj->transaction_journal_id])) {
|
|
|
|
$obj->budget_id = $array[$obj->transaction_journal_id]['id'];
|
|
|
|
$obj->budget_name = $array[$obj->transaction_journal_id]['name'];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
);
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @param array $transactions
|
|
|
|
*
|
|
|
|
* @return bool
|
|
|
|
*/
|
|
|
|
private function budgetDataForTransactions(array $transactions): bool
|
|
|
|
{
|
|
|
|
$set = DB::table('budget_transaction')
|
|
|
|
->leftJoin('budgets', 'budgets.id', '=', 'budget_transaction.budget_id')
|
|
|
|
->whereIn('budget_transaction.transaction_id', $transactions)
|
|
|
|
->get(
|
|
|
|
[
|
|
|
|
'budget_transaction.budget_id',
|
|
|
|
'budget_transaction.transaction_id',
|
|
|
|
'budgets.name',
|
|
|
|
'budgets.encrypted',
|
|
|
|
]
|
|
|
|
);
|
|
|
|
$set->each(
|
|
|
|
function ($obj) {
|
|
|
|
$obj->name = $obj->encrypted === 1 ? Crypt::decrypt($obj->name) : $obj->name;
|
|
|
|
}
|
|
|
|
);
|
|
|
|
$array = [];
|
|
|
|
foreach ($set as $obj) {
|
|
|
|
$array[$obj->transaction_id] = ['id' => $obj->budget_id, 'name' => $obj->name];
|
|
|
|
}
|
|
|
|
|
|
|
|
$this->workSet->each(
|
|
|
|
function ($obj) use ($array) {
|
|
|
|
|
|
|
|
// first transaction
|
|
|
|
if (isset($array[$obj->id])) {
|
|
|
|
$obj->budget_id = $array[$obj->id]['id'];
|
|
|
|
$obj->budget_name = $array[$obj->id]['name'];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
);
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @param array $journals
|
|
|
|
*
|
|
|
|
* @return bool
|
|
|
|
*/
|
|
|
|
private function categoryDataForJournals(array $journals): bool
|
|
|
|
{
|
|
|
|
$set = DB::table('category_transaction_journal')
|
|
|
|
->leftJoin('categories', 'categories.id', '=', 'category_transaction_journal.category_id')
|
|
|
|
->whereIn('category_transaction_journal.transaction_journal_id', $journals)
|
|
|
|
->get(
|
|
|
|
[
|
|
|
|
'category_transaction_journal.category_id',
|
|
|
|
'category_transaction_journal.transaction_journal_id',
|
|
|
|
'categories.name',
|
|
|
|
'categories.encrypted',
|
|
|
|
]
|
|
|
|
);
|
|
|
|
$set->each(
|
|
|
|
function ($obj) {
|
|
|
|
$obj->name = $obj->encrypted === 1 ? Crypt::decrypt($obj->name) : $obj->name;
|
|
|
|
}
|
|
|
|
);
|
|
|
|
$array = [];
|
|
|
|
foreach ($set as $obj) {
|
|
|
|
$array[$obj->transaction_journal_id] = ['id' => $obj->category_id, 'name' => $obj->name];
|
|
|
|
}
|
|
|
|
|
|
|
|
$this->workSet->each(
|
|
|
|
function ($obj) use ($array) {
|
|
|
|
if (isset($array[$obj->transaction_journal_id])) {
|
|
|
|
$obj->category_id = $array[$obj->transaction_journal_id]['id'];
|
|
|
|
$obj->category_name = $array[$obj->transaction_journal_id]['name'];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
);
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @param array $transactions
|
|
|
|
*
|
|
|
|
* @return bool
|
|
|
|
*/
|
|
|
|
private function categoryDataForTransactions(array $transactions): bool
|
|
|
|
{
|
|
|
|
$set = DB::table('category_transaction')
|
|
|
|
->leftJoin('categories', 'categories.id', '=', 'category_transaction.category_id')
|
|
|
|
->whereIn('category_transaction.transaction_id', $transactions)
|
|
|
|
->get(
|
|
|
|
[
|
|
|
|
'category_transaction.category_id',
|
|
|
|
'category_transaction.transaction_id',
|
|
|
|
'categories.name',
|
|
|
|
'categories.encrypted',
|
|
|
|
]
|
|
|
|
);
|
|
|
|
$set->each(
|
|
|
|
function ($obj) {
|
|
|
|
$obj->name = $obj->encrypted === 1 ? Crypt::decrypt($obj->name) : $obj->name;
|
|
|
|
}
|
|
|
|
);
|
|
|
|
$array = [];
|
|
|
|
foreach ($set as $obj) {
|
|
|
|
$array[$obj->transaction_id] = ['id' => $obj->category_id, 'name' => $obj->name];
|
|
|
|
}
|
|
|
|
|
|
|
|
$this->workSet->each(
|
|
|
|
function ($obj) use ($array) {
|
|
|
|
|
|
|
|
// first transaction
|
|
|
|
if (isset($array[$obj->id])) {
|
|
|
|
$obj->category_id = $array[$obj->id]['id'];
|
|
|
|
$obj->category_name = $array[$obj->id]['name'];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
);
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @return array
|
|
|
|
*/
|
|
|
|
private function extractJournalIds(): array
|
|
|
|
{
|
|
|
|
return $this->workSet->pluck('transaction_journal_id')->toArray();
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @return array
|
|
|
|
*/
|
|
|
|
private function extractTransactionIds()
|
|
|
|
{
|
|
|
|
$set = $this->workSet->pluck('id')->toArray();
|
|
|
|
$opposing = $this->workSet->pluck('opposing_id')->toArray();
|
|
|
|
$complete = $set + $opposing;
|
|
|
|
|
|
|
|
return array_unique($complete);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
private function getWorkSet()
|
|
|
|
{
|
|
|
|
$accountIds = $this->accounts->pluck('id')->toArray();
|
|
|
|
$this->workSet = Transaction
|
|
|
|
::leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id')
|
|
|
|
->leftJoin(
|
|
|
|
'transactions AS opposing', function (JoinClause $join) {
|
|
|
|
$join->on('opposing.transaction_journal_id', '=', 'transactions.transaction_journal_id')
|
|
|
|
->where('opposing.amount', '=', DB::raw('transactions.amount * -1'))
|
|
|
|
->where('transactions.identifier', '=', 'opposing.identifier');
|
|
|
|
}
|
|
|
|
)
|
|
|
|
->leftJoin('accounts', 'transactions.account_id', '=', 'accounts.id')
|
|
|
|
->leftJoin('accounts AS opposing_accounts', 'opposing.account_id', '=', 'opposing_accounts.id')
|
|
|
|
->leftJoin('transaction_types', 'transaction_journals.transaction_type_id', 'transaction_types.id')
|
|
|
|
->leftJoin('transaction_currencies', 'transaction_journals.transaction_currency_id', '=', 'transaction_currencies.id')
|
|
|
|
->whereIn('transactions.account_id', $accountIds)
|
|
|
|
->where('transaction_journals.user_id', $this->job->user_id)
|
|
|
|
->where('transaction_journals.date', '>=', $this->start->format('Y-m-d'))
|
|
|
|
->where('transaction_journals.date', '<=', $this->end->format('Y-m-d'))
|
|
|
|
->where('transaction_journals.completed', 1)
|
|
|
|
->whereNull('transaction_journals.deleted_at')
|
|
|
|
->whereNull('transactions.deleted_at')
|
|
|
|
->whereNull('opposing.deleted_at')
|
|
|
|
->orderBy('transaction_journals.date', 'DESC')
|
|
|
|
->orderBy('transactions.identifier', 'ASC')
|
|
|
|
->get(
|
|
|
|
[
|
|
|
|
'transactions.id',
|
|
|
|
'transactions.amount',
|
|
|
|
'transactions.description',
|
|
|
|
'transactions.account_id',
|
|
|
|
'accounts.name as account_name',
|
|
|
|
'accounts.encrypted as account_name_encrypted',
|
|
|
|
'transactions.identifier',
|
|
|
|
|
|
|
|
'opposing.id as opposing_id',
|
|
|
|
'opposing.amount AS opposing_amount',
|
|
|
|
'opposing.description as opposing_description',
|
|
|
|
'opposing.account_id as opposing_account_id',
|
|
|
|
'opposing_accounts.name as opposing_account_name',
|
|
|
|
'opposing_accounts.encrypted as opposing_account_encrypted',
|
|
|
|
'opposing.identifier as opposing_identifier',
|
|
|
|
|
|
|
|
'transaction_journals.id as transaction_journal_id',
|
|
|
|
'transaction_journals.date',
|
|
|
|
'transaction_journals.description as journal_description',
|
|
|
|
'transaction_journals.encrypted as journal_encrypted',
|
|
|
|
'transaction_journals.transaction_type_id',
|
|
|
|
'transaction_types.type as transaction_type',
|
|
|
|
'transaction_journals.transaction_currency_id',
|
|
|
|
'transaction_currencies.code AS transaction_currency_code',
|
|
|
|
|
|
|
|
]
|
|
|
|
);
|
|
|
|
}
|
2016-10-23 05:42:44 -05:00
|
|
|
}
|