mirror of
https://github.com/firefly-iii/firefly-iii.git
synced 2025-02-25 18:45:27 -06:00
Many updates to get split transactions and normal transactions working side by side.
This commit is contained in:
parent
801c7c0ab6
commit
9a3cd27700
@ -20,6 +20,7 @@ use FireflyIII\Export\Entry\Entry;
|
||||
use FireflyIII\Models\ExportJob;
|
||||
use FireflyIII\Models\TransactionJournal;
|
||||
use FireflyIII\Repositories\Journal\JournalRepositoryInterface;
|
||||
use FireflyIII\Repositories\Journal\JournalTaskerInterface;
|
||||
use Illuminate\Filesystem\FilesystemAdapter;
|
||||
use Illuminate\Support\Collection;
|
||||
use Storage;
|
||||
@ -95,9 +96,9 @@ class Processor
|
||||
*/
|
||||
public function collectJournals(): bool
|
||||
{
|
||||
/** @var JournalRepositoryInterface $repository */
|
||||
$repository = app(JournalRepositoryInterface::class);
|
||||
$this->journals = $repository->getJournalsInRange($this->accounts, $this->settings['startDate'], $this->settings['endDate']);
|
||||
/** @var JournalTaskerInterface $tasker */
|
||||
$tasker = app(JournalTaskerInterface::class);
|
||||
$this->journals = $tasker->getJournalsInRange($this->accounts, $this->settings['startDate'], $this->settings['endDate']);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -20,7 +20,7 @@ use FireflyIII\Repositories\Account\AccountRepositoryInterface;
|
||||
use FireflyIII\Repositories\Account\AccountTaskerInterface;
|
||||
use FireflyIII\Repositories\Bill\BillRepositoryInterface;
|
||||
use FireflyIII\Repositories\Category\CategoryRepositoryInterface as CRI;
|
||||
use FireflyIII\Repositories\Journal\JournalRepositoryInterface;
|
||||
use FireflyIII\Repositories\Journal\JournalTaskerInterface;
|
||||
use FireflyIII\Repositories\Tag\TagRepositoryInterface;
|
||||
use FireflyIII\Support\CacheProperties;
|
||||
use Input;
|
||||
@ -270,17 +270,17 @@ class JsonController extends Controller
|
||||
}
|
||||
|
||||
/**
|
||||
* @param JournalRepositoryInterface $repository
|
||||
* @param JournalTaskerInterface $tasker
|
||||
* @param $what
|
||||
*
|
||||
* @return \Symfony\Component\HttpFoundation\Response
|
||||
*/
|
||||
public function transactionJournals(JournalRepositoryInterface $repository, $what)
|
||||
public function transactionJournals(JournalTaskerInterface $tasker, $what)
|
||||
{
|
||||
$descriptions = [];
|
||||
$type = config('firefly.transactionTypesByWhat.' . $what);
|
||||
$types = [$type];
|
||||
$journals = $repository->getJournals($types, 1, 50);
|
||||
$journals = $tasker->getJournals($types, 1, 50);
|
||||
foreach ($journals as $j) {
|
||||
$descriptions[] = $j->description;
|
||||
}
|
||||
|
@ -23,9 +23,12 @@ use FireflyIII\Models\AccountType;
|
||||
use FireflyIII\Models\TransactionJournal;
|
||||
use FireflyIII\Models\TransactionType;
|
||||
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
|
||||
use FireflyIII\Repositories\Budget\BudgetRepositoryInterface;
|
||||
use FireflyIII\Repositories\Journal\JournalRepositoryInterface;
|
||||
use FireflyIII\Repositories\Journal\JournalTaskerInterface;
|
||||
use FireflyIII\Repositories\PiggyBank\PiggyBankRepositoryInterface;
|
||||
use Illuminate\Http\Request;
|
||||
use Log;
|
||||
use Preferences;
|
||||
use Response;
|
||||
use Session;
|
||||
@ -40,6 +43,14 @@ use View;
|
||||
*/
|
||||
class TransactionController extends Controller
|
||||
{
|
||||
/** @var AccountRepositoryInterface */
|
||||
private $accounts;
|
||||
private $attachments;
|
||||
/** @var BudgetRepositoryInterface */
|
||||
private $budgets;
|
||||
/** @var PiggyBankRepositoryInterface */
|
||||
private $piggyBanks;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
@ -52,8 +63,21 @@ class TransactionController extends Controller
|
||||
$maxFileSize = Steam::phpBytes(ini_get('upload_max_filesize'));
|
||||
$maxPostSize = Steam::phpBytes(ini_get('post_max_size'));
|
||||
$uploadSize = min($maxFileSize, $maxPostSize);
|
||||
|
||||
View::share('uploadSize', $uploadSize);
|
||||
|
||||
// some useful repositories:
|
||||
$this->middleware(
|
||||
function ($request, $next) {
|
||||
$this->accounts = app(AccountRepositoryInterface::class);
|
||||
$this->budgets = app(BudgetRepositoryInterface::class);
|
||||
$this->piggyBanks = app(PiggyBankRepositoryInterface::class);
|
||||
$this->attachments = app(AttachmentHelperInterface::class);
|
||||
|
||||
return $next($request);
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@ -63,20 +87,16 @@ class TransactionController extends Controller
|
||||
*/
|
||||
public function create(string $what = TransactionType::DEPOSIT)
|
||||
{
|
||||
/** @var AccountRepositoryInterface $accountRepository */
|
||||
$accountRepository = app(AccountRepositoryInterface::class);
|
||||
$budgetRepository = app('FireflyIII\Repositories\Budget\BudgetRepositoryInterface');
|
||||
$piggyRepository = app('FireflyIII\Repositories\PiggyBank\PiggyBankRepositoryInterface');
|
||||
$what = strtolower($what);
|
||||
$uploadSize = min(Steam::phpBytes(ini_get('upload_max_filesize')), Steam::phpBytes(ini_get('post_max_size')));
|
||||
$assetAccounts = ExpandedForm::makeSelectList($accountRepository->getActiveAccountsByType(['Default account', 'Asset account']));
|
||||
$budgets = ExpandedForm::makeSelectListWithEmpty($budgetRepository->getActiveBudgets());
|
||||
$piggyBanks = $piggyRepository->getPiggyBanksWithAmount();
|
||||
$piggies = ExpandedForm::makeSelectListWithEmpty($piggyBanks);
|
||||
$preFilled = Session::has('preFilled') ? session('preFilled') : [];
|
||||
$subTitle = trans('form.add_new_' . $what);
|
||||
$subTitleIcon = 'fa-plus';
|
||||
$optionalFields = Preferences::get('transaction_journal_optional_fields', [])->data;
|
||||
$what = strtolower($what);
|
||||
$uploadSize = min(Steam::phpBytes(ini_get('upload_max_filesize')), Steam::phpBytes(ini_get('post_max_size')));
|
||||
$assetAccounts = ExpandedForm::makeSelectList($this->accounts->getActiveAccountsByType(['Default account', 'Asset account']));
|
||||
$budgets = ExpandedForm::makeSelectListWithEmpty($this->budgets->getActiveBudgets());
|
||||
$piggyBanks = $this->piggyBanks->getPiggyBanksWithAmount();
|
||||
$piggies = ExpandedForm::makeSelectListWithEmpty($piggyBanks);
|
||||
$preFilled = Session::has('preFilled') ? session('preFilled') : [];
|
||||
$subTitle = trans('form.add_new_' . $what);
|
||||
$subTitleIcon = 'fa-plus';
|
||||
$optionalFields = Preferences::get('transaction_journal_optional_fields', [])->data;
|
||||
|
||||
Session::put('preFilled', $preFilled);
|
||||
|
||||
@ -91,7 +111,6 @@ class TransactionController extends Controller
|
||||
|
||||
asort($piggies);
|
||||
|
||||
|
||||
return view('transactions.create', compact('assetAccounts', 'subTitleIcon', 'uploadSize', 'budgets', 'what', 'piggies', 'subTitle', 'optionalFields'));
|
||||
}
|
||||
|
||||
@ -145,19 +164,12 @@ class TransactionController extends Controller
|
||||
{
|
||||
$count = $journal->transactions()->count();
|
||||
if ($count > 2) {
|
||||
return redirect(route('split.journal.edit', [$journal->id]));
|
||||
return redirect(route('journal.edit-split', [$journal->id]));
|
||||
}
|
||||
|
||||
// code to get list data:
|
||||
$budgetRepository = app('FireflyIII\Repositories\Budget\BudgetRepositoryInterface');
|
||||
$piggyRepository = app('FireflyIII\Repositories\PiggyBank\PiggyBankRepositoryInterface');
|
||||
|
||||
/** @var AccountRepositoryInterface $repository */
|
||||
$repository = app(AccountRepositoryInterface::class);
|
||||
|
||||
$assetAccounts = ExpandedForm::makeSelectList($repository->getAccountsByType(['Default account', 'Asset account']));
|
||||
$budgetList = ExpandedForm::makeSelectListWithEmpty($budgetRepository->getActiveBudgets());
|
||||
$piggyBankList = ExpandedForm::makeSelectListWithEmpty($piggyRepository->getPiggyBanks());
|
||||
$assetAccounts = ExpandedForm::makeSelectList($this->accounts->getAccountsByType(['Default account', 'Asset account']));
|
||||
$budgetList = ExpandedForm::makeSelectListWithEmpty($this->budgets->getActiveBudgets());
|
||||
$piggyBankList = ExpandedForm::makeSelectListWithEmpty($this->piggyBanks->getPiggyBanks());
|
||||
|
||||
// view related code
|
||||
$subTitle = trans('breadcrumbs.edit_journal', ['description' => $journal->description]);
|
||||
@ -216,20 +228,20 @@ class TransactionController extends Controller
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Request $request
|
||||
* @param JournalRepositoryInterface $repository
|
||||
* @param string $what
|
||||
* @param Request $request
|
||||
* @param JournalTaskerInterface $tasker
|
||||
* @param string $what
|
||||
*
|
||||
* @return View
|
||||
*/
|
||||
public function index(Request $request, JournalRepositoryInterface $repository, string $what)
|
||||
public function index(Request $request, JournalTaskerInterface $tasker, string $what)
|
||||
{
|
||||
$pageSize = intval(Preferences::get('transactionPageSize', 50)->data);
|
||||
$subTitleIcon = config('firefly.transactionIconsByWhat.' . $what);
|
||||
$types = config('firefly.transactionTypesByWhat.' . $what);
|
||||
$subTitle = trans('firefly.title_' . $what);
|
||||
$page = intval($request->get('page'));
|
||||
$journals = $repository->getJournals($types, $page, $pageSize);
|
||||
$journals = $tasker->getJournals($types, $page, $pageSize);
|
||||
|
||||
$journals->setPath('transactions/' . $what);
|
||||
|
||||
@ -266,14 +278,14 @@ class TransactionController extends Controller
|
||||
}
|
||||
|
||||
/**
|
||||
* @param TransactionJournal $journal
|
||||
* @param JournalRepositoryInterface $repository
|
||||
* @param TransactionJournal $journal
|
||||
* @param JournalTaskerInterface $tasker
|
||||
*
|
||||
* @return View
|
||||
*/
|
||||
public function show(TransactionJournal $journal, JournalRepositoryInterface $repository, JournalTaskerInterface $tasker)
|
||||
public function show(TransactionJournal $journal, JournalTaskerInterface $tasker)
|
||||
{
|
||||
$events = $repository->getPiggyBankEvents($journal);
|
||||
$events = $tasker->getPiggyBankEvents($journal);
|
||||
$transactions = $tasker->getTransactionsOverview($journal);
|
||||
$what = strtolower($journal->transaction_type_type ?? $journal->transactionType->type);
|
||||
$subTitle = trans('firefly.' . $what) . ' "' . e($journal->description) . '"';
|
||||
@ -291,63 +303,47 @@ class TransactionController extends Controller
|
||||
*/
|
||||
public function store(JournalFormRequest $request, JournalRepositoryInterface $repository)
|
||||
{
|
||||
$att = app('FireflyIII\Helpers\Attachments\AttachmentHelperInterface');
|
||||
$doSplit = intval($request->get('split_journal')) === 1;
|
||||
$journalData = $request->getJournalData();
|
||||
$doSplit = intval($request->get('split_journal')) === 1;
|
||||
$createAnother = intval($request->get('create_another')) === 1;
|
||||
$data = $request->getJournalData();
|
||||
$journal = $repository->store($data);
|
||||
if (is_null($journal->id)) {
|
||||
// error!
|
||||
Log::error('Could not store transaction journal: ', $journal->getErrors()->toArray());
|
||||
Session::flash('error', $journal->getErrors()->first());
|
||||
|
||||
return redirect(route('transactions.create', [$request->input('what')]))->withInput();
|
||||
}
|
||||
|
||||
$this->attachments->saveAttachmentsForModel($journal);
|
||||
|
||||
// store the journal only, flash the rest.
|
||||
if ($doSplit) {
|
||||
$journal = $repository->storeJournal($journalData);
|
||||
$journal->completed = false;
|
||||
$journal->save();
|
||||
|
||||
// store attachments:
|
||||
$att->saveAttachmentsForModel($journal);
|
||||
|
||||
// flash errors
|
||||
if (count($att->getErrors()->get('attachments')) > 0) {
|
||||
Session::flash('error', $att->getErrors()->get('attachments'));
|
||||
}
|
||||
// flash messages
|
||||
if (count($att->getMessages()->get('attachments')) > 0) {
|
||||
Session::flash('info', $att->getMessages()->get('attachments'));
|
||||
}
|
||||
|
||||
Session::put('journal-data', $journalData);
|
||||
|
||||
return redirect(route('split.journal.create', [$journal->id]));
|
||||
}
|
||||
|
||||
|
||||
// if not withdrawal, unset budgetid.
|
||||
if ($journalData['what'] != strtolower(TransactionType::WITHDRAWAL)) {
|
||||
$journalData['budget_id'] = 0;
|
||||
}
|
||||
|
||||
$journal = $repository->store($journalData);
|
||||
$att->saveAttachmentsForModel($journal);
|
||||
|
||||
// flash errors
|
||||
if (count($att->getErrors()->get('attachments')) > 0) {
|
||||
Session::flash('error', $att->getErrors()->get('attachments'));
|
||||
if (count($this->attachments->getErrors()->get('attachments')) > 0) {
|
||||
Session::flash('error', $this->attachments->getErrors()->get('attachments'));
|
||||
}
|
||||
// flash messages
|
||||
if (count($att->getMessages()->get('attachments')) > 0) {
|
||||
Session::flash('info', $att->getMessages()->get('attachments'));
|
||||
if (count($this->attachments->getMessages()->get('attachments')) > 0) {
|
||||
Session::flash('info', $this->attachments->getMessages()->get('attachments'));
|
||||
}
|
||||
|
||||
event(new TransactionJournalStored($journal, intval($journalData['piggy_bank_id'])));
|
||||
event(new TransactionJournalStored($journal, $data['piggy_bank_id']));
|
||||
|
||||
Session::flash('success', strval(trans('firefly.stored_journal', ['description' => e($journal->description)])));
|
||||
Preferences::mark();
|
||||
|
||||
if (intval($request->get('create_another')) === 1) {
|
||||
if ($createAnother === true) {
|
||||
// set value so create routine will not overwrite URL:
|
||||
Session::put('transactions.create.fromStore', true);
|
||||
|
||||
return redirect(route('transactions.create', [$request->input('what')]))->withInput();
|
||||
}
|
||||
|
||||
if ($doSplit === true) {
|
||||
// redirect to edit screen:
|
||||
return redirect(route('transactions.edit', [$journal->id]));
|
||||
}
|
||||
|
||||
|
||||
// redirect to previous URL.
|
||||
return redirect(session('transactions.create.url'));
|
||||
|
||||
@ -355,35 +351,31 @@ class TransactionController extends Controller
|
||||
|
||||
|
||||
/**
|
||||
* @param JournalFormRequest $request
|
||||
* @param JournalRepositoryInterface $repository
|
||||
* @param AttachmentHelperInterface $att
|
||||
* @param TransactionJournal $journal
|
||||
* @param JournalFormRequest $request
|
||||
* @param TransactionJournal $journal
|
||||
*
|
||||
* @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector
|
||||
*/
|
||||
public function update(JournalFormRequest $request, JournalRepositoryInterface $repository, AttachmentHelperInterface $att, TransactionJournal $journal)
|
||||
public function update(JournalFormRequest $request, JournalRepositoryInterface $repository, TransactionJournal $journal)
|
||||
{
|
||||
$journalData = $request->getJournalData();
|
||||
$repository->update($journal, $journalData);
|
||||
|
||||
// save attachments:
|
||||
$att->saveAttachmentsForModel($journal);
|
||||
$data = $request->getJournalData();
|
||||
$journal = $repository->update($journal, $data);
|
||||
$this->attachments->saveAttachmentsForModel($journal);
|
||||
|
||||
// flash errors
|
||||
if (count($att->getErrors()->get('attachments')) > 0) {
|
||||
Session::flash('error', $att->getErrors()->get('attachments'));
|
||||
if (count($this->attachments->getErrors()->get('attachments')) > 0) {
|
||||
Session::flash('error', $this->attachments->getErrors()->get('attachments'));
|
||||
}
|
||||
// flash messages
|
||||
if (count($att->getMessages()->get('attachments')) > 0) {
|
||||
Session::flash('info', $att->getMessages()->get('attachments'));
|
||||
if (count($this->attachments->getMessages()->get('attachments')) > 0) {
|
||||
Session::flash('info', $this->attachments->getMessages()->get('attachments'));
|
||||
}
|
||||
|
||||
event(new TransactionJournalUpdated($journal));
|
||||
// update, get events by date and sort DESC
|
||||
|
||||
$type = strtolower($journal->transaction_type_type ?? TransactionJournal::transactionTypeStr($journal));
|
||||
Session::flash('success', strval(trans('firefly.updated_' . $type, ['description' => e($journalData['description'])])));
|
||||
$type = strtolower(TransactionJournal::transactionTypeStr($journal));
|
||||
Session::flash('success', strval(trans('firefly.updated_' . $type, ['description' => e($data['description'])])));
|
||||
Preferences::mark();
|
||||
|
||||
if (intval($request->get('return_to_edit')) === 1) {
|
||||
|
@ -14,7 +14,6 @@ declare(strict_types = 1);
|
||||
namespace FireflyIII\Http\Requests;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use Exception;
|
||||
use FireflyIII\Exceptions\FireflyException;
|
||||
use FireflyIII\Models\TransactionType;
|
||||
use Input;
|
||||
@ -37,86 +36,110 @@ class JournalFormRequest extends Request
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns and validates the data required to store a new journal. Can handle both single transaction journals and split journals.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getJournalData()
|
||||
{
|
||||
$tags = $this->getFieldOrEmptyString('tags');
|
||||
$data = [
|
||||
'what' => $this->get('what'), // type. can be 'deposit', 'withdrawal' or 'transfer'
|
||||
'user' => auth()->user()->id,
|
||||
'date' => new Carbon($this->get('date')),
|
||||
'tags' => explode(',', $this->getFieldOrEmptyString('tags')),
|
||||
'currency_id' => intval($this->get('amount_currency_id_amount')),
|
||||
|
||||
return [
|
||||
'what' => $this->get('what'),
|
||||
'description' => trim($this->get('description')),
|
||||
'source_account_id' => intval($this->get('source_account_id')),
|
||||
'source_account_name' => trim($this->getFieldOrEmptyString('source_account_name')),
|
||||
'destination_account_id' => intval($this->get('destination_account_id')),
|
||||
'destination_account_name' => trim($this->getFieldOrEmptyString('destination_account_name')),
|
||||
'amount' => round($this->get('amount'), 2),
|
||||
'user' => auth()->user()->id,
|
||||
'amount_currency_id_amount' => intval($this->get('amount_currency_id_amount')),
|
||||
'date' => new Carbon($this->get('date')),
|
||||
'interest_date' => $this->getDateOrNull('interest_date'),
|
||||
'book_date' => $this->getDateOrNull('book_date'),
|
||||
'process_date' => $this->getDateOrNull('process_date'),
|
||||
'budget_id' => intval($this->get('budget_id')),
|
||||
'category' => trim($this->getFieldOrEmptyString('category')),
|
||||
'tags' => explode(',', $tags),
|
||||
'piggy_bank_id' => intval($this->get('piggy_bank_id')),
|
||||
// all custom fields:
|
||||
'interest_date' => $this->getDateOrNull('interest_date'),
|
||||
'book_date' => $this->getDateOrNull('book_date'),
|
||||
'process_date' => $this->getDateOrNull('process_date'),
|
||||
'due_date' => $this->getDateOrNull('due_date'),
|
||||
'payment_date' => $this->getDateOrNull('payment_date'),
|
||||
'invoice_date' => $this->getDateOrNull('invoice_date'),
|
||||
'internal_reference' => trim(strval($this->get('internal_reference'))),
|
||||
'notes' => trim(strval($this->get('notes'))),
|
||||
|
||||
// new custom fields here:
|
||||
'due_date' => $this->getDateOrNull('due_date'),
|
||||
'payment_date' => $this->getDateOrNull('payment_date'),
|
||||
'invoice_date' => $this->getDateOrNull('invoice_date'),
|
||||
'internal_reference' => trim(strval($this->get('internal_reference'))),
|
||||
'notes' => trim(strval($this->get('notes'))),
|
||||
// transaction / journal data:
|
||||
'description' => $this->getFieldOrEmptyString('description'),
|
||||
'amount' => round($this->get('amount'), 2),
|
||||
'budget_id' => intval($this->get('budget_id')),
|
||||
'category' => $this->getFieldOrEmptyString('category'),
|
||||
'source_account_id' => intval($this->get('source_account_id')),
|
||||
'source_account_name' => $this->getFieldOrEmptyString('source_account_name'),
|
||||
'destination_account_id' => $this->getFieldOrEmptyString('destination_account_id'),
|
||||
'destination_account_name' => $this->getFieldOrEmptyString('destination_account_name'),
|
||||
'piggy_bank_id' => intval($this->get('piggy_bank_id')),
|
||||
|
||||
];
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
* @throws Exception
|
||||
*/
|
||||
public function rules()
|
||||
{
|
||||
$what = Input::get('what');
|
||||
$rules = [
|
||||
'description' => 'required|min:1,max:255',
|
||||
'what' => 'required|in:withdrawal,deposit,transfer',
|
||||
'amount' => 'numeric|required|min:0.01',
|
||||
'date' => 'required|date',
|
||||
'process_date' => 'date',
|
||||
'book_date' => 'date',
|
||||
'interest_date' => 'date',
|
||||
'category' => 'between:1,255',
|
||||
'amount_currency_id_amount' => 'required|exists:transaction_currencies,id',
|
||||
'piggy_bank_id' => 'numeric',
|
||||
'what' => 'required|in:withdrawal,deposit,transfer',
|
||||
'date' => 'required|date',
|
||||
|
||||
// new custom fields here:
|
||||
'due_date' => 'date',
|
||||
'payment_date' => 'date',
|
||||
'internal_reference' => 'min:1,max:255',
|
||||
'notes' => 'min:1,max:65536',
|
||||
// then, custom fields:
|
||||
'interest_date' => 'date',
|
||||
'book_date' => 'date',
|
||||
'process_date' => 'date',
|
||||
'due_date' => 'date',
|
||||
'payment_date' => 'date',
|
||||
'invoice_date' => 'date',
|
||||
'internal_reference' => 'min:1,max:255',
|
||||
'notes' => 'min:1,max:50000',
|
||||
// and then transaction rules:
|
||||
'description' => 'required|between:1,255',
|
||||
'amount' => 'numeric|required|min:0.01',
|
||||
'budget_id' => 'mustExist:budgets,id|belongsToUser:budgets,id',
|
||||
'category' => 'between:1,255',
|
||||
'source_account_id' => 'numeric|belongsToUser:accounts,id',
|
||||
'source_account_name' => 'between:1,255',
|
||||
'destination_account_id' => 'numeric|belongsToUser:accounts,id',
|
||||
'destination_account_name' => 'between:1,255',
|
||||
'piggy_bank_id' => 'between:1,255',
|
||||
];
|
||||
|
||||
// some rules get an upgrade depending on the type of data:
|
||||
$rules = $this->enhanceRules($what, $rules);
|
||||
|
||||
return $rules;
|
||||
}
|
||||
|
||||
/**
|
||||
* Inspired by https://www.youtube.com/watch?v=WwnI0RS6J5A
|
||||
*
|
||||
* @param string $what
|
||||
* @param array $rules
|
||||
*
|
||||
* @return array
|
||||
* @throws FireflyException
|
||||
*/
|
||||
private function enhanceRules(string $what, array $rules): array
|
||||
{
|
||||
switch ($what) {
|
||||
case strtolower(TransactionType::WITHDRAWAL):
|
||||
$rules['source_account_id'] = 'required|exists:accounts,id|belongsToUser:accounts';
|
||||
$rules['destination_account_name'] = 'between:1,255';
|
||||
if (intval(Input::get('budget_id')) != 0) {
|
||||
$rules['budget_id'] = 'exists:budgets,id|belongsToUser:budgets';
|
||||
}
|
||||
break;
|
||||
case strtolower(TransactionType::DEPOSIT):
|
||||
$rules['source_account_name'] = 'between:1,255';
|
||||
$rules['destination_account_id'] = 'required|exists:accounts,id|belongsToUser:accounts';
|
||||
break;
|
||||
case strtolower(TransactionType::TRANSFER):
|
||||
// this may not work:
|
||||
$rules['source_account_id'] = 'required|exists:accounts,id|belongsToUser:accounts|different:destination_account_id';
|
||||
$rules['destination_account_id'] = 'required|exists:accounts,id|belongsToUser:accounts|different:source_account_id';
|
||||
|
||||
break;
|
||||
default:
|
||||
throw new FireflyException('Cannot handle transaction type of type ' . e($what) . '.');
|
||||
throw new FireflyException('Cannot handle transaction type of type ' . e($what) . ' . ');
|
||||
}
|
||||
|
||||
return $rules;
|
||||
@ -141,4 +164,63 @@ class JournalFormRequest extends Request
|
||||
{
|
||||
return $this->get($field) ?? '';
|
||||
}
|
||||
//
|
||||
// /**
|
||||
// * @param int $index
|
||||
// * @param string $field
|
||||
// *
|
||||
// * @return int
|
||||
// */
|
||||
// private function getIntFromArray(int $index, string $field): int
|
||||
// {
|
||||
// $array = $this->get($field);
|
||||
// if (isset($array[$index])) {
|
||||
// return intval($array[$index]);
|
||||
// }
|
||||
//
|
||||
// return 0;
|
||||
// }
|
||||
//
|
||||
// /**
|
||||
// * @param int $index
|
||||
// * @param string $field
|
||||
// *
|
||||
// * @return string
|
||||
// */
|
||||
// private function getStringFromArray(int $index, string $field): string
|
||||
// {
|
||||
// $array = $this->get($field);
|
||||
// if (isset($array[$index])) {
|
||||
// return trim($array[$index]);
|
||||
// }
|
||||
//
|
||||
// return '';
|
||||
// }
|
||||
//
|
||||
// /**
|
||||
// * @return array
|
||||
// */
|
||||
// private function getTransactionData(): array
|
||||
// {
|
||||
// $transactions = [];
|
||||
// $array = $this->get('amount');
|
||||
// if (is_array($array) && count($array) > 0) {
|
||||
// foreach ($array as $index => $amount) {
|
||||
// $transaction = [
|
||||
// 'description' => $this->getStringFromArray($index, 'description'),
|
||||
// 'amount' => round($amount, 2),
|
||||
// 'budget_id' => $this->getIntFromArray($index, 'budget_id'),
|
||||
// 'category' => $this->getStringFromArray($index, 'category'),
|
||||
// 'source_account_id' => $this->getIntFromArray($index, 'source_account_id'),
|
||||
// 'source_account_name' => $this->getStringFromArray($index, 'source_account_name'),
|
||||
// 'destination_account_id' => $this->getIntFromArray($index, 'destination_account_id'),
|
||||
// 'destination_account_name' => $this->getStringFromArray($index, 'destination_account_name'),
|
||||
// 'piggy_bank_id' => $this->getIntFromArray($index, 'piggy_bank_id'),
|
||||
// ];
|
||||
// $transactions[] = $transaction;
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// return $transactions;
|
||||
// }
|
||||
}
|
||||
|
@ -15,7 +15,7 @@ namespace FireflyIII\Jobs;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use FireflyIII\Models\RuleGroup;
|
||||
use FireflyIII\Repositories\Journal\JournalRepositoryInterface;
|
||||
use FireflyIII\Repositories\Journal\JournalTaskerInterface;
|
||||
use FireflyIII\Rules\Processor;
|
||||
use FireflyIII\User;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
@ -155,10 +155,10 @@ class ExecuteRuleGroupOnExistingTransactions extends Job implements ShouldQueue
|
||||
*/
|
||||
protected function collectJournals()
|
||||
{
|
||||
/** @var JournalRepositoryInterface $repository */
|
||||
$repository = app(JournalRepositoryInterface::class);
|
||||
/** @var JournalTaskerInterface $tasker */
|
||||
$tasker = app(JournalTaskerInterface::class);
|
||||
|
||||
return $repository->getJournalsInRange($this->accounts, $this->startDate, $this->endDate);
|
||||
return $tasker->getJournalsInRange($this->accounts, $this->startDate, $this->endDate);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -434,6 +434,9 @@ class TransactionJournal extends TransactionJournalSupport
|
||||
|
||||
return new TransactionJournalMeta();
|
||||
}
|
||||
if (is_string($value) && strlen($value) === 0) {
|
||||
return new TransactionJournalMeta();
|
||||
}
|
||||
|
||||
if ($value instanceof Carbon) {
|
||||
$value = $value->toW3cString();
|
||||
|
@ -15,6 +15,7 @@ namespace FireflyIII\Providers;
|
||||
|
||||
use FireflyIII\Support\Amount;
|
||||
use FireflyIII\Support\ExpandedForm;
|
||||
use FireflyIII\Support\ExpandedMultiForm;
|
||||
use FireflyIII\Support\FireflyConfig;
|
||||
use FireflyIII\Support\Navigation;
|
||||
use FireflyIII\Support\Preferences;
|
||||
@ -92,6 +93,11 @@ class FireflyServiceProvider extends ServiceProvider
|
||||
return new ExpandedForm;
|
||||
}
|
||||
);
|
||||
$this->app->bind(
|
||||
'expandedmultiform', function () {
|
||||
return new ExpandedMultiForm;
|
||||
}
|
||||
);
|
||||
|
||||
$this->app->bind('FireflyIII\Repositories\Currency\CurrencyRepositoryInterface', 'FireflyIII\Repositories\Currency\CurrencyRepository');
|
||||
$this->app->bind('FireflyIII\Support\Search\SearchInterface', 'FireflyIII\Support\Search\Search');
|
||||
|
@ -13,24 +13,18 @@ declare(strict_types = 1);
|
||||
|
||||
namespace FireflyIII\Repositories\Journal;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use DB;
|
||||
use FireflyIII\Exceptions\FireflyException;
|
||||
use FireflyIII\Models\Account;
|
||||
use FireflyIII\Models\AccountType;
|
||||
use FireflyIII\Models\Budget;
|
||||
use FireflyIII\Models\Category;
|
||||
use FireflyIII\Models\PiggyBankEvent;
|
||||
use FireflyIII\Models\Tag;
|
||||
use FireflyIII\Models\Transaction;
|
||||
use FireflyIII\Models\TransactionJournal;
|
||||
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;
|
||||
|
||||
/**
|
||||
@ -100,88 +94,6 @@ class JournalRepository implements JournalRepositoryInterface
|
||||
return $entry;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $types
|
||||
* @param int $page
|
||||
* @param int $pageSize
|
||||
*
|
||||
* @return LengthAwarePaginator
|
||||
*/
|
||||
public function getJournals(array $types, int $page, int $pageSize = 50): LengthAwarePaginator
|
||||
{
|
||||
$offset = ($page - 1) * $pageSize;
|
||||
$query = $this->user->transactionJournals()->expanded()->sortCorrectly();
|
||||
$query->where('transaction_journals.completed', 1);
|
||||
if (count($types) > 0) {
|
||||
$query->transactionTypes($types);
|
||||
}
|
||||
$count = $this->user->transactionJournals()->transactionTypes($types)->count();
|
||||
$set = $query->take($pageSize)->offset($offset)->get(TransactionJournal::queryFields());
|
||||
$journals = new LengthAwarePaginator($set, $count, $pageSize, $page);
|
||||
|
||||
return $journals;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a collection of ALL journals, given a specific account and a date range.
|
||||
*
|
||||
* @param Collection $accounts
|
||||
* @param Carbon $start
|
||||
* @param Carbon $end
|
||||
*
|
||||
* @return Collection
|
||||
*/
|
||||
public function getJournalsInRange(Collection $accounts, Carbon $start, Carbon $end): Collection
|
||||
{
|
||||
$query = $this->user->transactionJournals()->expanded()->sortCorrectly();
|
||||
$query->where('transaction_journals.completed', 1);
|
||||
$query->before($end);
|
||||
$query->after($start);
|
||||
|
||||
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);
|
||||
$q->orWhereIn('source.account_id', $ids);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
$set = $query->get(TransactionJournal::queryFields());
|
||||
|
||||
return $set;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param TransactionJournal $journal
|
||||
*
|
||||
* @return Collection
|
||||
*/
|
||||
public function getPiggyBankEvents(TransactionJournal $journal): Collection
|
||||
{
|
||||
/** @var Collection $set */
|
||||
$events = $journal->piggyBankEvents()->get();
|
||||
$events->each(
|
||||
function (PiggyBankEvent $event) {
|
||||
$event->piggyBank = $event->piggyBank()->withTrashed()->first();
|
||||
}
|
||||
);
|
||||
|
||||
return $events;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $data
|
||||
@ -192,56 +104,47 @@ class JournalRepository implements JournalRepositoryInterface
|
||||
{
|
||||
// find transaction type.
|
||||
$transactionType = TransactionType::where('type', ucfirst($data['what']))->first();
|
||||
|
||||
// store actual journal.
|
||||
$journal = new TransactionJournal(
|
||||
$journal = new TransactionJournal(
|
||||
[
|
||||
'user_id' => $data['user'],
|
||||
'transaction_type_id' => $transactionType->id,
|
||||
'transaction_currency_id' => $data['amount_currency_id_amount'],
|
||||
'transaction_currency_id' => $data['currency_id'],
|
||||
'description' => $data['description'],
|
||||
'completed' => 0,
|
||||
'date' => $data['date'],
|
||||
'interest_date' => $data['interest_date'],
|
||||
'book_date' => $data['book_date'],
|
||||
'process_date' => $data['process_date'],
|
||||
]
|
||||
);
|
||||
$journal->save();
|
||||
|
||||
// store or get category
|
||||
if (strlen($data['category']) > 0) {
|
||||
$category = Category::firstOrCreateEncrypted(['name' => $data['category'], 'user_id' => $data['user']]);
|
||||
$journal->categories()->save($category);
|
||||
}
|
||||
// store stuff:
|
||||
$this->storeCategoryWithJournal($journal, $data['category']);
|
||||
$this->storeBudgetWithJournal($journal, $data['budget_id']);
|
||||
$accounts = $this->storeAccounts($transactionType, $data);
|
||||
|
||||
// store or get budget
|
||||
if (intval($data['budget_id']) > 0 && $transactionType->type !== TransactionType::TRANSFER) {
|
||||
/** @var \FireflyIII\Models\Budget $budget */
|
||||
$budget = Budget::find($data['budget_id']);
|
||||
$journal->budgets()->save($budget);
|
||||
}
|
||||
// store two transactions:
|
||||
$one = [
|
||||
'journal' => $journal,
|
||||
'account' => $accounts['source'],
|
||||
'amount' => bcmul(strval($data['amount']), '-1'),
|
||||
'description' => null,
|
||||
'category' => null,
|
||||
'budget' => null,
|
||||
'identifier' => 0,
|
||||
];
|
||||
$this->storeTransaction($one);
|
||||
|
||||
// store accounts (depends on type)
|
||||
list($sourceAccount, $destinationAccount) = $this->storeAccounts($transactionType, $data);
|
||||
$two = [
|
||||
'journal' => $journal,
|
||||
'account' => $accounts['destination'],
|
||||
'amount' => $data['amount'],
|
||||
'description' => null,
|
||||
'category' => null,
|
||||
'budget' => null,
|
||||
'identifier' => 0,
|
||||
];
|
||||
|
||||
$this->storeTransaction($two);
|
||||
|
||||
// store accompanying transactions.
|
||||
Transaction::create( // first transaction.
|
||||
[
|
||||
'account_id' => $sourceAccount->id,
|
||||
'transaction_journal_id' => $journal->id,
|
||||
'amount' => $data['amount'] * -1,
|
||||
]
|
||||
);
|
||||
Transaction::create( // second transaction.
|
||||
[
|
||||
'account_id' => $destinationAccount->id,
|
||||
'transaction_journal_id' => $journal->id,
|
||||
'amount' => $data['amount'],
|
||||
]
|
||||
);
|
||||
$journal->completed = 1;
|
||||
$journal->save();
|
||||
|
||||
// store tags
|
||||
if (isset($data['tags']) && is_array($data['tags'])) {
|
||||
@ -256,6 +159,9 @@ class JournalRepository implements JournalRepositoryInterface
|
||||
Log::debug(sprintf('Could not store meta field "%s" with value "%s" for journal #%d', json_encode($key), json_encode($value), $journal->id));
|
||||
}
|
||||
|
||||
$journal->completed = 1;
|
||||
$journal->save();
|
||||
|
||||
return $journal;
|
||||
|
||||
}
|
||||
@ -302,45 +208,24 @@ class JournalRepository implements JournalRepositoryInterface
|
||||
*/
|
||||
public function update(TransactionJournal $journal, array $data): TransactionJournal
|
||||
{
|
||||
// update actual journal.
|
||||
$journal->transaction_currency_id = $data['amount_currency_id_amount'];
|
||||
// update actual journal:
|
||||
$journal->transaction_currency_id = $data['currency_id'];
|
||||
$journal->description = $data['description'];
|
||||
$journal->date = $data['date'];
|
||||
|
||||
// unlink all categories, recreate them:
|
||||
$journal->categories()->detach();
|
||||
if (strlen($data['category']) > 0) {
|
||||
$category = Category::firstOrCreateEncrypted(['name' => $data['category'], 'user_id' => $data['user']]);
|
||||
$journal->categories()->save($category);
|
||||
}
|
||||
|
||||
// unlink all budgets and recreate them:
|
||||
$journal->budgets()->detach();
|
||||
if (intval($data['budget_id']) > 0 && $journal->transactionType->type !== TransactionType::TRANSFER) {
|
||||
/** @var \FireflyIII\Models\Budget $budget */
|
||||
$budget = Budget::where('user_id', $this->user->id)->where('id', $data['budget_id'])->first();
|
||||
$journal->budgets()->save($budget);
|
||||
}
|
||||
|
||||
// store accounts (depends on type)
|
||||
list($fromAccount, $toAccount) = $this->storeAccounts($journal->transactionType, $data);
|
||||
$this->storeCategoryWithJournal($journal, $data['category']);
|
||||
$this->storeBudgetWithJournal($journal, $data['budget_id']);
|
||||
$accounts = $this->storeAccounts($journal->transactionType, $data);
|
||||
|
||||
// update the from and to transaction.
|
||||
/** @var Transaction $transaction */
|
||||
foreach ($journal->transactions()->get() as $transaction) {
|
||||
if ($transaction->amount < 0) {
|
||||
// this is the from transaction, negative amount:
|
||||
$transaction->amount = $data['amount'] * -1;
|
||||
$transaction->account_id = $fromAccount->id;
|
||||
$transaction->save();
|
||||
}
|
||||
if ($transaction->amount > 0) {
|
||||
$transaction->amount = $data['amount'];
|
||||
$transaction->account_id = $toAccount->id;
|
||||
$transaction->save();
|
||||
}
|
||||
}
|
||||
$sourceAmount = bcmul(strval($data['amount']), '-1');
|
||||
$this->updateSourceTransaction($journal, $accounts['source'], $sourceAmount); // negative because source loses money.
|
||||
|
||||
$amount = strval($data['amount']);
|
||||
$this->updateDestinationTransaction($journal, $accounts['destination'], $amount); // positive because destination gets money.
|
||||
|
||||
$journal->save();
|
||||
|
||||
@ -402,38 +287,66 @@ class JournalRepository implements JournalRepositoryInterface
|
||||
*/
|
||||
private function storeAccounts(TransactionType $type, array $data): array
|
||||
{
|
||||
$sourceAccount = null;
|
||||
$destinationAccount = null;
|
||||
$accounts = [
|
||||
'source' => null,
|
||||
'destination' => null,
|
||||
];
|
||||
switch ($type->type) {
|
||||
case TransactionType::WITHDRAWAL:
|
||||
list($sourceAccount, $destinationAccount) = $this->storeWithdrawalAccounts($data);
|
||||
$accounts = $this->storeWithdrawalAccounts($data);
|
||||
break;
|
||||
|
||||
case TransactionType::DEPOSIT:
|
||||
list($sourceAccount, $destinationAccount) = $this->storeDepositAccounts($data);
|
||||
$accounts = $this->storeDepositAccounts($data);
|
||||
|
||||
break;
|
||||
case TransactionType::TRANSFER:
|
||||
$sourceAccount = Account::where('user_id', $this->user->id)->where('id', $data['source_account_id'])->first();
|
||||
$destinationAccount = Account::where('user_id', $this->user->id)->where('id', $data['destination_account_id'])->first();
|
||||
|
||||
$accounts['source'] = Account::where('user_id', $this->user->id)->where('id', $data['source_account_id'])->first();
|
||||
$accounts['destination'] = Account::where('user_id', $this->user->id)->where('id', $data['destination_account_id'])->first();
|
||||
break;
|
||||
default:
|
||||
throw new FireflyException('Did not recognise transaction type.');
|
||||
throw new FireflyException(sprintf('Did not recognise transaction type "%s".', $type->type));
|
||||
}
|
||||
|
||||
if (is_null($destinationAccount)) {
|
||||
if (is_null($accounts['source'])) {
|
||||
Log::error('"destination"-account is null, so we cannot continue!', ['data' => $data]);
|
||||
throw new FireflyException('"destination"-account is null, so we cannot continue!');
|
||||
}
|
||||
|
||||
if (is_null($sourceAccount)) {
|
||||
if (is_null($accounts['destination'])) {
|
||||
Log::error('"source"-account is null, so we cannot continue!', ['data' => $data]);
|
||||
throw new FireflyException('"source"-account is null, so we cannot continue!');
|
||||
|
||||
}
|
||||
|
||||
|
||||
return [$sourceAccount, $destinationAccount];
|
||||
return $accounts;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param TransactionJournal $journal
|
||||
* @param int $budgetId
|
||||
*/
|
||||
private function storeBudgetWithJournal(TransactionJournal $journal, int $budgetId)
|
||||
{
|
||||
if (intval($budgetId) > 0 && $journal->transactionType->type !== TransactionType::TRANSFER) {
|
||||
/** @var \FireflyIII\Models\Budget $budget */
|
||||
$budget = Budget::find($budgetId);
|
||||
$journal->budgets()->save($budget);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param TransactionJournal $journal
|
||||
* @param string $category
|
||||
*/
|
||||
private function storeCategoryWithJournal(TransactionJournal $journal, string $category)
|
||||
{
|
||||
if (strlen($category) > 0) {
|
||||
$category = Category::firstOrCreateEncrypted(['name' => $category, 'user_id' => $journal->user_id]);
|
||||
$journal->categories()->save($category);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -446,19 +359,50 @@ class JournalRepository implements JournalRepositoryInterface
|
||||
$destinationAccount = Account::where('user_id', $this->user->id)->where('id', $data['destination_account_id'])->first(['accounts.*']);
|
||||
|
||||
if (strlen($data['source_account_name']) > 0) {
|
||||
$fromType = AccountType::where('type', 'Revenue account')->first();
|
||||
$fromAccount = Account::firstOrCreateEncrypted(
|
||||
['user_id' => $data['user'], 'account_type_id' => $fromType->id, 'name' => $data['source_account_name'], 'active' => 1]
|
||||
$sourceType = AccountType::where('type', 'Revenue account')->first();
|
||||
$sourceAccount = Account::firstOrCreateEncrypted(
|
||||
['user_id' => $data['user'], 'account_type_id' => $sourceType->id, 'name' => $data['source_account_name'], 'active' => 1]
|
||||
);
|
||||
|
||||
return [$fromAccount, $destinationAccount];
|
||||
return [
|
||||
'source' => $sourceAccount,
|
||||
'destination' => $destinationAccount,
|
||||
];
|
||||
}
|
||||
$fromType = AccountType::where('type', 'Cash account')->first();
|
||||
$fromAccount = Account::firstOrCreateEncrypted(
|
||||
['user_id' => $data['user'], 'account_type_id' => $fromType->id, 'name' => 'Cash account', 'active' => 1]
|
||||
$sourceType = AccountType::where('type', 'Cash account')->first();
|
||||
$sourceAccount = Account::firstOrCreateEncrypted(
|
||||
['user_id' => $data['user'], 'account_type_id' => $sourceType->id, 'name' => 'Cash account', 'active' => 1]
|
||||
);
|
||||
|
||||
return [$fromAccount, $destinationAccount];
|
||||
return [
|
||||
'source' => $sourceAccount,
|
||||
'destination' => $destinationAccount,
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
private function storeTransaction(array $data): Transaction
|
||||
{
|
||||
/** @var Transaction $transaction */
|
||||
$transaction = Transaction::create(
|
||||
[
|
||||
'transaction_journal_id' => $data['journal']->id,
|
||||
'account_id' => $data['account']->id,
|
||||
'amount' => $data['amount'],
|
||||
'description' => $data['description'],
|
||||
'identifier' => $data['identifier'],
|
||||
]
|
||||
);
|
||||
if (!is_null($data['category'])) {
|
||||
$transaction->categories()->save($data['category']);
|
||||
}
|
||||
|
||||
if (!is_null($data['budget'])) {
|
||||
$transaction->categories()->save($data['budget']);
|
||||
}
|
||||
|
||||
return $transaction;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@ -481,14 +425,69 @@ class JournalRepository implements JournalRepositoryInterface
|
||||
]
|
||||
);
|
||||
|
||||
return [$sourceAccount, $destinationAccount];
|
||||
return [
|
||||
'source' => $sourceAccount,
|
||||
'destination' => $destinationAccount,
|
||||
];
|
||||
}
|
||||
$destinationType = AccountType::where('type', 'Cash account')->first();
|
||||
$destinationAccount = Account::firstOrCreateEncrypted(
|
||||
['user_id' => $data['user'], 'account_type_id' => $destinationType->id, 'name' => 'Cash account', 'active' => 1]
|
||||
);
|
||||
|
||||
return [$sourceAccount, $destinationAccount];
|
||||
return [
|
||||
'source' => $sourceAccount,
|
||||
'destination' => $destinationAccount,
|
||||
];
|
||||
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @param TransactionJournal $journal
|
||||
* @param Account $account
|
||||
* @param string $amount
|
||||
*
|
||||
* @throws FireflyException
|
||||
*/
|
||||
private function updateDestinationTransaction(TransactionJournal $journal, Account $account, string $amount)
|
||||
{
|
||||
// should be one:
|
||||
$set = $journal->transactions()->where('amount', '>', 0)->get();
|
||||
if ($set->count() != 1) {
|
||||
throw new FireflyException(
|
||||
sprintf('Journal #%d has an unexpected (%d) amount of transactions with an amount more than zero.', $journal->id, $set->count())
|
||||
);
|
||||
}
|
||||
/** @var Transaction $transaction */
|
||||
$transaction = $set->first();
|
||||
$transaction->amount = $amount;
|
||||
$transaction->account_id = $account->id;
|
||||
$transaction->save();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @param TransactionJournal $journal
|
||||
* @param Account $account
|
||||
* @param string $amount
|
||||
*
|
||||
* @throws FireflyException
|
||||
*/
|
||||
private function updateSourceTransaction(TransactionJournal $journal, Account $account, string $amount)
|
||||
{
|
||||
// should be one:
|
||||
$set = $journal->transactions()->where('amount', '<', 0)->get();
|
||||
if ($set->count() != 1) {
|
||||
throw new FireflyException(
|
||||
sprintf('Journal #%d has an unexpected (%d) amount of transactions with an amount less than zero.', $journal->id, $set->count())
|
||||
);
|
||||
}
|
||||
/** @var Transaction $transaction */
|
||||
$transaction = $set->first();
|
||||
$transaction->amount = $amount;
|
||||
$transaction->account_id = $account->id;
|
||||
$transaction->save();
|
||||
|
||||
|
||||
}
|
||||
|
@ -13,11 +13,7 @@ declare(strict_types = 1);
|
||||
|
||||
namespace FireflyIII\Repositories\Journal;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use FireflyIII\Models\Transaction;
|
||||
use FireflyIII\Models\TransactionJournal;
|
||||
use Illuminate\Pagination\LengthAwarePaginator;
|
||||
use Illuminate\Support\Collection;
|
||||
|
||||
/**
|
||||
* Interface JournalRepositoryInterface
|
||||
@ -52,34 +48,6 @@ interface JournalRepositoryInterface
|
||||
*/
|
||||
public function first(): TransactionJournal;
|
||||
|
||||
/**
|
||||
* Returns a page of a specific type(s) of journal.
|
||||
*
|
||||
* @param array $types
|
||||
* @param int $page
|
||||
* @param int $pageSize
|
||||
*
|
||||
* @return LengthAwarePaginator
|
||||
*/
|
||||
public function getJournals(array $types, int $page, int $pageSize = 50): LengthAwarePaginator;
|
||||
|
||||
/**
|
||||
* Returns a collection of ALL journals, given a specific account and a date range.
|
||||
*
|
||||
* @param Collection $accounts
|
||||
* @param Carbon $start
|
||||
* @param Carbon $end
|
||||
*
|
||||
* @return Collection
|
||||
*/
|
||||
public function getJournalsInRange(Collection $accounts, Carbon $start, Carbon $end): Collection;
|
||||
|
||||
/**
|
||||
* @param TransactionJournal $journal
|
||||
*
|
||||
* @return Collection
|
||||
*/
|
||||
public function getPiggyBankEvents(TransactionJournal $journal): Collection;
|
||||
|
||||
/**
|
||||
* @param array $data
|
||||
|
@ -13,6 +13,7 @@ declare(strict_types = 1);
|
||||
|
||||
namespace FireflyIII\Repositories\Journal;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use Crypt;
|
||||
use DB;
|
||||
use FireflyIII\Models\Transaction;
|
||||
@ -20,6 +21,8 @@ use FireflyIII\Models\TransactionJournal;
|
||||
use FireflyIII\User;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Database\Query\JoinClause;
|
||||
use Illuminate\Pagination\LengthAwarePaginator;
|
||||
use Illuminate\Support\Collection;
|
||||
|
||||
/**
|
||||
* Class JournalTasker
|
||||
@ -42,6 +45,91 @@ class JournalTasker implements JournalTaskerInterface
|
||||
$this->user = $user;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a page of a specific type(s) of journal.
|
||||
*
|
||||
* @param array $types
|
||||
* @param int $page
|
||||
* @param int $pageSize
|
||||
*
|
||||
* @return LengthAwarePaginator
|
||||
*/
|
||||
public function getJournals(array $types, int $page, int $pageSize = 50): LengthAwarePaginator
|
||||
{
|
||||
$offset = ($page - 1) * $pageSize;
|
||||
$query = $this->user->transactionJournals()->expanded()->sortCorrectly();
|
||||
$query->where('transaction_journals.completed', 1);
|
||||
if (count($types) > 0) {
|
||||
$query->transactionTypes($types);
|
||||
}
|
||||
$count = $this->user->transactionJournals()->transactionTypes($types)->count();
|
||||
$set = $query->take($pageSize)->offset($offset)->get(TransactionJournal::queryFields());
|
||||
$journals = new LengthAwarePaginator($set, $count, $pageSize, $page);
|
||||
|
||||
return $journals;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a collection of ALL journals, given a specific account and a date range.
|
||||
*
|
||||
* @param Collection $accounts
|
||||
* @param Carbon $start
|
||||
* @param Carbon $end
|
||||
*
|
||||
* @return Collection
|
||||
*/
|
||||
public function getJournalsInRange(Collection $accounts, Carbon $start, Carbon $end): Collection
|
||||
{
|
||||
$query = $this->user->transactionJournals()->expanded()->sortCorrectly();
|
||||
$query->where('transaction_journals.completed', 1);
|
||||
$query->before($end);
|
||||
$query->after($start);
|
||||
|
||||
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);
|
||||
$q->orWhereIn('source.account_id', $ids);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
$set = $query->get(TransactionJournal::queryFields());
|
||||
|
||||
return $set;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param TransactionJournal $journal
|
||||
*
|
||||
* @return Collection
|
||||
*/
|
||||
public function getPiggyBankEvents(TransactionJournal $journal): Collection
|
||||
{
|
||||
/** @var Collection $set */
|
||||
$events = $journal->piggyBankEvents()->get();
|
||||
$events->each(
|
||||
function (PiggyBankEvent $event) {
|
||||
$event->piggyBank = $event->piggyBank()->withTrashed()->first();
|
||||
}
|
||||
);
|
||||
|
||||
return $events;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an overview of the transactions of a journal, tailored to the view
|
||||
* that shows a transaction (transaction/show/xx).
|
||||
@ -138,7 +226,6 @@ class JournalTasker implements JournalTaskerInterface
|
||||
return $transactions;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Collect the balance of an account before the given transaction has hit. This is tricky, because
|
||||
* the balance does not depend on the transaction itself but the journal it's part of. And of course
|
||||
|
@ -14,7 +14,10 @@ declare(strict_types = 1);
|
||||
namespace FireflyIII\Repositories\Journal;
|
||||
|
||||
|
||||
use Carbon\Carbon;
|
||||
use FireflyIII\Models\TransactionJournal;
|
||||
use Illuminate\Pagination\LengthAwarePaginator;
|
||||
use Illuminate\Support\Collection;
|
||||
|
||||
/**
|
||||
* Interface JournalTaskerInterface
|
||||
@ -23,6 +26,34 @@ use FireflyIII\Models\TransactionJournal;
|
||||
*/
|
||||
interface JournalTaskerInterface
|
||||
{
|
||||
/**
|
||||
* Returns a page of a specific type(s) of journal.
|
||||
*
|
||||
* @param array $types
|
||||
* @param int $page
|
||||
* @param int $pageSize
|
||||
*
|
||||
* @return LengthAwarePaginator
|
||||
*/
|
||||
public function getJournals(array $types, int $page, int $pageSize = 50): LengthAwarePaginator;
|
||||
|
||||
/**
|
||||
* Returns a collection of ALL journals, given a specific account and a date range.
|
||||
*
|
||||
* @param Collection $accounts
|
||||
* @param Carbon $start
|
||||
* @param Carbon $end
|
||||
*
|
||||
* @return Collection
|
||||
*/
|
||||
public function getJournalsInRange(Collection $accounts, Carbon $start, Carbon $end): Collection;
|
||||
|
||||
/**
|
||||
* @param TransactionJournal $journal
|
||||
*
|
||||
* @return Collection
|
||||
*/
|
||||
public function getPiggyBankEvents(TransactionJournal $journal): Collection;
|
||||
|
||||
/**
|
||||
* Get an overview of the transactions of a journal, tailored to the view
|
||||
|
@ -15,7 +15,7 @@ namespace FireflyIII\Rules;
|
||||
|
||||
use FireflyIII\Models\TransactionJournal;
|
||||
use FireflyIII\Models\TransactionType;
|
||||
use FireflyIII\Repositories\Journal\JournalRepositoryInterface;
|
||||
use FireflyIII\Repositories\Journal\JournalTaskerInterface;
|
||||
use Illuminate\Support\Collection;
|
||||
use Log;
|
||||
|
||||
@ -31,8 +31,8 @@ class TransactionMatcher
|
||||
private $limit = 10;
|
||||
/** @var int Maximum number of transaction to search in (for performance reasons) * */
|
||||
private $range = 200;
|
||||
/** @var JournalRepositoryInterface */
|
||||
private $repository;
|
||||
/** @var JournalTaskerInterface */
|
||||
private $tasker;
|
||||
/** @var array */
|
||||
private $transactionTypes = [TransactionType::DEPOSIT, TransactionType::WITHDRAWAL, TransactionType::TRANSFER];
|
||||
/** @var array List of triggers to match */
|
||||
@ -41,11 +41,11 @@ class TransactionMatcher
|
||||
/**
|
||||
* TransactionMatcher constructor. Typehint the repository.
|
||||
*
|
||||
* @param JournalRepositoryInterface $repository
|
||||
* @param JournalTaskerInterface $tasker
|
||||
*/
|
||||
public function __construct(JournalRepositoryInterface $repository)
|
||||
public function __construct(JournalTaskerInterface $tasker)
|
||||
{
|
||||
$this->repository = $repository;
|
||||
$this->tasker = $tasker;
|
||||
|
||||
}
|
||||
|
||||
@ -76,7 +76,7 @@ class TransactionMatcher
|
||||
// - the maximum number of transactions to search in have been searched
|
||||
do {
|
||||
// Fetch a batch of transactions from the database
|
||||
$paginator = $this->repository->getJournals($this->transactionTypes, $page, $pagesize);
|
||||
$paginator = $this->tasker->getJournals($this->transactionTypes, $page, $pagesize);
|
||||
$set = $paginator->getCollection();
|
||||
|
||||
|
||||
|
@ -427,6 +427,7 @@ class ExpandedForm
|
||||
*/
|
||||
protected function expandOptionArray(string $name, $label, array $options): array
|
||||
{
|
||||
$name = str_replace('[]', '', $name);
|
||||
$options['class'] = 'form-control';
|
||||
$options['id'] = 'ffInput_' . $name;
|
||||
$options['autocomplete'] = 'off';
|
||||
@ -494,6 +495,7 @@ class ExpandedForm
|
||||
if (isset($options['label'])) {
|
||||
return $options['label'];
|
||||
}
|
||||
$name = str_replace('[]', '', $name);
|
||||
|
||||
return strval(trans('form.' . $name));
|
||||
|
||||
|
188
app/Support/ExpandedMultiForm.php
Normal file
188
app/Support/ExpandedMultiForm.php
Normal file
@ -0,0 +1,188 @@
|
||||
<?php
|
||||
/**
|
||||
* ExpandedMultiForm.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\Support;
|
||||
|
||||
use Amount as Amt;
|
||||
use Carbon\Carbon;
|
||||
use Illuminate\Support\MessageBag;
|
||||
use Input;
|
||||
use RuntimeException;
|
||||
use Session;
|
||||
|
||||
/**
|
||||
* Class ExpandedMultiForm
|
||||
*
|
||||
* @package FireflyIII\Support
|
||||
*/
|
||||
class ExpandedMultiForm
|
||||
{
|
||||
|
||||
/**
|
||||
* @param string $name
|
||||
* @param int $index
|
||||
* @param null $value
|
||||
* @param array $options
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function amount(string $name, int $index, $value = null, array $options = []): string
|
||||
{
|
||||
$label = $this->label($name, $options);
|
||||
$options = $this->expandOptionArray($name, $index, $label, $options);
|
||||
$classes = $this->getHolderClasses($name, $index);
|
||||
$value = $this->fillFieldValue($name, $index, $value);
|
||||
$options['step'] = 'any';
|
||||
$options['min'] = '0.01';
|
||||
$defaultCurrency = isset($options['currency']) ? $options['currency'] : Amt::getDefaultCurrency();
|
||||
$currencies = Amt::getAllCurrencies();
|
||||
$options['data-hiddenfield'] = 'amount_currency_id_' . $name . '_' . $index;
|
||||
unset($options['currency']);
|
||||
unset($options['placeholder']);
|
||||
$html = view('form.multi.amount', compact('defaultCurrency', 'index', 'currencies', 'classes', 'name', 'label', 'value', 'options'))->render();
|
||||
|
||||
return $html;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $name
|
||||
* @param int $index
|
||||
* @param array $list
|
||||
* @param null $selected
|
||||
* @param array $options
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function select(string $name, int $index, array $list = [], $selected = null, array $options = []): string
|
||||
{
|
||||
$label = $this->label($name, $options);
|
||||
$options = $this->expandOptionArray($name, $index, $label, $options);
|
||||
$classes = $this->getHolderClasses($name, $index);
|
||||
$selected = $this->fillFieldValue($name, $index, $selected);
|
||||
unset($options['autocomplete']);
|
||||
unset($options['placeholder']);
|
||||
$html = view('form.multi.select', compact('classes', 'index', 'name', 'label', 'selected', 'options', 'list'))->render();
|
||||
|
||||
return $html;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $name
|
||||
* @param int $index
|
||||
* @param null $value
|
||||
* @param array $options
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function text(string $name, int $index, $value = null, array $options = []): string
|
||||
{
|
||||
$label = $this->label($name, $options);
|
||||
$options = $this->expandOptionArray($name, $index, $label, $options);
|
||||
$classes = $this->getHolderClasses($name, $index);
|
||||
$value = $this->fillFieldValue($name, $index, $value);
|
||||
$html = view('form.multi.text', compact('classes', 'name', 'index', 'label', 'value', 'options'))->render();
|
||||
|
||||
return $html;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $name
|
||||
* @param int $index
|
||||
* @param string $label
|
||||
* @param array $options
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function expandOptionArray(string $name, int $index, string $label, array $options): array
|
||||
{
|
||||
$options['class'] = 'form-control';
|
||||
$options['id'] = 'ffInput_' . $name . '_' . $index;
|
||||
$options['autocomplete'] = 'off';
|
||||
$options['placeholder'] = ucfirst($label);
|
||||
|
||||
return $options;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $name
|
||||
* @param int $index
|
||||
* @param $value
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
protected function fillFieldValue(string $name, int $index, $value)
|
||||
{
|
||||
if (Session::has('preFilled')) {
|
||||
$preFilled = session('preFilled');
|
||||
$value = isset($preFilled[$name][$index]) && is_null($value) ? $preFilled[$name][$index] : $value;
|
||||
}
|
||||
try {
|
||||
if (!is_null(Input::old($name)[$index])) {
|
||||
$value = Input::old($name)[$index];
|
||||
}
|
||||
} catch (RuntimeException $e) {
|
||||
// don't care about session errors.
|
||||
}
|
||||
if ($value instanceof Carbon) {
|
||||
$value = $value->format('Y-m-d');
|
||||
}
|
||||
|
||||
|
||||
return $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $name
|
||||
* @param int $index
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function getHolderClasses(string $name, int $index): string
|
||||
{
|
||||
/*
|
||||
* Get errors from session:
|
||||
*/
|
||||
/** @var MessageBag $errors */
|
||||
$errors = session('errors');
|
||||
$classes = 'form-group';
|
||||
$set = [];
|
||||
|
||||
if (!is_null($errors)) {
|
||||
$set = $errors->get($name . '.' . $index);
|
||||
}
|
||||
|
||||
if (!is_null($errors) && count($set) > 0) {
|
||||
$classes = 'form-group has-error has-feedback';
|
||||
}
|
||||
|
||||
return $classes;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $name
|
||||
* @param array $options
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function label(string $name, array $options): string
|
||||
{
|
||||
if (isset($options['label'])) {
|
||||
return $options['label'];
|
||||
}
|
||||
|
||||
return strval(trans('form.' . $name));
|
||||
|
||||
}
|
||||
}
|
35
app/Support/Facades/ExpandedMultiForm.php
Normal file
35
app/Support/Facades/ExpandedMultiForm.php
Normal file
@ -0,0 +1,35 @@
|
||||
<?php
|
||||
/**
|
||||
* ExpandedMultiForm.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\Support\Facades;
|
||||
|
||||
use Illuminate\Support\Facades\Facade;
|
||||
|
||||
/**
|
||||
* Class Amount
|
||||
*
|
||||
* @package FireflyIII\Support\Facades
|
||||
*/
|
||||
class ExpandedMultiForm extends Facade
|
||||
{
|
||||
/**
|
||||
* Get the registered name of the component.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected static function getFacadeAccessor(): string
|
||||
{
|
||||
return 'expandedmultiform';
|
||||
}
|
||||
|
||||
}
|
@ -124,6 +124,28 @@ class FireflyValidator extends Validator
|
||||
return (intval($checksum) === 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $attribute
|
||||
* @param $value
|
||||
* @param $parameters
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function validateMustExist($attribute, $value, $parameters): bool
|
||||
{
|
||||
$field = $parameters[1] ?? 'id';
|
||||
|
||||
if (intval($value) === 0) {
|
||||
return true;
|
||||
}
|
||||
$count = DB::table($parameters[0])->where($field, $value)->count();
|
||||
if ($count === 1) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $attribute
|
||||
*
|
||||
|
@ -188,8 +188,8 @@ return [
|
||||
|
||||
|
||||
// own stuff:
|
||||
//Barryvdh\LaravelIdeHelper\IdeHelperServiceProvider::class,
|
||||
//Barryvdh\Debugbar\ServiceProvider::class,
|
||||
Barryvdh\LaravelIdeHelper\IdeHelperServiceProvider::class,
|
||||
Barryvdh\Debugbar\ServiceProvider::class,
|
||||
DaveJamesMiller\Breadcrumbs\ServiceProvider::class,
|
||||
TwigBridge\ServiceProvider::class,
|
||||
'PragmaRX\Google2FA\Vendor\Laravel\ServiceProvider',
|
||||
@ -226,50 +226,51 @@ return [
|
||||
|
||||
'aliases' => [
|
||||
|
||||
'App' => Illuminate\Support\Facades\App::class,
|
||||
'Artisan' => Illuminate\Support\Facades\Artisan::class,
|
||||
'Auth' => Illuminate\Support\Facades\Auth::class,
|
||||
'Blade' => Illuminate\Support\Facades\Blade::class,
|
||||
'Cache' => Illuminate\Support\Facades\Cache::class,
|
||||
'Config' => Illuminate\Support\Facades\Config::class,
|
||||
'Cookie' => Illuminate\Support\Facades\Cookie::class,
|
||||
'Crypt' => Illuminate\Support\Facades\Crypt::class,
|
||||
'DB' => Illuminate\Support\Facades\DB::class,
|
||||
'Eloquent' => Illuminate\Database\Eloquent\Model::class,
|
||||
'Event' => Illuminate\Support\Facades\Event::class,
|
||||
'File' => Illuminate\Support\Facades\File::class,
|
||||
'Gate' => Illuminate\Support\Facades\Gate::class,
|
||||
'Hash' => Illuminate\Support\Facades\Hash::class,
|
||||
'Lang' => Illuminate\Support\Facades\Lang::class,
|
||||
'Log' => Illuminate\Support\Facades\Log::class,
|
||||
'Mail' => Illuminate\Support\Facades\Mail::class,
|
||||
'Notification' => Illuminate\Support\Facades\Notification::class,
|
||||
'Password' => Illuminate\Support\Facades\Password::class,
|
||||
'Queue' => Illuminate\Support\Facades\Queue::class,
|
||||
'Redirect' => Illuminate\Support\Facades\Redirect::class,
|
||||
'Redis' => Illuminate\Support\Facades\Redis::class,
|
||||
'Request' => Illuminate\Support\Facades\Request::class,
|
||||
'Response' => Illuminate\Support\Facades\Response::class,
|
||||
'Route' => Illuminate\Support\Facades\Route::class,
|
||||
'Schema' => Illuminate\Support\Facades\Schema::class,
|
||||
'Session' => Illuminate\Support\Facades\Session::class,
|
||||
'Storage' => Illuminate\Support\Facades\Storage::class,
|
||||
'URL' => Illuminate\Support\Facades\URL::class,
|
||||
'Validator' => Illuminate\Support\Facades\Validator::class,
|
||||
'View' => Illuminate\Support\Facades\View::class,
|
||||
'Twig' => 'TwigBridge\Facade\Twig',
|
||||
'Form' => Collective\Html\FormFacade::class,
|
||||
'Html' => Collective\Html\HtmlFacade::class,
|
||||
'Breadcrumbs' => 'DaveJamesMiller\Breadcrumbs\Facade',
|
||||
'Preferences' => 'FireflyIII\Support\Facades\Preferences',
|
||||
'FireflyConfig' => 'FireflyIII\Support\Facades\FireflyConfig',
|
||||
'Navigation' => 'FireflyIII\Support\Facades\Navigation',
|
||||
'Amount' => 'FireflyIII\Support\Facades\Amount',
|
||||
'Steam' => 'FireflyIII\Support\Facades\Steam',
|
||||
'ExpandedForm' => 'FireflyIII\Support\Facades\ExpandedForm',
|
||||
'Entrust' => 'Zizaco\Entrust\EntrustFacade',
|
||||
'Input' => 'Illuminate\Support\Facades\Input',
|
||||
'Google2FA' => 'PragmaRX\Google2FA\Vendor\Laravel\Facade',
|
||||
'App' => Illuminate\Support\Facades\App::class,
|
||||
'Artisan' => Illuminate\Support\Facades\Artisan::class,
|
||||
'Auth' => Illuminate\Support\Facades\Auth::class,
|
||||
'Blade' => Illuminate\Support\Facades\Blade::class,
|
||||
'Cache' => Illuminate\Support\Facades\Cache::class,
|
||||
'Config' => Illuminate\Support\Facades\Config::class,
|
||||
'Cookie' => Illuminate\Support\Facades\Cookie::class,
|
||||
'Crypt' => Illuminate\Support\Facades\Crypt::class,
|
||||
'DB' => Illuminate\Support\Facades\DB::class,
|
||||
'Eloquent' => Illuminate\Database\Eloquent\Model::class,
|
||||
'Event' => Illuminate\Support\Facades\Event::class,
|
||||
'File' => Illuminate\Support\Facades\File::class,
|
||||
'Gate' => Illuminate\Support\Facades\Gate::class,
|
||||
'Hash' => Illuminate\Support\Facades\Hash::class,
|
||||
'Lang' => Illuminate\Support\Facades\Lang::class,
|
||||
'Log' => Illuminate\Support\Facades\Log::class,
|
||||
'Mail' => Illuminate\Support\Facades\Mail::class,
|
||||
'Notification' => Illuminate\Support\Facades\Notification::class,
|
||||
'Password' => Illuminate\Support\Facades\Password::class,
|
||||
'Queue' => Illuminate\Support\Facades\Queue::class,
|
||||
'Redirect' => Illuminate\Support\Facades\Redirect::class,
|
||||
'Redis' => Illuminate\Support\Facades\Redis::class,
|
||||
'Request' => Illuminate\Support\Facades\Request::class,
|
||||
'Response' => Illuminate\Support\Facades\Response::class,
|
||||
'Route' => Illuminate\Support\Facades\Route::class,
|
||||
'Schema' => Illuminate\Support\Facades\Schema::class,
|
||||
'Session' => Illuminate\Support\Facades\Session::class,
|
||||
'Storage' => Illuminate\Support\Facades\Storage::class,
|
||||
'URL' => Illuminate\Support\Facades\URL::class,
|
||||
'Validator' => Illuminate\Support\Facades\Validator::class,
|
||||
'View' => Illuminate\Support\Facades\View::class,
|
||||
'Twig' => 'TwigBridge\Facade\Twig',
|
||||
'Form' => Collective\Html\FormFacade::class,
|
||||
'Html' => Collective\Html\HtmlFacade::class,
|
||||
'Breadcrumbs' => 'DaveJamesMiller\Breadcrumbs\Facade',
|
||||
'Preferences' => 'FireflyIII\Support\Facades\Preferences',
|
||||
'FireflyConfig' => 'FireflyIII\Support\Facades\FireflyConfig',
|
||||
'Navigation' => 'FireflyIII\Support\Facades\Navigation',
|
||||
'Amount' => 'FireflyIII\Support\Facades\Amount',
|
||||
'Steam' => 'FireflyIII\Support\Facades\Steam',
|
||||
'ExpandedForm' => 'FireflyIII\Support\Facades\ExpandedForm',
|
||||
'ExpandedMultiForm' => 'FireflyIII\Support\Facades\ExpandedMultiForm',
|
||||
'Entrust' => 'Zizaco\Entrust\EntrustFacade',
|
||||
'Input' => 'Illuminate\Support\Facades\Input',
|
||||
'Google2FA' => 'PragmaRX\Google2FA\Vendor\Laravel\Facade',
|
||||
|
||||
|
||||
],
|
||||
|
@ -162,6 +162,11 @@ return [
|
||||
'multiRadio', 'file', 'multiCheckbox', 'staticText', 'amountSmall',
|
||||
],
|
||||
],
|
||||
'ExpandedMultiForm' => [
|
||||
'is_safe' => [
|
||||
'text','select','amount'
|
||||
],
|
||||
],
|
||||
'Form' => [
|
||||
'is_safe' => [
|
||||
'input', 'select', 'checkbox', 'model', 'open', 'radio', 'textarea', 'file',
|
||||
|
@ -5,6 +5,9 @@ $(function () {
|
||||
// when you click on a currency, this happens:
|
||||
$('.currency-option').click(currencySelect);
|
||||
|
||||
// when you click on a multi currency, this happens:
|
||||
$('.multi-currency-option').click(multiCurrencySelect);
|
||||
|
||||
var ranges = {};
|
||||
ranges[dateRangeConfig.currentPeriod] = [moment(dateRangeConfig.ranges.current[0]), moment(dateRangeConfig.ranges.current[1])];
|
||||
ranges[dateRangeConfig.previousPeriod] = [moment(dateRangeConfig.ranges.previous[0]), moment(dateRangeConfig.ranges.previous[1])];
|
||||
@ -57,6 +60,47 @@ $(function () {
|
||||
|
||||
});
|
||||
|
||||
function multiCurrencySelect(e) {
|
||||
"use strict";
|
||||
// clicked on
|
||||
var target = $(e.target); // target is the <A> tag.
|
||||
|
||||
// name of the field in question:
|
||||
var name = target.data('name');
|
||||
|
||||
// index of the field in question:
|
||||
var index = target.data('index');
|
||||
console.log('name is ' + name + ':' + index);
|
||||
|
||||
// id of menu button (used later on):
|
||||
var menuID = 'currency_dropdown_' + name + '_' + index;
|
||||
|
||||
// the hidden input with the actual value of the selected currency:
|
||||
var hiddenInputName = 'amount_currency_id_' + name + '_' + index;
|
||||
console.log('Looking for hidden input: ' + hiddenInputName);
|
||||
|
||||
// span with the current selection (next to the caret):
|
||||
var spanId = 'currency_select_symbol_' + name + '_' + index;
|
||||
|
||||
// the selected currency symbol:
|
||||
var symbol = target.data('symbol');
|
||||
|
||||
// id of the selected currency.
|
||||
var id = target.data('id');
|
||||
|
||||
// update the hidden input:
|
||||
$('input[name="' + hiddenInputName + '"]').val(id);
|
||||
|
||||
// update the symbol:
|
||||
$('#' + spanId).text(symbol);
|
||||
|
||||
// close the menu (hack hack)
|
||||
$('#' + menuID).click();
|
||||
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
function currencySelect(e) {
|
||||
"use strict";
|
||||
// clicked on
|
||||
|
@ -2,7 +2,12 @@
|
||||
<label for="{{ options.id }}" class="col-sm-4 control-label">{{ label }}</label>
|
||||
|
||||
<div class="col-sm-8">
|
||||
{{ Form.input('date', name, value, options) }}
|
||||
<div class="input-group">
|
||||
<div class="input-group-addon">
|
||||
<i class="fa fa-calendar"></i>
|
||||
</div>
|
||||
{{ Form.input('date', name, value, options) }}
|
||||
</div>
|
||||
{% include 'form/help.twig' %}
|
||||
{% include 'form/feedback.twig' %}
|
||||
</div>
|
||||
|
30
resources/views/form/multi/amount.twig
Normal file
30
resources/views/form/multi/amount.twig
Normal file
@ -0,0 +1,30 @@
|
||||
<div class="{{ classes }}" id="{{ name }}_{{ index }}_holder">
|
||||
<label for="{{ options.id }}" class="col-sm-4 control-label">{{ label }}</label>
|
||||
<div class="col-sm-8">
|
||||
<div class="input-group">
|
||||
<div class="input-group-btn">
|
||||
<button type="button"
|
||||
class="btn btn-default dropdown-toggle currency-dropdown" id="currency_dropdown_{{ name }}_{{ index }}" data-toggle="dropdown"
|
||||
aria-expanded="false">
|
||||
<span id="currency_select_symbol_{{ name }}_{{ index }}">{{ defaultCurrency.symbol|raw }}</span> <span class="caret"></span>
|
||||
</button>
|
||||
<ul class="dropdown-menu currency-dropdown-menu" role="menu">
|
||||
{% for currency in currencies %}
|
||||
<li>
|
||||
<a href="#"
|
||||
class="multi-currency-option"
|
||||
data-id="{{ currency.id }}"
|
||||
data-name="{{ name }}"
|
||||
data-index="{{ index }}"
|
||||
data-currency="{{ currency.code }}"
|
||||
data-symbol="{{ currency.symbol|raw }}">{{ currency.name }}</a></li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
{{ Form.input('number', name~'['~index~']', value, options) }}
|
||||
</div>
|
||||
{% include 'form.multi.feedback.twig' %}
|
||||
</div>
|
||||
|
||||
<input type="hidden" name="amount_currency_id_{{ name }}_{{ index }}" value="{{ defaultCurrency.id }}"/>
|
||||
</div>
|
4
resources/views/form/multi/feedback.twig
Normal file
4
resources/views/form/multi/feedback.twig
Normal file
@ -0,0 +1,4 @@
|
||||
{% if errors.has(name~'.'~index) %}
|
||||
<span class="form-control-feedback"><i class="fa fa-fw fa-remove"></i></span>
|
||||
<p class="text-danger">{{ errors.first(name~'.'~index) }}</p>
|
||||
{% endif %}
|
10
resources/views/form/multi/select.twig
Normal file
10
resources/views/form/multi/select.twig
Normal file
@ -0,0 +1,10 @@
|
||||
<div class="{{ classes }}" id="{{ name }}_{{ index }}_holder">
|
||||
<label for="{{ options.id }}" class="col-sm-4 control-label">{{ label }}</label>
|
||||
|
||||
<div class="col-sm-8">
|
||||
{{ Form.select(name~'['~index~']', list, selected , options ) }}
|
||||
{% include 'form.help.twig' %}
|
||||
{% include 'form.multi.feedback.twig' %}
|
||||
|
||||
</div>
|
||||
</div>
|
9
resources/views/form/multi/text.twig
Normal file
9
resources/views/form/multi/text.twig
Normal file
@ -0,0 +1,9 @@
|
||||
<div class="{{ classes }}" id="{{ name }}_{{ index }}_holder">
|
||||
<label for="{{ options.id }}" class="col-sm-4 control-label">{{ label }}</label>
|
||||
|
||||
<div class="col-sm-8">
|
||||
{{ Form.input('text', name~'['~index~']', value, options) }}
|
||||
{% include 'form/help.twig' %}
|
||||
{% include 'form.multi.feedback.twig' %}
|
||||
</div>
|
||||
</div>
|
@ -29,23 +29,22 @@
|
||||
</div>
|
||||
|
||||
|
||||
<!-- DESCRIPTION ALWAYS AVAILABLE -->
|
||||
{# DESCRIPTION IS ALWAYS AVAILABLE #}
|
||||
{{ ExpandedForm.text('description') }}
|
||||
|
||||
<!-- SELECTABLE SOURCE ACCOUNT ONLY FOR WITHDRAWALS AND TRANSFERS -->
|
||||
{{ ExpandedForm.select('source_account_id',assetAccounts, null, {label: trans('form.asset_source_account')}) }}
|
||||
{# SELECTABLE SOURCE ACCOUNT ONLY FOR WITHDRAWALS AND TRANSFERS #}
|
||||
{{ ExpandedForm.select('source_account_id', assetAccounts, null, {label: trans('form.asset_source_account')}) }}
|
||||
|
||||
<!-- FREE FORMAT SOURCE ACCOUNT ONLY FOR DEPOSITS -->
|
||||
{{ ExpandedForm.text('source_account_name',null, {label: trans('form.revenue_account')}) }}
|
||||
{# FREE FORMAT SOURCE ACCOUNT ONLY FOR DEPOSITS #}
|
||||
{{ ExpandedForm.text('source_account_name', null, {label: trans('form.revenue_account')}) }}
|
||||
|
||||
<!-- FREE FORMAT DESTINATION ACCOUNT ONLY FOR EXPENSES -->
|
||||
{{ ExpandedForm.text('destination_account_name',null, {label: trans('form.expense_account')}) }}
|
||||
{# FREE FORMAT DESTINATION ACCOUNT ONLY FOR EXPENSES #}
|
||||
{{ ExpandedForm.text('destination_account_name', null, {label: trans('form.expense_account')}) }}
|
||||
|
||||
<!-- SELECTABLE DESTINATION ACCOUNT ONLY FOR TRANSFERS AND DEPOSITS -->
|
||||
{{ ExpandedForm.select('destination_account_id',assetAccounts, null, {label: trans('form.asset_destination_account')} ) }}
|
||||
{# SELECTABLE DESTINATION ACCOUNT ONLY FOR TRANSFERS AND DEPOSITS #}
|
||||
{{ ExpandedForm.select('destination_account_id', assetAccounts, null, {label: trans('form.asset_destination_account')} ) }}
|
||||
|
||||
|
||||
<!-- ALWAYS SHOW AMOUNT -->
|
||||
{# ALWAYS SHOW AMOUNT #}
|
||||
{{ ExpandedForm.amount('amount') }}
|
||||
|
||||
<!-- ALWAYS SHOW DATE -->
|
||||
@ -66,9 +65,9 @@
|
||||
<div class="box-body">
|
||||
<!-- BUDGET ONLY WHEN CREATING A WITHDRAWAL -->
|
||||
{% if budgets|length > 1 %}
|
||||
{{ ExpandedForm.select('budget_id',budgets,0) }}
|
||||
{{ ExpandedForm.select('budget_id', budgets, 0) }}
|
||||
{% else %}
|
||||
{{ ExpandedForm.select('budget_id',budgets,0, {helpText: trans('firefly.no_budget_pointer')}) }}
|
||||
{{ ExpandedForm.select('budget_id', budgets, 0, {helpText: trans('firefly.no_budget_pointer')}) }}
|
||||
{% endif %}
|
||||
|
||||
<!-- CATEGORY ALWAYS -->
|
||||
@ -78,7 +77,7 @@
|
||||
{{ ExpandedForm.text('tags') }}
|
||||
|
||||
<!-- RELATE THIS TRANSFER TO A PIGGY BANK -->
|
||||
{{ ExpandedForm.select('piggy_bank_id',piggies) }}
|
||||
{{ ExpandedForm.select('piggy_bank_id', piggies) }}
|
||||
|
||||
</div>
|
||||
</div>
|
||||
@ -108,33 +107,33 @@
|
||||
</div>
|
||||
<div class="box-body">
|
||||
|
||||
{# INTEREST DATE #}
|
||||
{% if optionalFields.interest_date %}
|
||||
<!-- INTEREST DATE -->
|
||||
{{ ExpandedForm.date('interest_date') }}
|
||||
{% endif %}
|
||||
|
||||
{# BOOK DATE #}
|
||||
{% if optionalFields.book_date %}
|
||||
<!-- BOOK DATE -->
|
||||
{{ ExpandedForm.date('book_date') }}
|
||||
{% endif %}
|
||||
|
||||
{# PROCESSING DATE #}
|
||||
{% if optionalFields.process_date %}
|
||||
<!-- PROCESSING DATE -->
|
||||
{{ ExpandedForm.date('process_date') }}
|
||||
{% endif %}
|
||||
|
||||
{# DUE DATE #}
|
||||
{% if optionalFields.due_date %}
|
||||
<!-- DUE DATE -->
|
||||
{{ ExpandedForm.date('due_date') }}
|
||||
{% endif %}
|
||||
|
||||
{# PAYMENT DATE #}
|
||||
{% if optionalFields.payment_date %}
|
||||
<!-- PAYMENT DATE -->
|
||||
{{ ExpandedForm.date('payment_date') }}
|
||||
{% endif %}
|
||||
|
||||
{# INVOICE DATE #}
|
||||
{% if optionalFields.invoice_date %}
|
||||
<!-- INVOICE DATE -->
|
||||
{{ ExpandedForm.date('invoice_date') }}
|
||||
{% endif %}
|
||||
|
||||
@ -149,13 +148,14 @@
|
||||
<h3 class="box-title">{{ 'optional_field_meta_business'|_ }}</h3>
|
||||
</div>
|
||||
<div class="box-body">
|
||||
|
||||
{# REFERENCE #}
|
||||
{% if optionalFields.internal_reference %}
|
||||
<!-- REFERENCE -->
|
||||
{{ ExpandedForm.text('internal_reference') }}
|
||||
{% endif %}
|
||||
|
||||
{# NOTES #}
|
||||
{% if optionalFields.notes %}
|
||||
<!-- NOTES -->
|
||||
{{ ExpandedForm.textarea('notes') }}
|
||||
{% endif %}
|
||||
|
||||
@ -170,8 +170,8 @@
|
||||
<h3 class="box-title">{{ 'optional_field_attachments'|_ }}</h3>
|
||||
</div>
|
||||
<div class="box-body">
|
||||
{# ATTACHMENTS #}
|
||||
{% if optionalFields.attachments %}
|
||||
<!-- ATTACHMENTS -->
|
||||
{{ ExpandedForm.file('attachments[]', {'multiple': 'multiple','helpText': trans('firefly.upload_max_file_size', {'size': uploadSize|filesize}) }) }}
|
||||
{% endif %}
|
||||
</div>
|
||||
@ -193,6 +193,7 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
#}
|
||||
</div>
|
||||
</form>
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user