Files
firefly-iii/app/lib/Firefly/Storage/TransactionJournal/EloquentTransactionJournalRepository.php

618 lines
21 KiB
PHP
Raw Normal View History

<?php
namespace Firefly\Storage\TransactionJournal;
use Carbon\Carbon;
use Firefly\Exception\FireflyException;
use Illuminate\Queue\Jobs\Job;
/**
* Class EloquentTransactionJournalRepository
*
* @package Firefly\Storage\TransactionJournal
*/
class EloquentTransactionJournalRepository implements TransactionJournalRepositoryInterface
{
2014-09-02 17:27:28 +02:00
protected $_user = null;
/**
*
*/
public function __construct()
{
$this->_user = \Auth::user();
}
/**
* @param Job $job
* @param array $payload
*
* @return mixed
*/
public function importTransfer(Job $job, array $payload)
{
/** @var \Firefly\Storage\Import\ImportRepositoryInterface $repository */
$repository = \App::make('Firefly\Storage\Import\ImportRepositoryInterface');
/** @var \Importmap $importMap */
$importMap = $repository->findImportmap($payload['mapID']);
$user = $importMap->user;
$this->overruleUser($user);
if ($job->attempts() > 10) {
\Log::error('Never found accounts for transfer "' . $payload['data']['description'] . '". KILL!');
$importMap->jobsdone++;
$importMap->save();
$job->delete(); // count fixed
return;
}
/** @var \Firefly\Storage\Account\AccountRepositoryInterface $accounts */
$accounts = \App::make('Firefly\Storage\Account\AccountRepositoryInterface');
$accounts->overruleUser($user);
/** @var \Firefly\Helper\Controllers\TransactionInterface $transactions */
$transactions = \App::make('Firefly\Helper\Controllers\TransactionInterface');
$transactions->overruleUser($this->_user);
/*
* Prep some variables from the payload:
*/
$fromAccountId = intval($payload['data']['accountfrom_id']);
$toAccountId = intval($payload['data']['accountto_id']);
$description = $payload['data']['description'];
$transferId = intval($payload['data']['id']);
$amount = floatval($payload['data']['amount']);
$date = new Carbon($payload['data']['date']);
/*
* maybe Journal is already imported:
*/
$importEntry = $repository->findImportEntry($importMap, 'Transfer', $transferId);
/*
* if so, delete job and return:
*/
if (!is_null($importEntry)) {
\Log::debug('Already imported transfer ' . $description);
$importMap->jobsdone++;
$importMap->save();
$job->delete(); // count fixed
return;
}
/*
* Find the 'from' account:
*/
$oldFromAccountEntry = $repository->findImportEntry($importMap, 'Account', $fromAccountId);
$accountFrom = $accounts->find($oldFromAccountEntry->new);
/*
* Find the 'to' account:
*/
$oldToAccountEntry = $repository->findImportEntry($importMap, 'Account', $toAccountId);
$accountTo = $accounts->find($oldToAccountEntry->new);
/*
* If either is NULL, wait a bit and then reschedule.
*/
if (is_null($accountTo) || is_null($accountFrom)) {
\Log::notice('No account to, or account from. Release transfer ' . $description);
if (\Config::get('queue.default') == 'sync') {
$importMap->jobsdone++;
$importMap->save();
$job->delete(); // count fixed
} else {
$job->release(300); // proper release.
}
return;
}
/*
* Import transfer:
*/
$set = [
'account_from_id' => $accountFrom->id,
'account_to_id' => $accountTo->id,
'amount' => $amount,
'description' => $description,
'date' => $date->format('Y-m-d'),
'category' => '',
'what' => 'transfer',
'return_journal' => true
];
$journal = $transactions->store($set);
$repository->store($importMap, 'Transfer', $transferId, $journal->id);
\Log::debug('Imported transfer "' . $description . '" (' . $amount . ') (' . $date->format('Y-m-d') . ')');
// update map:
$importMap->jobsdone++;
$importMap->save();
$job->delete(); // count fixed.
}
/**
* @param \User $user
*
* @return mixed|void
*/
public function overruleUser(\User $user)
{
$this->_user = $user;
return true;
}
public function store(array $data)
{
/*
* Create the journal and fill relevant fields.
*/
$journal = new \TransactionJournal;
$journal->description = trim($data['description']);
$journal->date = new Carbon($data['date']);
$journal->user_id = $this->_user->id;
$journal->completed = false;
/*
* Find the more complex fields and fill those:
*/
$currency = \TransactionCurrency::where('code', 'EUR')->first();
$journal->transaction_currency_id = $currency->id;
$transactionType = \TransactionType::where('type', $data['what'])->first();
$journal->transaction_type_id = $transactionType->id;
/*
* Validatre & save journal
*/
$journal->validate();
$journal->save();
/*
* Return regardless.
*/
return $journal;
}
/**
* @param \TransactionJournal $journal
* @param \Account $account
* @param $amount
*
* @return \Transaction|null
*/
public function saveTransaction(\TransactionJournal $journal, \Account $account, $amount)
{
$transaction = new \Transaction;
$transaction->account_id = $account->id;
$transaction->transaction_journal_id = $journal->id;
$transaction->amount = $amount;
if ($transaction->validate()) {
$transaction->save();
}
return $transaction;
}
/**
* @param Job $job
* @param array $payload
*
* @return mixed
*/
public function importTransaction(Job $job, array $payload)
{
/** @var \Firefly\Storage\Import\ImportRepositoryInterface $repository */
$repository = \App::make('Firefly\Storage\Import\ImportRepositoryInterface');
/** @var \Importmap $importMap */
$importMap = $repository->findImportmap($payload['mapID']);
$user = $importMap->user;
$this->overruleUser($user);
if ($job->attempts() > 10) {
\Log::error('Never found asset account for transaction "' . $payload['data']['description'] . '". KILL!');
$importMap->jobsdone++;
$importMap->save();
$job->delete(); // count fixed
return;
}
/** @var \Firefly\Storage\Account\AccountRepositoryInterface $accounts */
$accounts = \App::make('Firefly\Storage\Account\AccountRepositoryInterface');
$accounts->overruleUser($user);
/** @var \Firefly\Helper\Controllers\TransactionInterface $transactions */
$transactions = \App::make('Firefly\Helper\Controllers\TransactionInterface');
$transactions->overruleUser($this->_user);
/*
* Prep some vars coming out of the pay load:
*/
$amount = floatval($payload['data']['amount']);
$date = new Carbon($payload['data']['date']);
$description = $payload['data']['description'];
$transactionId = intval($payload['data']['id']);
$accountId = intval($payload['data']['account_id']);
/*
* maybe Journal is already imported:
*/
$importEntry = $repository->findImportEntry($importMap, 'Transaction', $transactionId);
/*
* if so, delete job and return:
*/
if (!is_null($importEntry)) {
\Log::debug('Already imported transaction ' . $description);
$importMap->jobsdone++;
$importMap->save();
$job->delete(); // count fixed
return;
}
/*
* Find or create the "import account" which is used because at this point, Firefly
* doesn't know which beneficiary (expense account) should be connected to this transaction.
*/
$accountType = $accounts->findAccountType('Import account');
$importAccount = $accounts->firstOrCreate(
[
'account_type_id' => $accountType->id,
'name' => 'Import account',
'user_id' => $user->id,
'active' => 1,
]
);
unset($accountType);
/*
* Find the asset account this transaction is paid from / paid to:
*/
$accountEntry = $repository->findImportEntry($importMap, 'Account', $accountId);
$assetAccount = $accounts->find($accountEntry->new);
unset($accountEntry);
/*
* If $assetAccount is null, we release this job and try later.
*/
if (is_null($assetAccount)) {
\Log::notice('No asset account for "' . $description . '", try again later.');
if (\Config::get('queue.default') == 'sync') {
$importMap->jobsdone++;
$importMap->save();
$job->delete(); // count fixed
} else {
$job->release(300); // proper release.
}
return;
}
/*
* Prep some data for the import routine:
*/
$set = [
'category' => '',
'description' => $description,
'date' => $date->format('Y-m-d'),
'return_journal' => true
];
/*
* If the amount is less than zero, we move money to the $importAccount. Otherwise,
* we move it from the $importAccount.
*/
if ($amount < 0) {
// if amount is less than zero, move to $importAccount
$accountFrom = $assetAccount;
$accountTo = $importAccount;
$set['what'] = 'withdrawal';
$set['account_id'] = $accountFrom->id;
$set['expense_account'] = $importAccount->name;
} else {
$accountFrom = $importAccount;
$accountTo = $assetAccount;
$set['what'] = 'deposit';
$set['account_id'] = $accountTo->id;
$set['revenue_account'] = $accountFrom->name;
}
/*
* Modify the amount so it will work with or new transaction journal structure.
*/
$set['amount'] = $amount < 0 ? $amount * -1 : $amount;
/*
* Import it:
*/
$journal = $transactions->store($set);
$repository->store($importMap, 'Transaction', $transactionId, $journal->id);
\Log::debug('Imported transaction "' . $description . '" (' . $amount . ') (' . $date->format('Y-m-d') . ')');
// update map:
$importMap->jobsdone++;
$importMap->save();
$job->delete(); // count fixed
return;
}
/**
* @param $journalId
*
* @return mixed
*/
public function find($journalId)
{
2014-09-02 17:27:28 +02:00
return $this->_user->transactionjournals()->with(
['transactions' => function ($q) {
return $q->orderBy('amount', 'ASC');
}, 'transactioncurrency', 'transactiontype', 'components', 'transactions.account',
'transactions.account.accounttype']
)
->where('id', $journalId)->first();
}
/**
* @param $type
*
* @return \TransactionType
*/
public function getTransactionType($type)
{
return \TransactionType::whereType($type)->first();
}
/**
* @param \Account $account
* @param Carbon $date
*
* @return mixed
*/
public function getByAccountAndDate(\Account $account, Carbon $date)
2014-07-09 12:56:06 +02:00
{
$accountID = $account->id;
2014-09-02 17:27:28 +02:00
$query = $this->_user->transactionjournals()->with(
[
'transactions',
'transactions.account',
'transactioncurrency',
'transactiontype'
]
)
->distinct()
->leftJoin(
'transactions', 'transactions.transaction_journal_id', '=',
'transaction_journals.id'
)
->leftJoin('accounts', 'accounts.id', '=', 'transactions.account_id')
->where('transactions.account_id', $accountID)
->where('transaction_journals.date', $date->format('Y-m-d'))
->orderBy('transaction_journals.date', 'DESC')
->orderBy('transaction_journals.id', 'DESC')
->get(['transaction_journals.*']);
return $query;
2014-07-09 12:56:06 +02:00
}
/**
* @param \Account $account
* @param int $count
* @param Carbon $start
* @param Carbon $end
*
* @return mixed
*/
public function getByAccountInDateRange(\Account $account, $count = 25, Carbon $start, Carbon $end)
{
$accountID = $account->id;
2014-09-02 17:27:28 +02:00
$query = $this->_user->transactionjournals()->with(
[
'transactions',
'transactioncurrency',
'transactiontype'
]
)
->leftJoin(
'transactions', 'transactions.transaction_journal_id', '=',
'transaction_journals.id'
)
->leftJoin('accounts', 'accounts.id', '=', 'transactions.account_id')
->where('accounts.id', $accountID)
->where('date', '>=', $start->format('Y-m-d'))
->where('date', '<=', $end->format('Y-m-d'))
->orderBy('transaction_journals.date', 'DESC')
->orderBy('transaction_journals.id', 'DESC')
->take($count)
->get(['transaction_journals.*']);
return $query;
}
/**
* @param \TransactionType $type
* @param int $count
* @param Carbon $start
* @param Carbon $end
*
* @return mixed
*/
public function paginate(\TransactionType $type, $count = 25, Carbon $start = null, Carbon $end = null)
{
2014-09-08 10:37:31 +02:00
$query = $this->_user->transactionjournals()->WithRelevantData()
->transactionTypes([$type->type])
->orderBy('transaction_journals.date', 'DESC')
->orderBy('transaction_journals.id', 'DESC');
if (!is_null($start)) {
$query->where('transaction_journals.date', '>=', $start->format('Y-m-d'));
}
if (!is_null($end)) {
$query->where('transaction_journals.date', '<=', $end->format('Y-m-d'));
}
$result = $query->select(['transaction_journals.*'])->paginate($count);
return $result;
}
2014-08-10 15:01:46 +02:00
/**
* @param \TransactionJournal $journal
* @param $data
*
* @return mixed|\TransactionJournal
* @throws \Firefly\Exception\FireflyException
*/
public function update(\TransactionJournal $journal, $data)
{
/** @var \Firefly\Storage\Category\CategoryRepositoryInterface $catRepository */
$catRepository = \App::make('Firefly\Storage\Category\CategoryRepositoryInterface');
2014-09-02 17:27:28 +02:00
$catRepository->overruleUser($this->_user);
/** @var \Firefly\Storage\Budget\BudgetRepositoryInterface $budgetRepository */
$budRepository = \App::make('Firefly\Storage\Budget\BudgetRepositoryInterface');
2014-09-02 17:27:28 +02:00
$budRepository->overruleUser($this->_user);
/** @var \Firefly\Storage\Account\AccountRepositoryInterface $accountRepository */
$accountRepository = \App::make('Firefly\Storage\Account\AccountRepositoryInterface');
2014-09-02 17:27:28 +02:00
$accountRepository->overruleUser($this->_user);
// update basics first:
$journal->description = $data['description'];
2014-09-02 17:27:28 +02:00
$journal->date = $data['date'];
$amount = floatval($data['amount']);
// remove previous category, if any:
if (!is_null($journal->categories()->first())) {
$journal->categories()->detach($journal->categories()->first()->id);
}
// remove previous budget, if any:
if (!is_null($journal->budgets()->first())) {
$journal->budgets()->detach($journal->budgets()->first()->id);
}
// remove previous piggy bank, if any:
$category = isset($data['category']) ? $catRepository->findByName($data['category']) : null;
if (!is_null($category)) {
$journal->categories()->attach($category);
}
// update the amounts:
$transactions = $journal->transactions()->orderBy('amount', 'ASC')->get();
// remove previous piggy bank, if any:
/** @var \Transaction $transaction */
foreach ($transactions as $transaction) {
if (!is_null($transaction->piggybank()->first())) {
2014-08-19 13:24:11 +02:00
$transaction->piggybank_id = null;
$transaction->save();
}
}
unset($transaction);
2014-08-09 06:32:55 +02:00
$transactions[0]->amount = $amount * -1;
$transactions[1]->amount = $amount;
// switch on type to properly change things:
2014-08-19 13:24:11 +02:00
$fireEvent = false;
switch ($journal->transactiontype->type) {
case 'Withdrawal':
// means transaction[0] is the users account.
2014-09-02 17:27:28 +02:00
$account = $accountRepository->find($data['account_id']);
$beneficiary = $accountRepository->createOrFindBeneficiary($data['beneficiary']);
$transactions[0]->account()->associate($account);
$transactions[1]->account()->associate($beneficiary);
// do budget:
$budget = $budRepository->find($data['budget_id']);
2014-08-10 11:30:14 +02:00
if (!is_null($budget)) {
$journal->budgets()->attach($budget);
}
break;
case 'Deposit':
// means transaction[0] is the beneficiary.
2014-09-02 17:27:28 +02:00
$account = $accountRepository->find($data['account_id']);
$beneficiary = $accountRepository->createOrFindBeneficiary($data['beneficiary']);
$journal->transactions[0]->account()->associate($beneficiary);
$journal->transactions[1]->account()->associate($account);
break;
case 'Transfer':
// means transaction[0] is account that sent the money (from).
/** @var \Account $fromAccount */
$fromAccount = $accountRepository->find($data['account_from_id']);
/** @var \Account $toAccount */
$toAccount = $accountRepository->find($data['account_to_id']);
$journal->transactions[0]->account()->associate($fromAccount);
$journal->transactions[1]->account()->associate($toAccount);
// attach the new piggy bank, if valid:
/** @var \Firefly\Storage\Piggybank\PiggybankRepositoryInterface $piggyRepository */
$piggyRepository = \App::make('Firefly\Storage\Piggybank\PiggybankRepositoryInterface');
2014-09-02 17:27:28 +02:00
$piggyRepository->overruleUser($this->_user);
if (isset($data['piggybank_id'])) {
/** @var \Piggybank $piggyBank */
$piggyBank = $piggyRepository->find(intval($data['piggybank_id']));
2014-08-19 13:24:11 +02:00
// loop transactions and re-attach the piggy bank:
if ($piggyBank) {
2014-08-19 13:24:11 +02:00
$connected = false;
foreach ($journal->transactions()->get() as $transaction) {
if ($transaction->account_id == $piggyBank->account_id) {
$connected = true;
$transaction->piggybank()->associate($piggyBank);
$transaction->save();
$fireEvent = true;
break;
}
}
if ($connected === false) {
\Session::flash(
'warning', 'Piggy bank "' . e($piggyBank->name)
. '" is not set to draw money from any of the accounts in this transfer'
);
}
}
}
break;
default:
2014-08-10 15:01:46 +02:00
throw new FireflyException('Cannot edit this!');
break;
}
$transactions[0]->save();
$transactions[1]->save();
if ($journal->validate()) {
$journal->save();
}
if ($fireEvent) {
2014-08-19 13:24:11 +02:00
\Event::fire('piggybanks.updateRelatedTransfer', [$piggyBank]);
}
return $journal;
}
}