More code for the split journal support.

This commit is contained in:
James Cole 2016-05-17 15:19:07 +02:00
parent e113736887
commit 495b80f5ef
13 changed files with 612 additions and 11 deletions

View File

@ -9,6 +9,7 @@
namespace FireflyIII\Crud\Split;
use FireflyIII\Events\TransactionStored;
use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Models\Account;
use FireflyIII\Models\AccountType;
@ -132,6 +133,13 @@ class Journal implements JournalInterface
$two->budgets()->save($budget);
}
if ($transaction['piggy_bank_id'] > 0) {
// add some extra meta information to the transaction data
$transaction['transaction_journal_id'] = $journal->id;
$transaction['date'] = $journal->date->format('Y-m-d');
event(new TransactionStored($transaction));
}
return new Collection([$one, $two]);
}

View File

@ -0,0 +1,46 @@
<?php
/**
* TransactionStored.php
* Copyright (C) 2016 thegrumpydictator@gmail.com
*
* This software may be modified and distributed under the terms
* of the MIT license. See the LICENSE file for details.
*/
declare(strict_types = 1);
/**
* TransactionStored.php
* Copyright (C) 2016 thegrumpydictator@gmail.com
*
* This software may be modified and distributed under the terms
* of the MIT license. See the LICENSE file for details.
*/
namespace FireflyIII\Events;
use Illuminate\Queue\SerializesModels;
/**
* Class TransactionJournalStored
*
* @package FireflyIII\Events
*/
class TransactionStored extends Event
{
use SerializesModels;
public $transaction = [];
/**
* Create a new event instance.
*
* @param array $transaction
*/
public function __construct(array $transaction)
{
//
$this->transaction = $transaction;
}
}

View File

@ -11,6 +11,7 @@ declare(strict_types = 1);
namespace FireflyIII\Export\Entry;
use FireflyIII\Models\TransactionJournal;
use Illuminate\Support\Collection;
/**
* To extend the exported object, in case of new features in Firefly III for example,
@ -45,8 +46,21 @@ class Entry
public $description;
/** @var EntryAccount */
public $destinationAccount;
/** @var Collection */
public $destinationAccounts;
/** @var EntryAccount */
public $sourceAccount;
/** @var Collection */
public $sourceAccounts;
/**
* Entry constructor.
*/
private function __construct()
{
$this->sourceAccounts = new Collection;
$this->destinationAccounts = new Collection;
}
/**
* @param TransactionJournal $journal
@ -66,10 +80,18 @@ class Entry
$entry->bill = new EntryBill($journal->bill);
$sources = TransactionJournal::sourceAccountList($journal);
$entry->sourceAccount = new EntryAccount($sources->first());
$destinations = TransactionJournal::destinationAccountList($journal);
$entry->sourceAccount = new EntryAccount($sources->first());
$entry->destinationAccount = new EntryAccount($destinations->first());
foreach ($sources as $source) {
$entry->sourceAccounts->push(new EntryAccount($source));
}
foreach ($destinations as $destination) {
$entry->destinationAccounts->push(new EntryAccount($destination));
}
return $entry;
}

View File

@ -11,6 +11,7 @@ declare(strict_types = 1);
namespace FireflyIII\Export\Exporter;
use FireflyIII\Export\Entry\Entry;
use FireflyIII\Export\Entry\EntryAccount;
use FireflyIII\Models\ExportJob;
use League\Csv\Writer;
use SplFileObject;
@ -61,19 +62,84 @@ class CsvExporter extends BasicExporter implements ExporterInterface
// all rows:
$rows = [];
/*
* Count the maximum number of sources and destinations
* each entry has. May need to expand the number of export fields:
*/
$maxSourceAccounts = 1;
$maxDestinationAccounts = 1;
/** @var Entry $entry */
foreach ($this->getEntries() as $entry) {
$sources = $entry->sourceAccounts->count();
$destinations = $entry->destinationAccounts->count();
$maxSourceAccounts = max($maxSourceAccounts, $sources);
$maxDestinationAccounts = max($maxDestinationAccounts, $destinations);
}
// add header:
$rows[] = array_keys(Entry::getFieldsAndTypes());
$rows[] = array_keys($this->getFieldsAndTypes($maxSourceAccounts, $maxDestinationAccounts));
// then the rest:
/** @var Entry $entry */
foreach ($this->getEntries() as $entry) {
// order is defined in Entry::getFieldsAndTypes.
$rows[] = [
$entry->description, $entry->amount, $entry->date, $entry->sourceAccount->accountId, $entry->sourceAccount->name, $entry->sourceAccount->iban,
$entry->sourceAccount->type, $entry->sourceAccount->number, $entry->destinationAccount->accountId, $entry->destinationAccount->name,
$entry->destinationAccount->iban, $entry->destinationAccount->type, $entry->destinationAccount->number, $entry->budget->budgetId,
$entry->budget->name, $entry->category->categoryId, $entry->category->name, $entry->bill->billId, $entry->bill->name,
];
$current = [
$entry->description,
$entry->amount,
$entry->date];
for ($i = 0; $i < $maxSourceAccounts; $i++) {
/** @var EntryAccount $source */
$source = $entry->sourceAccounts->get($i);
$currentId = '';
$currentName = '';
$currentIban = '';
$currentType = '';
$currentNumber = '';
if ($source) {
$currentId = $source->accountId;
$currentName = $source->name;
$currentIban = $source->iban;
$currentType = $source->type;
$currentNumber = $source->number;
}
$current[] = $currentId;
$current[] = $currentName;
$current[] = $currentIban;
$current[] = $currentType;
$current[] = $currentNumber;
}
unset($source);
for ($i = 0; $i < $maxDestinationAccounts; $i++) {
/** @var EntryAccount $destination */
$destination = $entry->destinationAccounts->get($i);
$currentId = '';
$currentName = '';
$currentIban = '';
$currentType = '';
$currentNumber = '';
if ($destination) {
$currentId = $destination->accountId;
$currentName = $destination->name;
$currentIban = $destination->iban;
$currentType = $destination->type;
$currentNumber = $destination->number;
}
$current[] = $currentId;
$current[] = $currentName;
$current[] = $currentIban;
$current[] = $currentType;
$current[] = $currentNumber;
}
unset($destination);
$current[] = $entry->budget->budgetId;
$current[] = $entry->budget->name;
$current[] = $entry->category->categoryId;
$current[] = $entry->category->name;
$current[] = $entry->bill->billId;
$current[] = $entry->bill->name;
$rows[] = $current;
}
$writer->insertAll($rows);
@ -81,6 +147,43 @@ class CsvExporter extends BasicExporter implements ExporterInterface
return true;
}
/**
* @return array
*/
private function getFieldsAndTypes(int $sources, int $destinations): array
{
// key = field name (see top of class)
// value = field type (see csv.php under 'roles')
$array = [
'description' => 'description',
'amount' => 'amount',
'date' => 'date-transaction',
];
for ($i = 0; $i < $sources; $i++) {
$array['source_account_' . $i . '_id'] = 'account-id';
$array['source_account_' . $i . '_name'] = 'account-name';
$array['source_account_' . $i . '_iban'] = 'account-iban';
$array['source_account_' . $i . '_type'] = '_ignore';
$array['source_account_' . $i . '_number'] = 'account-number';
}
for ($i = 0; $i < $destinations; $i++) {
$array['destination_account_' . $i . '_id'] = 'account-id';
$array['destination_account_' . $i . '_name'] = 'account-name';
$array['destination_account_' . $i . '_iban'] = 'account-iban';
$array['destination_account_' . $i . '_type'] = '_ignore';
$array['destination_account_' . $i . '_number'] = 'account-number';
}
$array['budget_id'] = 'budget-id';
$array['budget_name'] = 'budget-name';
$array['category_id'] = 'category-id';
$array['category_name'] = 'category-name';
$array['bill_id'] = 'bill-id';
$array['bill_name'] = 'bill-name';
return $array;
}
private function tempFile()
{
$this->fileName = $this->job->key . '-records.csv';

View File

@ -0,0 +1,68 @@
<?php
/**
* ConnectTransactionToPiggyBank.php
* Copyright (C) 2016 thegrumpydictator@gmail.com
*
* This software may be modified and distributed under the terms
* of the MIT license. See the LICENSE file for details.
*/
declare(strict_types = 1);
namespace FireflyIII\Handlers\Events;
use FireflyIII\Events\TransactionJournalStored;
use FireflyIII\Events\TransactionStored;
use FireflyIII\Models\PiggyBankEvent;
use FireflyIII\Repositories\PiggyBank\PiggyBankRepositoryInterface;
/**
* Class ConnectTransactionToPiggyBank
*
* @package FireflyIII\Handlers\Events
*/
class ConnectTransactionToPiggyBank
{
/**
* Connect a new transaction journal to any related piggy banks.
*
* @param TransactionStored $event
*
* @return bool
*/
public function handle(TransactionStored $event): bool
{
echo '<pre>';
/** @var PiggyBankRepositoryInterface $repository */
$repository = app(PiggyBankRepositoryInterface::class);
$transaction = $event->transaction;
$piggyBank = $repository->find($transaction['piggy_bank_id']);
// valid piggy:
if (is_null($piggyBank->id)) {
return true;
}
$amount = strval($transaction['amount']);
// piggy bank account something with amount:
if ($transaction['source_account_id'] == $piggyBank->account_id) {
// if the source of this transaction is the same as the piggy bank,
// the money is being removed from the piggy bank. So the
// amount must be negative:
$amount = bcmul($amount, '-1');
}
$repetition = $piggyBank->currentRelevantRep();
// add or remove the money from the piggy bank:
$newAmount = bcadd(strval($repetition->currentamount), $amount);
$repetition->currentamount = $newAmount;
$repetition->save();
// now generate a piggy bank event:
PiggyBankEvent::create(['piggy_bank_id' => $piggyBank->id, 'date' => $transaction['date'], 'amount' => $newAmount]);
return true;
}
}

View File

@ -53,11 +53,13 @@ class SplitController extends Controller
$accountRepository = app('FireflyIII\Repositories\Account\AccountRepositoryInterface');
$currencyRepository = app('FireflyIII\Repositories\Currency\CurrencyRepositoryInterface');
$budgetRepository = app('FireflyIII\Repositories\Budget\BudgetRepositoryInterface');
$piggyRepository = app('FireflyIII\Repositories\PiggyBank\PiggyBankRepositoryInterface');
$assetAccounts = ExpandedForm::makeSelectList($accountRepository->getAccountsByType(['Default account', 'Asset account']));
$sessionData = session('journal-data', []);
$uploadSize = min(Steam::phpBytes(ini_get('upload_max_filesize')), Steam::phpBytes(ini_get('post_max_size')));
$currencies = ExpandedForm::makeSelectList($currencyRepository->get());
$budgets = ExpandedForm::makeSelectListWithEmpty($budgetRepository->getActiveBudgets());
$piggyBanks = ExpandedForm::makeSelectListWithEmpty($piggyRepository->getPiggyBanksWithAmount());
$subTitle = trans('form.add_new_' . $sessionData['what']);
$subTitleIcon = 'fa-plus';
$preFilled = [
@ -74,7 +76,8 @@ class SplitController extends Controller
];
return view(
'split.journals.create', compact('journal', 'subTitle', 'subTitleIcon', 'preFilled', 'assetAccounts', 'currencies', 'budgets', 'uploadSize')
'split.journals.create',
compact('journal', 'piggyBanks', 'subTitle', 'subTitleIcon', 'preFilled', 'assetAccounts', 'currencies', 'budgets', 'uploadSize')
);
}
@ -125,7 +128,6 @@ class SplitController extends Controller
public function store(JournalInterface $repository, SplitJournalFormRequest $request, TransactionJournal $journal)
{
$data = $request->getSplitData();
foreach ($data['transactions'] as $transaction) {
$repository->storeTransaction($journal, $transaction);
}
@ -245,7 +247,7 @@ class SplitController extends Controller
$destinationName = $request->old('destination_account_name')[$index] ?? $transaction->account->name;
// any transfer not from the source:
if (($journal->isWithdrawal() || $journal->isDeposit()) && $transaction->account_id !== $sourceAccounts->first()->id) {
if ($transaction->account_id !== $sourceAccounts->first()->id) {
$array['description'][] = $description;
$array['destination_account_id'][] = $transaction->account_id;
$array['destination_account_name'][] = $destinationName;

View File

@ -59,6 +59,9 @@ class SplitJournalFormRequest extends Request
'category' => $this->get('category')[$index] ?? '',
'source_account_id' => intval($this->get('journal_source_account_id')),
'source_account_name' => $this->get('journal_source_account_name'),
'piggy_bank_id' => isset($this->get('piggy_bank_id')[$index])
? intval($this->get('piggy_bank_id')[$index])
: 0,
'destination_account_id' => isset($this->get('destination_account_id')[$index])
? intval($this->get('destination_account_id')[$index])
: intval($this->get('journal_destination_account_id')),

View File

@ -30,12 +30,16 @@ class EventServiceProvider extends ServiceProvider
'FireflyIII\Handlers\Events\FireRulesForUpdate',
],
'FireflyIII\Events\BudgetLimitStored' => [
'FireflyIII\Handlers\Events\BudgetLimitEventHandler@store',
],
'FireflyIII\Events\BudgetLimitUpdated' => [
'FireflyIII\Handlers\Events\BudgetLimitEventHandler@update',
],
'FireflyIII\Events\TransactionStored' => [
'FireflyIII\Handlers\Events\ConnectTransactionToPiggyBank',
],
'FireflyIII\Events\TransactionJournalStored' => [
'FireflyIII\Handlers\Events\ScanForBillsAfterStore',
'FireflyIII\Handlers\Events\ConnectJournalToPiggyBank',

View File

@ -18,6 +18,7 @@ use FireflyIII\Models\TransactionType;
use FireflyIII\Repositories\Tag\TagRepositoryInterface;
use FireflyIII\User;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Query\JoinClause;
use Illuminate\Pagination\LengthAwarePaginator;
use Illuminate\Support\Collection;
use Log;
@ -167,6 +168,18 @@ class JournalRepository implements JournalRepositoryInterface
if ($accounts->count() > 0) {
$ids = $accounts->pluck('id')->toArray();
// join source and destination:
$query->leftJoin(
'transactions as source', function (JoinClause $join) {
$join->on('source.transaction_journal_id', '=', 'transaction_journals.id')->where('source.amount', '<', 0);
}
);
$query->leftJoin(
'transactions as destination', function (JoinClause $join) {
$join->on('destination.transaction_journal_id', '=', 'transaction_journals.id')->where('destination.amount', '>', 0);
}
);
$query->where(
function (Builder $q) use ($ids) {
$q->whereIn('destination.account_id', $ids);

View File

@ -57,6 +57,21 @@ class PiggyBankRepository implements PiggyBankRepositoryInterface
return true;
}
/**
* @param int $piggyBankid
*
* @return PiggyBank
*/
public function find(int $piggyBankid): PiggyBank
{
$piggyBank = $this->user->piggyBanks()->where('piggy_banks.id', $piggyBankid)->first(['piggy_banks.*']);
if (!is_null($piggyBank)) {
return $piggyBank;
}
return new PiggyBank();
}
/**
* @param PiggyBank $piggyBank
*

View File

@ -34,6 +34,13 @@ interface PiggyBankRepositoryInterface
*/
public function destroy(PiggyBank $piggyBank): bool;
/**
* @param int $piggyBankid
*
* @return PiggyBank
*/
public function find(int $piggyBankid): PiggyBank;
/**
* Get all events.
*

View File

@ -93,6 +93,9 @@
<th>{{ trans('list.budget') }}</th>
{% endif %}
<th>{{ trans('list.category') }}</th>
{% if preFilled.what == 'transfer' %}
<th>{{ trans('list.piggy_bank') }}</th>
{% endif %}
</tr>
</thead>
<tbody>
@ -139,6 +142,12 @@
<td>
<input type="text" name="category[]" value="{{ preFilled.category[index] }}" class="form-control"/>
</td>
{% if preFilled.what == 'transfer' %}
<td>
<!-- RELATE THIS TRANSFER TO A PIGGY BANK -->
{{ Form.select('piggy_bank_id[]',piggyBanks, preFilled.piggy_bank_id[index], {class: 'form-control'}) }}
</td>
{% endif %}
</tr>
{% endfor %}
</tbody>

View File

@ -0,0 +1,301 @@
{
"users": [
{
"email": "thegrumpydictator@gmail.com",
"password": "james"
}
],
"roles": [
{
"user_id": 1,
"role": 1
}
],
"accounts": [
{
"user_id": 1,
"account_type_id": 3,
"name": "Checking Account",
"iban": "NL11XOLA6707795988"
},
{
"user_id": 1,
"account_type_id": 3,
"name": "Alternate",
"iban": "NL40UKBK3619908726"
},
{
"user_id": 1,
"account_type_id": 4,
"name": "SixtyFive"
},
{
"user_id": 1,
"account_type_id": 4,
"name": "EightyFour"
},
{
"user_id": 1,
"account_type_id": 4,
"name": "Fiftyone"
},
{
"user_id": 1,
"account_type_id": 5,
"name": "Work SixtyFive"
},
{
"user_id": 1,
"account_type_id": 5,
"name": "Work EightyFour"
},
{
"user_id": 1,
"account_type_id": 5,
"name": "Work Fiftyone"
}
],
"account-meta": [
{
"account_id": 1,
"name": "accountRole",
"data": "\"defaultAsset\""
},
{
"account_id": 2,
"name": "accountRole",
"data": "\"defaultAsset\""
}
],
"bills": [],
"budgets": [
{
"name": "Groceries",
"user_id": 1
},
{
"name": "Bills",
"user_id": 1
},
{
"name": "Car",
"user_id": 1
}
],
"budget-limits": [],
"monthly-limits": [
{
"budget_id": 1,
"amount_min": 200,
"amount_max": 200
},
{
"budget_id": 2,
"amount_min": 1000,
"amount_max": 1000
},
{
"budget_id": 3,
"amount_min": 200,
"amount_max": 200
}
],
"categories": [
{
"name": "Daily groceries",
"user_id": 1
},
{
"name": "Car",
"user_id": 1
},
{
"name": "Reimbursements",
"user_id": 1
}
],
"piggy-banks": [
{
"account_id": 2,
"name": "New camera",
"targetamount": 1000,
"startdate": "2015-04-01",
"reminder_skip": 0,
"remind_me": 0,
"order": 1,
"currentamount": 0
},
{
"account_id": 2,
"name": "New phone",
"targetamount": 600,
"startdate": "2015-04-01",
"reminder_skip": 0,
"remind_me": 0,
"order": 2,
"currentamount": 0
},
{
"account_id": 2,
"name": "New couch",
"targetamount": 500,
"startdate": "2015-04-01",
"reminder_skip": 0,
"remind_me": 0,
"order": 3,
"currentamount": 0
}
],
"piggy-events": [],
"rule-groups": [],
"rules": [],
"rule-triggers": [],
"rule-actions": [],
"tags": [],
"monthly-deposits": [],
"monthly-transfers": [],
"monthly-withdrawals": [],
"attachments": [],
"multi-withdrawals": [
{
"user_id": 1,
"date": "2016-03-12",
"description": "Even multi-withdrawal (50, 50)",
"destination_ids": [
3,
4
],
"source_id": 1,
"amounts": [
50,
50
],
"category_ids": [
1,
2,
3
],
"budget_ids": [
1,
2,
3
]
},
{
"user_id": 1,
"date": "2016-05-12",
"description": "Uneven multi-withdrawal (15,34,51)",
"destination_ids": [
3,
4,
5
],
"source_id": 1,
"amounts": [
14,
35,
51
],
"category_ids": [
1,
2,
3
],
"budget_ids": [
1,
2,
3
]
}
],
"multi-deposits": [
{
"user_id": 1,
"date": "2016-03-02",
"description": "Even multi-deposit (50, 50)",
"source_ids": [
6,
7
],
"destination_id": 1,
"amounts": [
50,
50
],
"category_ids": [
1,
2,
3
]
},
{
"user_id": 1,
"date": "2016-05-02",
"description": "Uneven multi-deposit (15,34,51)",
"source_ids": [
6,
7,
8
],
"destination_id": 1,
"amounts": [
14,
35,
51
],
"category_ids": [
1,
2,
3
]
}
],
"multi-transfers": [
{
"user_id": 1,
"date": "2016-01-18",
"description": "Even multi-transfer (50, 50)",
"source_ids": [
1,
1
],
"destination_ids": [
2,
2
],
"amounts": [
50,
50
],
"category_ids": [
1,
2
]
},
{
"user_id": 1,
"date": "2016-03-28",
"description": "Uneven multi-transfer (15,34,51)",
"source_ids": [
1,
1,
1
],
"destination_ids": [
2,
2,
2
],
"amounts": [
14,
35,
51
],
"category_ids": [
1,
2,
3
]
}
]
}