mirror of
https://github.com/firefly-iii/firefly-iii.git
synced 2025-09-30 02:26:58 +00:00
Many updates to get split transactions and normal transactions working side by side.
This commit is contained in:
@@ -20,6 +20,7 @@ use FireflyIII\Export\Entry\Entry;
|
|||||||
use FireflyIII\Models\ExportJob;
|
use FireflyIII\Models\ExportJob;
|
||||||
use FireflyIII\Models\TransactionJournal;
|
use FireflyIII\Models\TransactionJournal;
|
||||||
use FireflyIII\Repositories\Journal\JournalRepositoryInterface;
|
use FireflyIII\Repositories\Journal\JournalRepositoryInterface;
|
||||||
|
use FireflyIII\Repositories\Journal\JournalTaskerInterface;
|
||||||
use Illuminate\Filesystem\FilesystemAdapter;
|
use Illuminate\Filesystem\FilesystemAdapter;
|
||||||
use Illuminate\Support\Collection;
|
use Illuminate\Support\Collection;
|
||||||
use Storage;
|
use Storage;
|
||||||
@@ -95,9 +96,9 @@ class Processor
|
|||||||
*/
|
*/
|
||||||
public function collectJournals(): bool
|
public function collectJournals(): bool
|
||||||
{
|
{
|
||||||
/** @var JournalRepositoryInterface $repository */
|
/** @var JournalTaskerInterface $tasker */
|
||||||
$repository = app(JournalRepositoryInterface::class);
|
$tasker = app(JournalTaskerInterface::class);
|
||||||
$this->journals = $repository->getJournalsInRange($this->accounts, $this->settings['startDate'], $this->settings['endDate']);
|
$this->journals = $tasker->getJournalsInRange($this->accounts, $this->settings['startDate'], $this->settings['endDate']);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@@ -20,7 +20,7 @@ use FireflyIII\Repositories\Account\AccountRepositoryInterface;
|
|||||||
use FireflyIII\Repositories\Account\AccountTaskerInterface;
|
use FireflyIII\Repositories\Account\AccountTaskerInterface;
|
||||||
use FireflyIII\Repositories\Bill\BillRepositoryInterface;
|
use FireflyIII\Repositories\Bill\BillRepositoryInterface;
|
||||||
use FireflyIII\Repositories\Category\CategoryRepositoryInterface as CRI;
|
use FireflyIII\Repositories\Category\CategoryRepositoryInterface as CRI;
|
||||||
use FireflyIII\Repositories\Journal\JournalRepositoryInterface;
|
use FireflyIII\Repositories\Journal\JournalTaskerInterface;
|
||||||
use FireflyIII\Repositories\Tag\TagRepositoryInterface;
|
use FireflyIII\Repositories\Tag\TagRepositoryInterface;
|
||||||
use FireflyIII\Support\CacheProperties;
|
use FireflyIII\Support\CacheProperties;
|
||||||
use Input;
|
use Input;
|
||||||
@@ -270,17 +270,17 @@ class JsonController extends Controller
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param JournalRepositoryInterface $repository
|
* @param JournalTaskerInterface $tasker
|
||||||
* @param $what
|
* @param $what
|
||||||
*
|
*
|
||||||
* @return \Symfony\Component\HttpFoundation\Response
|
* @return \Symfony\Component\HttpFoundation\Response
|
||||||
*/
|
*/
|
||||||
public function transactionJournals(JournalRepositoryInterface $repository, $what)
|
public function transactionJournals(JournalTaskerInterface $tasker, $what)
|
||||||
{
|
{
|
||||||
$descriptions = [];
|
$descriptions = [];
|
||||||
$type = config('firefly.transactionTypesByWhat.' . $what);
|
$type = config('firefly.transactionTypesByWhat.' . $what);
|
||||||
$types = [$type];
|
$types = [$type];
|
||||||
$journals = $repository->getJournals($types, 1, 50);
|
$journals = $tasker->getJournals($types, 1, 50);
|
||||||
foreach ($journals as $j) {
|
foreach ($journals as $j) {
|
||||||
$descriptions[] = $j->description;
|
$descriptions[] = $j->description;
|
||||||
}
|
}
|
||||||
|
@@ -23,9 +23,12 @@ use FireflyIII\Models\AccountType;
|
|||||||
use FireflyIII\Models\TransactionJournal;
|
use FireflyIII\Models\TransactionJournal;
|
||||||
use FireflyIII\Models\TransactionType;
|
use FireflyIII\Models\TransactionType;
|
||||||
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
|
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
|
||||||
|
use FireflyIII\Repositories\Budget\BudgetRepositoryInterface;
|
||||||
use FireflyIII\Repositories\Journal\JournalRepositoryInterface;
|
use FireflyIII\Repositories\Journal\JournalRepositoryInterface;
|
||||||
use FireflyIII\Repositories\Journal\JournalTaskerInterface;
|
use FireflyIII\Repositories\Journal\JournalTaskerInterface;
|
||||||
|
use FireflyIII\Repositories\PiggyBank\PiggyBankRepositoryInterface;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
|
use Log;
|
||||||
use Preferences;
|
use Preferences;
|
||||||
use Response;
|
use Response;
|
||||||
use Session;
|
use Session;
|
||||||
@@ -40,6 +43,14 @@ use View;
|
|||||||
*/
|
*/
|
||||||
class TransactionController extends Controller
|
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'));
|
$maxFileSize = Steam::phpBytes(ini_get('upload_max_filesize'));
|
||||||
$maxPostSize = Steam::phpBytes(ini_get('post_max_size'));
|
$maxPostSize = Steam::phpBytes(ini_get('post_max_size'));
|
||||||
$uploadSize = min($maxFileSize, $maxPostSize);
|
$uploadSize = min($maxFileSize, $maxPostSize);
|
||||||
|
|
||||||
View::share('uploadSize', $uploadSize);
|
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,15 +87,11 @@ class TransactionController extends Controller
|
|||||||
*/
|
*/
|
||||||
public function create(string $what = TransactionType::DEPOSIT)
|
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);
|
$what = strtolower($what);
|
||||||
$uploadSize = min(Steam::phpBytes(ini_get('upload_max_filesize')), Steam::phpBytes(ini_get('post_max_size')));
|
$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']));
|
$assetAccounts = ExpandedForm::makeSelectList($this->accounts->getActiveAccountsByType(['Default account', 'Asset account']));
|
||||||
$budgets = ExpandedForm::makeSelectListWithEmpty($budgetRepository->getActiveBudgets());
|
$budgets = ExpandedForm::makeSelectListWithEmpty($this->budgets->getActiveBudgets());
|
||||||
$piggyBanks = $piggyRepository->getPiggyBanksWithAmount();
|
$piggyBanks = $this->piggyBanks->getPiggyBanksWithAmount();
|
||||||
$piggies = ExpandedForm::makeSelectListWithEmpty($piggyBanks);
|
$piggies = ExpandedForm::makeSelectListWithEmpty($piggyBanks);
|
||||||
$preFilled = Session::has('preFilled') ? session('preFilled') : [];
|
$preFilled = Session::has('preFilled') ? session('preFilled') : [];
|
||||||
$subTitle = trans('form.add_new_' . $what);
|
$subTitle = trans('form.add_new_' . $what);
|
||||||
@@ -91,7 +111,6 @@ class TransactionController extends Controller
|
|||||||
|
|
||||||
asort($piggies);
|
asort($piggies);
|
||||||
|
|
||||||
|
|
||||||
return view('transactions.create', compact('assetAccounts', 'subTitleIcon', 'uploadSize', 'budgets', 'what', 'piggies', 'subTitle', 'optionalFields'));
|
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();
|
$count = $journal->transactions()->count();
|
||||||
if ($count > 2) {
|
if ($count > 2) {
|
||||||
return redirect(route('split.journal.edit', [$journal->id]));
|
return redirect(route('journal.edit-split', [$journal->id]));
|
||||||
}
|
}
|
||||||
|
|
||||||
// code to get list data:
|
$assetAccounts = ExpandedForm::makeSelectList($this->accounts->getAccountsByType(['Default account', 'Asset account']));
|
||||||
$budgetRepository = app('FireflyIII\Repositories\Budget\BudgetRepositoryInterface');
|
$budgetList = ExpandedForm::makeSelectListWithEmpty($this->budgets->getActiveBudgets());
|
||||||
$piggyRepository = app('FireflyIII\Repositories\PiggyBank\PiggyBankRepositoryInterface');
|
$piggyBankList = ExpandedForm::makeSelectListWithEmpty($this->piggyBanks->getPiggyBanks());
|
||||||
|
|
||||||
/** @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());
|
|
||||||
|
|
||||||
// view related code
|
// view related code
|
||||||
$subTitle = trans('breadcrumbs.edit_journal', ['description' => $journal->description]);
|
$subTitle = trans('breadcrumbs.edit_journal', ['description' => $journal->description]);
|
||||||
@@ -217,19 +229,19 @@ class TransactionController extends Controller
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @param Request $request
|
* @param Request $request
|
||||||
* @param JournalRepositoryInterface $repository
|
* @param JournalTaskerInterface $tasker
|
||||||
* @param string $what
|
* @param string $what
|
||||||
*
|
*
|
||||||
* @return View
|
* @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);
|
$pageSize = intval(Preferences::get('transactionPageSize', 50)->data);
|
||||||
$subTitleIcon = config('firefly.transactionIconsByWhat.' . $what);
|
$subTitleIcon = config('firefly.transactionIconsByWhat.' . $what);
|
||||||
$types = config('firefly.transactionTypesByWhat.' . $what);
|
$types = config('firefly.transactionTypesByWhat.' . $what);
|
||||||
$subTitle = trans('firefly.title_' . $what);
|
$subTitle = trans('firefly.title_' . $what);
|
||||||
$page = intval($request->get('page'));
|
$page = intval($request->get('page'));
|
||||||
$journals = $repository->getJournals($types, $page, $pageSize);
|
$journals = $tasker->getJournals($types, $page, $pageSize);
|
||||||
|
|
||||||
$journals->setPath('transactions/' . $what);
|
$journals->setPath('transactions/' . $what);
|
||||||
|
|
||||||
@@ -267,13 +279,13 @@ class TransactionController extends Controller
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @param TransactionJournal $journal
|
* @param TransactionJournal $journal
|
||||||
* @param JournalRepositoryInterface $repository
|
* @param JournalTaskerInterface $tasker
|
||||||
*
|
*
|
||||||
* @return View
|
* @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);
|
$transactions = $tasker->getTransactionsOverview($journal);
|
||||||
$what = strtolower($journal->transaction_type_type ?? $journal->transactionType->type);
|
$what = strtolower($journal->transaction_type_type ?? $journal->transactionType->type);
|
||||||
$subTitle = trans('firefly.' . $what) . ' "' . e($journal->description) . '"';
|
$subTitle = trans('firefly.' . $what) . ' "' . e($journal->description) . '"';
|
||||||
@@ -291,63 +303,47 @@ class TransactionController extends Controller
|
|||||||
*/
|
*/
|
||||||
public function store(JournalFormRequest $request, JournalRepositoryInterface $repository)
|
public function store(JournalFormRequest $request, JournalRepositoryInterface $repository)
|
||||||
{
|
{
|
||||||
$att = app('FireflyIII\Helpers\Attachments\AttachmentHelperInterface');
|
|
||||||
$doSplit = intval($request->get('split_journal')) === 1;
|
$doSplit = intval($request->get('split_journal')) === 1;
|
||||||
$journalData = $request->getJournalData();
|
$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.
|
// store the journal only, flash the rest.
|
||||||
if ($doSplit) {
|
if (count($this->attachments->getErrors()->get('attachments')) > 0) {
|
||||||
$journal = $repository->storeJournal($journalData);
|
Session::flash('error', $this->attachments->getErrors()->get('attachments'));
|
||||||
$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
|
// flash messages
|
||||||
if (count($att->getMessages()->get('attachments')) > 0) {
|
if (count($this->attachments->getMessages()->get('attachments')) > 0) {
|
||||||
Session::flash('info', $att->getMessages()->get('attachments'));
|
Session::flash('info', $this->attachments->getMessages()->get('attachments'));
|
||||||
}
|
}
|
||||||
|
|
||||||
Session::put('journal-data', $journalData);
|
event(new TransactionJournalStored($journal, $data['piggy_bank_id']));
|
||||||
|
|
||||||
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'));
|
|
||||||
}
|
|
||||||
// flash messages
|
|
||||||
if (count($att->getMessages()->get('attachments')) > 0) {
|
|
||||||
Session::flash('info', $att->getMessages()->get('attachments'));
|
|
||||||
}
|
|
||||||
|
|
||||||
event(new TransactionJournalStored($journal, intval($journalData['piggy_bank_id'])));
|
|
||||||
|
|
||||||
Session::flash('success', strval(trans('firefly.stored_journal', ['description' => e($journal->description)])));
|
Session::flash('success', strval(trans('firefly.stored_journal', ['description' => e($journal->description)])));
|
||||||
Preferences::mark();
|
Preferences::mark();
|
||||||
|
|
||||||
if (intval($request->get('create_another')) === 1) {
|
if ($createAnother === true) {
|
||||||
// set value so create routine will not overwrite URL:
|
// set value so create routine will not overwrite URL:
|
||||||
Session::put('transactions.create.fromStore', true);
|
Session::put('transactions.create.fromStore', true);
|
||||||
|
|
||||||
return redirect(route('transactions.create', [$request->input('what')]))->withInput();
|
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.
|
// redirect to previous URL.
|
||||||
return redirect(session('transactions.create.url'));
|
return redirect(session('transactions.create.url'));
|
||||||
|
|
||||||
@@ -356,34 +352,30 @@ class TransactionController extends Controller
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @param JournalFormRequest $request
|
* @param JournalFormRequest $request
|
||||||
* @param JournalRepositoryInterface $repository
|
|
||||||
* @param AttachmentHelperInterface $att
|
|
||||||
* @param TransactionJournal $journal
|
* @param TransactionJournal $journal
|
||||||
*
|
*
|
||||||
* @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector
|
* @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();
|
$data = $request->getJournalData();
|
||||||
$repository->update($journal, $journalData);
|
$journal = $repository->update($journal, $data);
|
||||||
|
$this->attachments->saveAttachmentsForModel($journal);
|
||||||
// save attachments:
|
|
||||||
$att->saveAttachmentsForModel($journal);
|
|
||||||
|
|
||||||
// flash errors
|
// flash errors
|
||||||
if (count($att->getErrors()->get('attachments')) > 0) {
|
if (count($this->attachments->getErrors()->get('attachments')) > 0) {
|
||||||
Session::flash('error', $att->getErrors()->get('attachments'));
|
Session::flash('error', $this->attachments->getErrors()->get('attachments'));
|
||||||
}
|
}
|
||||||
// flash messages
|
// flash messages
|
||||||
if (count($att->getMessages()->get('attachments')) > 0) {
|
if (count($this->attachments->getMessages()->get('attachments')) > 0) {
|
||||||
Session::flash('info', $att->getMessages()->get('attachments'));
|
Session::flash('info', $this->attachments->getMessages()->get('attachments'));
|
||||||
}
|
}
|
||||||
|
|
||||||
event(new TransactionJournalUpdated($journal));
|
event(new TransactionJournalUpdated($journal));
|
||||||
// update, get events by date and sort DESC
|
// update, get events by date and sort DESC
|
||||||
|
|
||||||
$type = strtolower($journal->transaction_type_type ?? TransactionJournal::transactionTypeStr($journal));
|
$type = strtolower(TransactionJournal::transactionTypeStr($journal));
|
||||||
Session::flash('success', strval(trans('firefly.updated_' . $type, ['description' => e($journalData['description'])])));
|
Session::flash('success', strval(trans('firefly.updated_' . $type, ['description' => e($data['description'])])));
|
||||||
Preferences::mark();
|
Preferences::mark();
|
||||||
|
|
||||||
if (intval($request->get('return_to_edit')) === 1) {
|
if (intval($request->get('return_to_edit')) === 1) {
|
||||||
|
@@ -14,7 +14,6 @@ declare(strict_types = 1);
|
|||||||
namespace FireflyIII\Http\Requests;
|
namespace FireflyIII\Http\Requests;
|
||||||
|
|
||||||
use Carbon\Carbon;
|
use Carbon\Carbon;
|
||||||
use Exception;
|
|
||||||
use FireflyIII\Exceptions\FireflyException;
|
use FireflyIII\Exceptions\FireflyException;
|
||||||
use FireflyIII\Models\TransactionType;
|
use FireflyIII\Models\TransactionType;
|
||||||
use Input;
|
use Input;
|
||||||
@@ -37,80 +36,104 @@ 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
|
* @return array
|
||||||
*/
|
*/
|
||||||
public function getJournalData()
|
public function getJournalData()
|
||||||
{
|
{
|
||||||
$tags = $this->getFieldOrEmptyString('tags');
|
$data = [
|
||||||
|
'what' => $this->get('what'), // type. can be 'deposit', 'withdrawal' or 'transfer'
|
||||||
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,
|
'user' => auth()->user()->id,
|
||||||
'amount_currency_id_amount' => intval($this->get('amount_currency_id_amount')),
|
|
||||||
'date' => new Carbon($this->get('date')),
|
'date' => new Carbon($this->get('date')),
|
||||||
|
'tags' => explode(',', $this->getFieldOrEmptyString('tags')),
|
||||||
|
'currency_id' => intval($this->get('amount_currency_id_amount')),
|
||||||
|
|
||||||
|
// all custom fields:
|
||||||
'interest_date' => $this->getDateOrNull('interest_date'),
|
'interest_date' => $this->getDateOrNull('interest_date'),
|
||||||
'book_date' => $this->getDateOrNull('book_date'),
|
'book_date' => $this->getDateOrNull('book_date'),
|
||||||
'process_date' => $this->getDateOrNull('process_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')),
|
|
||||||
|
|
||||||
// new custom fields here:
|
|
||||||
'due_date' => $this->getDateOrNull('due_date'),
|
'due_date' => $this->getDateOrNull('due_date'),
|
||||||
'payment_date' => $this->getDateOrNull('payment_date'),
|
'payment_date' => $this->getDateOrNull('payment_date'),
|
||||||
'invoice_date' => $this->getDateOrNull('invoice_date'),
|
'invoice_date' => $this->getDateOrNull('invoice_date'),
|
||||||
'internal_reference' => trim(strval($this->get('internal_reference'))),
|
'internal_reference' => trim(strval($this->get('internal_reference'))),
|
||||||
'notes' => trim(strval($this->get('notes'))),
|
'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
|
* @return array
|
||||||
* @throws Exception
|
|
||||||
*/
|
*/
|
||||||
public function rules()
|
public function rules()
|
||||||
{
|
{
|
||||||
$what = Input::get('what');
|
$what = Input::get('what');
|
||||||
$rules = [
|
$rules = [
|
||||||
'description' => 'required|min:1,max:255',
|
|
||||||
'what' => 'required|in:withdrawal,deposit,transfer',
|
'what' => 'required|in:withdrawal,deposit,transfer',
|
||||||
'amount' => 'numeric|required|min:0.01',
|
|
||||||
'date' => 'required|date',
|
'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',
|
|
||||||
|
|
||||||
// new custom fields here:
|
// then, custom fields:
|
||||||
|
'interest_date' => 'date',
|
||||||
|
'book_date' => 'date',
|
||||||
|
'process_date' => 'date',
|
||||||
'due_date' => 'date',
|
'due_date' => 'date',
|
||||||
'payment_date' => 'date',
|
'payment_date' => 'date',
|
||||||
|
'invoice_date' => 'date',
|
||||||
'internal_reference' => 'min:1,max:255',
|
'internal_reference' => 'min:1,max:255',
|
||||||
'notes' => 'min:1,max:65536',
|
'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) {
|
switch ($what) {
|
||||||
case strtolower(TransactionType::WITHDRAWAL):
|
case strtolower(TransactionType::WITHDRAWAL):
|
||||||
$rules['source_account_id'] = 'required|exists:accounts,id|belongsToUser:accounts';
|
$rules['source_account_id'] = 'required|exists:accounts,id|belongsToUser:accounts';
|
||||||
$rules['destination_account_name'] = 'between:1,255';
|
$rules['destination_account_name'] = 'between:1,255';
|
||||||
if (intval(Input::get('budget_id')) != 0) {
|
|
||||||
$rules['budget_id'] = 'exists:budgets,id|belongsToUser:budgets';
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
case strtolower(TransactionType::DEPOSIT):
|
case strtolower(TransactionType::DEPOSIT):
|
||||||
$rules['source_account_name'] = 'between:1,255';
|
$rules['source_account_name'] = 'between:1,255';
|
||||||
$rules['destination_account_id'] = 'required|exists:accounts,id|belongsToUser:accounts';
|
$rules['destination_account_id'] = 'required|exists:accounts,id|belongsToUser:accounts';
|
||||||
break;
|
break;
|
||||||
case strtolower(TransactionType::TRANSFER):
|
case strtolower(TransactionType::TRANSFER):
|
||||||
|
// this may not work:
|
||||||
$rules['source_account_id'] = 'required|exists:accounts,id|belongsToUser:accounts|different:destination_account_id';
|
$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';
|
$rules['destination_account_id'] = 'required|exists:accounts,id|belongsToUser:accounts|different:source_account_id';
|
||||||
|
|
||||||
@@ -141,4 +164,63 @@ class JournalFormRequest extends Request
|
|||||||
{
|
{
|
||||||
return $this->get($field) ?? '';
|
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 Carbon\Carbon;
|
||||||
use FireflyIII\Models\RuleGroup;
|
use FireflyIII\Models\RuleGroup;
|
||||||
use FireflyIII\Repositories\Journal\JournalRepositoryInterface;
|
use FireflyIII\Repositories\Journal\JournalTaskerInterface;
|
||||||
use FireflyIII\Rules\Processor;
|
use FireflyIII\Rules\Processor;
|
||||||
use FireflyIII\User;
|
use FireflyIII\User;
|
||||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||||
@@ -155,10 +155,10 @@ class ExecuteRuleGroupOnExistingTransactions extends Job implements ShouldQueue
|
|||||||
*/
|
*/
|
||||||
protected function collectJournals()
|
protected function collectJournals()
|
||||||
{
|
{
|
||||||
/** @var JournalRepositoryInterface $repository */
|
/** @var JournalTaskerInterface $tasker */
|
||||||
$repository = app(JournalRepositoryInterface::class);
|
$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();
|
return new TransactionJournalMeta();
|
||||||
}
|
}
|
||||||
|
if (is_string($value) && strlen($value) === 0) {
|
||||||
|
return new TransactionJournalMeta();
|
||||||
|
}
|
||||||
|
|
||||||
if ($value instanceof Carbon) {
|
if ($value instanceof Carbon) {
|
||||||
$value = $value->toW3cString();
|
$value = $value->toW3cString();
|
||||||
|
@@ -15,6 +15,7 @@ namespace FireflyIII\Providers;
|
|||||||
|
|
||||||
use FireflyIII\Support\Amount;
|
use FireflyIII\Support\Amount;
|
||||||
use FireflyIII\Support\ExpandedForm;
|
use FireflyIII\Support\ExpandedForm;
|
||||||
|
use FireflyIII\Support\ExpandedMultiForm;
|
||||||
use FireflyIII\Support\FireflyConfig;
|
use FireflyIII\Support\FireflyConfig;
|
||||||
use FireflyIII\Support\Navigation;
|
use FireflyIII\Support\Navigation;
|
||||||
use FireflyIII\Support\Preferences;
|
use FireflyIII\Support\Preferences;
|
||||||
@@ -92,6 +93,11 @@ class FireflyServiceProvider extends ServiceProvider
|
|||||||
return new ExpandedForm;
|
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\Repositories\Currency\CurrencyRepositoryInterface', 'FireflyIII\Repositories\Currency\CurrencyRepository');
|
||||||
$this->app->bind('FireflyIII\Support\Search\SearchInterface', 'FireflyIII\Support\Search\Search');
|
$this->app->bind('FireflyIII\Support\Search\SearchInterface', 'FireflyIII\Support\Search\Search');
|
||||||
|
@@ -13,24 +13,18 @@ declare(strict_types = 1);
|
|||||||
|
|
||||||
namespace FireflyIII\Repositories\Journal;
|
namespace FireflyIII\Repositories\Journal;
|
||||||
|
|
||||||
use Carbon\Carbon;
|
|
||||||
use DB;
|
use DB;
|
||||||
use FireflyIII\Exceptions\FireflyException;
|
use FireflyIII\Exceptions\FireflyException;
|
||||||
use FireflyIII\Models\Account;
|
use FireflyIII\Models\Account;
|
||||||
use FireflyIII\Models\AccountType;
|
use FireflyIII\Models\AccountType;
|
||||||
use FireflyIII\Models\Budget;
|
use FireflyIII\Models\Budget;
|
||||||
use FireflyIII\Models\Category;
|
use FireflyIII\Models\Category;
|
||||||
use FireflyIII\Models\PiggyBankEvent;
|
|
||||||
use FireflyIII\Models\Tag;
|
use FireflyIII\Models\Tag;
|
||||||
use FireflyIII\Models\Transaction;
|
use FireflyIII\Models\Transaction;
|
||||||
use FireflyIII\Models\TransactionJournal;
|
use FireflyIII\Models\TransactionJournal;
|
||||||
use FireflyIII\Models\TransactionType;
|
use FireflyIII\Models\TransactionType;
|
||||||
use FireflyIII\Repositories\Tag\TagRepositoryInterface;
|
use FireflyIII\Repositories\Tag\TagRepositoryInterface;
|
||||||
use FireflyIII\User;
|
use FireflyIII\User;
|
||||||
use Illuminate\Database\Eloquent\Builder;
|
|
||||||
use Illuminate\Database\Query\JoinClause;
|
|
||||||
use Illuminate\Pagination\LengthAwarePaginator;
|
|
||||||
use Illuminate\Support\Collection;
|
|
||||||
use Log;
|
use Log;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -100,88 +94,6 @@ class JournalRepository implements JournalRepositoryInterface
|
|||||||
return $entry;
|
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
|
* @param array $data
|
||||||
@@ -192,56 +104,47 @@ class JournalRepository implements JournalRepositoryInterface
|
|||||||
{
|
{
|
||||||
// find transaction type.
|
// find transaction type.
|
||||||
$transactionType = TransactionType::where('type', ucfirst($data['what']))->first();
|
$transactionType = TransactionType::where('type', ucfirst($data['what']))->first();
|
||||||
|
|
||||||
// store actual journal.
|
|
||||||
$journal = new TransactionJournal(
|
$journal = new TransactionJournal(
|
||||||
[
|
[
|
||||||
'user_id' => $data['user'],
|
'user_id' => $data['user'],
|
||||||
'transaction_type_id' => $transactionType->id,
|
'transaction_type_id' => $transactionType->id,
|
||||||
'transaction_currency_id' => $data['amount_currency_id_amount'],
|
'transaction_currency_id' => $data['currency_id'],
|
||||||
'description' => $data['description'],
|
'description' => $data['description'],
|
||||||
'completed' => 0,
|
'completed' => 0,
|
||||||
'date' => $data['date'],
|
'date' => $data['date'],
|
||||||
'interest_date' => $data['interest_date'],
|
|
||||||
'book_date' => $data['book_date'],
|
|
||||||
'process_date' => $data['process_date'],
|
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
$journal->save();
|
$journal->save();
|
||||||
|
|
||||||
// store or get category
|
// store stuff:
|
||||||
if (strlen($data['category']) > 0) {
|
$this->storeCategoryWithJournal($journal, $data['category']);
|
||||||
$category = Category::firstOrCreateEncrypted(['name' => $data['category'], 'user_id' => $data['user']]);
|
$this->storeBudgetWithJournal($journal, $data['budget_id']);
|
||||||
$journal->categories()->save($category);
|
$accounts = $this->storeAccounts($transactionType, $data);
|
||||||
}
|
|
||||||
|
|
||||||
// store or get budget
|
// store two transactions:
|
||||||
if (intval($data['budget_id']) > 0 && $transactionType->type !== TransactionType::TRANSFER) {
|
$one = [
|
||||||
/** @var \FireflyIII\Models\Budget $budget */
|
'journal' => $journal,
|
||||||
$budget = Budget::find($data['budget_id']);
|
'account' => $accounts['source'],
|
||||||
$journal->budgets()->save($budget);
|
'amount' => bcmul(strval($data['amount']), '-1'),
|
||||||
}
|
'description' => null,
|
||||||
|
'category' => null,
|
||||||
|
'budget' => null,
|
||||||
|
'identifier' => 0,
|
||||||
|
];
|
||||||
|
$this->storeTransaction($one);
|
||||||
|
|
||||||
// store accounts (depends on type)
|
$two = [
|
||||||
list($sourceAccount, $destinationAccount) = $this->storeAccounts($transactionType, $data);
|
'journal' => $journal,
|
||||||
|
'account' => $accounts['destination'],
|
||||||
// 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'],
|
'amount' => $data['amount'],
|
||||||
]
|
'description' => null,
|
||||||
);
|
'category' => null,
|
||||||
$journal->completed = 1;
|
'budget' => null,
|
||||||
$journal->save();
|
'identifier' => 0,
|
||||||
|
];
|
||||||
|
|
||||||
|
$this->storeTransaction($two);
|
||||||
|
|
||||||
|
|
||||||
// store tags
|
// store tags
|
||||||
if (isset($data['tags']) && is_array($data['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));
|
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;
|
return $journal;
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -302,45 +208,24 @@ class JournalRepository implements JournalRepositoryInterface
|
|||||||
*/
|
*/
|
||||||
public function update(TransactionJournal $journal, array $data): TransactionJournal
|
public function update(TransactionJournal $journal, array $data): TransactionJournal
|
||||||
{
|
{
|
||||||
// update actual journal.
|
// update actual journal:
|
||||||
$journal->transaction_currency_id = $data['amount_currency_id_amount'];
|
$journal->transaction_currency_id = $data['currency_id'];
|
||||||
$journal->description = $data['description'];
|
$journal->description = $data['description'];
|
||||||
$journal->date = $data['date'];
|
$journal->date = $data['date'];
|
||||||
|
|
||||||
// unlink all categories, recreate them:
|
// unlink all categories, recreate them:
|
||||||
$journal->categories()->detach();
|
$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();
|
$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)
|
$this->storeCategoryWithJournal($journal, $data['category']);
|
||||||
list($fromAccount, $toAccount) = $this->storeAccounts($journal->transactionType, $data);
|
$this->storeBudgetWithJournal($journal, $data['budget_id']);
|
||||||
|
$accounts = $this->storeAccounts($journal->transactionType, $data);
|
||||||
|
|
||||||
// update the from and to transaction.
|
$sourceAmount = bcmul(strval($data['amount']), '-1');
|
||||||
/** @var Transaction $transaction */
|
$this->updateSourceTransaction($journal, $accounts['source'], $sourceAmount); // negative because source loses money.
|
||||||
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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
$amount = strval($data['amount']);
|
||||||
|
$this->updateDestinationTransaction($journal, $accounts['destination'], $amount); // positive because destination gets money.
|
||||||
|
|
||||||
$journal->save();
|
$journal->save();
|
||||||
|
|
||||||
@@ -402,38 +287,66 @@ class JournalRepository implements JournalRepositoryInterface
|
|||||||
*/
|
*/
|
||||||
private function storeAccounts(TransactionType $type, array $data): array
|
private function storeAccounts(TransactionType $type, array $data): array
|
||||||
{
|
{
|
||||||
$sourceAccount = null;
|
$accounts = [
|
||||||
$destinationAccount = null;
|
'source' => null,
|
||||||
|
'destination' => null,
|
||||||
|
];
|
||||||
switch ($type->type) {
|
switch ($type->type) {
|
||||||
case TransactionType::WITHDRAWAL:
|
case TransactionType::WITHDRAWAL:
|
||||||
list($sourceAccount, $destinationAccount) = $this->storeWithdrawalAccounts($data);
|
$accounts = $this->storeWithdrawalAccounts($data);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case TransactionType::DEPOSIT:
|
case TransactionType::DEPOSIT:
|
||||||
list($sourceAccount, $destinationAccount) = $this->storeDepositAccounts($data);
|
$accounts = $this->storeDepositAccounts($data);
|
||||||
|
|
||||||
break;
|
break;
|
||||||
case TransactionType::TRANSFER:
|
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;
|
break;
|
||||||
default:
|
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]);
|
Log::error('"destination"-account is null, so we cannot continue!', ['data' => $data]);
|
||||||
throw new FireflyException('"destination"-account is null, so we cannot continue!');
|
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]);
|
Log::error('"source"-account is null, so we cannot continue!', ['data' => $data]);
|
||||||
throw new FireflyException('"source"-account is null, so we cannot continue!');
|
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.*']);
|
$destinationAccount = Account::where('user_id', $this->user->id)->where('id', $data['destination_account_id'])->first(['accounts.*']);
|
||||||
|
|
||||||
if (strlen($data['source_account_name']) > 0) {
|
if (strlen($data['source_account_name']) > 0) {
|
||||||
$fromType = AccountType::where('type', 'Revenue account')->first();
|
$sourceType = AccountType::where('type', 'Revenue account')->first();
|
||||||
$fromAccount = Account::firstOrCreateEncrypted(
|
$sourceAccount = Account::firstOrCreateEncrypted(
|
||||||
['user_id' => $data['user'], 'account_type_id' => $fromType->id, 'name' => $data['source_account_name'], 'active' => 1]
|
['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();
|
$sourceType = AccountType::where('type', 'Cash account')->first();
|
||||||
$fromAccount = Account::firstOrCreateEncrypted(
|
$sourceAccount = Account::firstOrCreateEncrypted(
|
||||||
['user_id' => $data['user'], 'account_type_id' => $fromType->id, 'name' => 'Cash account', 'active' => 1]
|
['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();
|
$destinationType = AccountType::where('type', 'Cash account')->first();
|
||||||
$destinationAccount = Account::firstOrCreateEncrypted(
|
$destinationAccount = Account::firstOrCreateEncrypted(
|
||||||
['user_id' => $data['user'], 'account_type_id' => $destinationType->id, 'name' => 'Cash account', 'active' => 1]
|
['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;
|
namespace FireflyIII\Repositories\Journal;
|
||||||
|
|
||||||
use Carbon\Carbon;
|
|
||||||
use FireflyIII\Models\Transaction;
|
|
||||||
use FireflyIII\Models\TransactionJournal;
|
use FireflyIII\Models\TransactionJournal;
|
||||||
use Illuminate\Pagination\LengthAwarePaginator;
|
|
||||||
use Illuminate\Support\Collection;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Interface JournalRepositoryInterface
|
* Interface JournalRepositoryInterface
|
||||||
@@ -52,34 +48,6 @@ interface JournalRepositoryInterface
|
|||||||
*/
|
*/
|
||||||
public function first(): TransactionJournal;
|
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
|
* @param array $data
|
||||||
|
@@ -13,6 +13,7 @@ declare(strict_types = 1);
|
|||||||
|
|
||||||
namespace FireflyIII\Repositories\Journal;
|
namespace FireflyIII\Repositories\Journal;
|
||||||
|
|
||||||
|
use Carbon\Carbon;
|
||||||
use Crypt;
|
use Crypt;
|
||||||
use DB;
|
use DB;
|
||||||
use FireflyIII\Models\Transaction;
|
use FireflyIII\Models\Transaction;
|
||||||
@@ -20,6 +21,8 @@ use FireflyIII\Models\TransactionJournal;
|
|||||||
use FireflyIII\User;
|
use FireflyIII\User;
|
||||||
use Illuminate\Database\Eloquent\Builder;
|
use Illuminate\Database\Eloquent\Builder;
|
||||||
use Illuminate\Database\Query\JoinClause;
|
use Illuminate\Database\Query\JoinClause;
|
||||||
|
use Illuminate\Pagination\LengthAwarePaginator;
|
||||||
|
use Illuminate\Support\Collection;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class JournalTasker
|
* Class JournalTasker
|
||||||
@@ -42,6 +45,91 @@ class JournalTasker implements JournalTaskerInterface
|
|||||||
$this->user = $user;
|
$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
|
* Get an overview of the transactions of a journal, tailored to the view
|
||||||
* that shows a transaction (transaction/show/xx).
|
* that shows a transaction (transaction/show/xx).
|
||||||
@@ -138,7 +226,6 @@ class JournalTasker implements JournalTaskerInterface
|
|||||||
return $transactions;
|
return $transactions;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Collect the balance of an account before the given transaction has hit. This is tricky, because
|
* 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
|
* 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;
|
namespace FireflyIII\Repositories\Journal;
|
||||||
|
|
||||||
|
|
||||||
|
use Carbon\Carbon;
|
||||||
use FireflyIII\Models\TransactionJournal;
|
use FireflyIII\Models\TransactionJournal;
|
||||||
|
use Illuminate\Pagination\LengthAwarePaginator;
|
||||||
|
use Illuminate\Support\Collection;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Interface JournalTaskerInterface
|
* Interface JournalTaskerInterface
|
||||||
@@ -23,6 +26,34 @@ use FireflyIII\Models\TransactionJournal;
|
|||||||
*/
|
*/
|
||||||
interface JournalTaskerInterface
|
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
|
* 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\TransactionJournal;
|
||||||
use FireflyIII\Models\TransactionType;
|
use FireflyIII\Models\TransactionType;
|
||||||
use FireflyIII\Repositories\Journal\JournalRepositoryInterface;
|
use FireflyIII\Repositories\Journal\JournalTaskerInterface;
|
||||||
use Illuminate\Support\Collection;
|
use Illuminate\Support\Collection;
|
||||||
use Log;
|
use Log;
|
||||||
|
|
||||||
@@ -31,8 +31,8 @@ class TransactionMatcher
|
|||||||
private $limit = 10;
|
private $limit = 10;
|
||||||
/** @var int Maximum number of transaction to search in (for performance reasons) * */
|
/** @var int Maximum number of transaction to search in (for performance reasons) * */
|
||||||
private $range = 200;
|
private $range = 200;
|
||||||
/** @var JournalRepositoryInterface */
|
/** @var JournalTaskerInterface */
|
||||||
private $repository;
|
private $tasker;
|
||||||
/** @var array */
|
/** @var array */
|
||||||
private $transactionTypes = [TransactionType::DEPOSIT, TransactionType::WITHDRAWAL, TransactionType::TRANSFER];
|
private $transactionTypes = [TransactionType::DEPOSIT, TransactionType::WITHDRAWAL, TransactionType::TRANSFER];
|
||||||
/** @var array List of triggers to match */
|
/** @var array List of triggers to match */
|
||||||
@@ -41,11 +41,11 @@ class TransactionMatcher
|
|||||||
/**
|
/**
|
||||||
* TransactionMatcher constructor. Typehint the repository.
|
* 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
|
// - the maximum number of transactions to search in have been searched
|
||||||
do {
|
do {
|
||||||
// Fetch a batch of transactions from the database
|
// 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();
|
$set = $paginator->getCollection();
|
||||||
|
|
||||||
|
|
||||||
|
@@ -427,6 +427,7 @@ class ExpandedForm
|
|||||||
*/
|
*/
|
||||||
protected function expandOptionArray(string $name, $label, array $options): array
|
protected function expandOptionArray(string $name, $label, array $options): array
|
||||||
{
|
{
|
||||||
|
$name = str_replace('[]', '', $name);
|
||||||
$options['class'] = 'form-control';
|
$options['class'] = 'form-control';
|
||||||
$options['id'] = 'ffInput_' . $name;
|
$options['id'] = 'ffInput_' . $name;
|
||||||
$options['autocomplete'] = 'off';
|
$options['autocomplete'] = 'off';
|
||||||
@@ -494,6 +495,7 @@ class ExpandedForm
|
|||||||
if (isset($options['label'])) {
|
if (isset($options['label'])) {
|
||||||
return $options['label'];
|
return $options['label'];
|
||||||
}
|
}
|
||||||
|
$name = str_replace('[]', '', $name);
|
||||||
|
|
||||||
return strval(trans('form.' . $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);
|
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
|
* @param $attribute
|
||||||
*
|
*
|
||||||
|
@@ -188,8 +188,8 @@ return [
|
|||||||
|
|
||||||
|
|
||||||
// own stuff:
|
// own stuff:
|
||||||
//Barryvdh\LaravelIdeHelper\IdeHelperServiceProvider::class,
|
Barryvdh\LaravelIdeHelper\IdeHelperServiceProvider::class,
|
||||||
//Barryvdh\Debugbar\ServiceProvider::class,
|
Barryvdh\Debugbar\ServiceProvider::class,
|
||||||
DaveJamesMiller\Breadcrumbs\ServiceProvider::class,
|
DaveJamesMiller\Breadcrumbs\ServiceProvider::class,
|
||||||
TwigBridge\ServiceProvider::class,
|
TwigBridge\ServiceProvider::class,
|
||||||
'PragmaRX\Google2FA\Vendor\Laravel\ServiceProvider',
|
'PragmaRX\Google2FA\Vendor\Laravel\ServiceProvider',
|
||||||
@@ -267,6 +267,7 @@ return [
|
|||||||
'Amount' => 'FireflyIII\Support\Facades\Amount',
|
'Amount' => 'FireflyIII\Support\Facades\Amount',
|
||||||
'Steam' => 'FireflyIII\Support\Facades\Steam',
|
'Steam' => 'FireflyIII\Support\Facades\Steam',
|
||||||
'ExpandedForm' => 'FireflyIII\Support\Facades\ExpandedForm',
|
'ExpandedForm' => 'FireflyIII\Support\Facades\ExpandedForm',
|
||||||
|
'ExpandedMultiForm' => 'FireflyIII\Support\Facades\ExpandedMultiForm',
|
||||||
'Entrust' => 'Zizaco\Entrust\EntrustFacade',
|
'Entrust' => 'Zizaco\Entrust\EntrustFacade',
|
||||||
'Input' => 'Illuminate\Support\Facades\Input',
|
'Input' => 'Illuminate\Support\Facades\Input',
|
||||||
'Google2FA' => 'PragmaRX\Google2FA\Vendor\Laravel\Facade',
|
'Google2FA' => 'PragmaRX\Google2FA\Vendor\Laravel\Facade',
|
||||||
|
@@ -162,6 +162,11 @@ return [
|
|||||||
'multiRadio', 'file', 'multiCheckbox', 'staticText', 'amountSmall',
|
'multiRadio', 'file', 'multiCheckbox', 'staticText', 'amountSmall',
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
|
'ExpandedMultiForm' => [
|
||||||
|
'is_safe' => [
|
||||||
|
'text','select','amount'
|
||||||
|
],
|
||||||
|
],
|
||||||
'Form' => [
|
'Form' => [
|
||||||
'is_safe' => [
|
'is_safe' => [
|
||||||
'input', 'select', 'checkbox', 'model', 'open', 'radio', 'textarea', 'file',
|
'input', 'select', 'checkbox', 'model', 'open', 'radio', 'textarea', 'file',
|
||||||
|
@@ -5,6 +5,9 @@ $(function () {
|
|||||||
// when you click on a currency, this happens:
|
// when you click on a currency, this happens:
|
||||||
$('.currency-option').click(currencySelect);
|
$('.currency-option').click(currencySelect);
|
||||||
|
|
||||||
|
// when you click on a multi currency, this happens:
|
||||||
|
$('.multi-currency-option').click(multiCurrencySelect);
|
||||||
|
|
||||||
var ranges = {};
|
var ranges = {};
|
||||||
ranges[dateRangeConfig.currentPeriod] = [moment(dateRangeConfig.ranges.current[0]), moment(dateRangeConfig.ranges.current[1])];
|
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])];
|
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) {
|
function currencySelect(e) {
|
||||||
"use strict";
|
"use strict";
|
||||||
// clicked on
|
// clicked on
|
||||||
|
@@ -2,7 +2,12 @@
|
|||||||
<label for="{{ options.id }}" class="col-sm-4 control-label">{{ label }}</label>
|
<label for="{{ options.id }}" class="col-sm-4 control-label">{{ label }}</label>
|
||||||
|
|
||||||
<div class="col-sm-8">
|
<div class="col-sm-8">
|
||||||
|
<div class="input-group">
|
||||||
|
<div class="input-group-addon">
|
||||||
|
<i class="fa fa-calendar"></i>
|
||||||
|
</div>
|
||||||
{{ Form.input('date', name, value, options) }}
|
{{ Form.input('date', name, value, options) }}
|
||||||
|
</div>
|
||||||
{% include 'form/help.twig' %}
|
{% include 'form/help.twig' %}
|
||||||
{% include 'form/feedback.twig' %}
|
{% include 'form/feedback.twig' %}
|
||||||
</div>
|
</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>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
<!-- DESCRIPTION ALWAYS AVAILABLE -->
|
{# DESCRIPTION IS ALWAYS AVAILABLE #}
|
||||||
{{ ExpandedForm.text('description') }}
|
{{ ExpandedForm.text('description') }}
|
||||||
|
|
||||||
<!-- SELECTABLE SOURCE ACCOUNT ONLY FOR WITHDRAWALS AND TRANSFERS -->
|
{# SELECTABLE SOURCE ACCOUNT ONLY FOR WITHDRAWALS AND TRANSFERS #}
|
||||||
{{ ExpandedForm.select('source_account_id', assetAccounts, null, {label: trans('form.asset_source_account')}) }}
|
{{ ExpandedForm.select('source_account_id', assetAccounts, null, {label: trans('form.asset_source_account')}) }}
|
||||||
|
|
||||||
<!-- FREE FORMAT SOURCE ACCOUNT ONLY FOR DEPOSITS -->
|
{# FREE FORMAT SOURCE ACCOUNT ONLY FOR DEPOSITS #}
|
||||||
{{ ExpandedForm.text('source_account_name', null, {label: trans('form.revenue_account')}) }}
|
{{ ExpandedForm.text('source_account_name', null, {label: trans('form.revenue_account')}) }}
|
||||||
|
|
||||||
<!-- FREE FORMAT DESTINATION ACCOUNT ONLY FOR EXPENSES -->
|
{# FREE FORMAT DESTINATION ACCOUNT ONLY FOR EXPENSES #}
|
||||||
{{ ExpandedForm.text('destination_account_name', null, {label: trans('form.expense_account')}) }}
|
{{ ExpandedForm.text('destination_account_name', null, {label: trans('form.expense_account')}) }}
|
||||||
|
|
||||||
<!-- SELECTABLE DESTINATION ACCOUNT ONLY FOR TRANSFERS AND DEPOSITS -->
|
{# SELECTABLE DESTINATION ACCOUNT ONLY FOR TRANSFERS AND DEPOSITS #}
|
||||||
{{ ExpandedForm.select('destination_account_id', assetAccounts, null, {label: trans('form.asset_destination_account')} ) }}
|
{{ ExpandedForm.select('destination_account_id', assetAccounts, null, {label: trans('form.asset_destination_account')} ) }}
|
||||||
|
|
||||||
|
{# ALWAYS SHOW AMOUNT #}
|
||||||
<!-- ALWAYS SHOW AMOUNT -->
|
|
||||||
{{ ExpandedForm.amount('amount') }}
|
{{ ExpandedForm.amount('amount') }}
|
||||||
|
|
||||||
<!-- ALWAYS SHOW DATE -->
|
<!-- ALWAYS SHOW DATE -->
|
||||||
@@ -108,33 +107,33 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="box-body">
|
<div class="box-body">
|
||||||
|
|
||||||
|
{# INTEREST DATE #}
|
||||||
{% if optionalFields.interest_date %}
|
{% if optionalFields.interest_date %}
|
||||||
<!-- INTEREST DATE -->
|
|
||||||
{{ ExpandedForm.date('interest_date') }}
|
{{ ExpandedForm.date('interest_date') }}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
|
{# BOOK DATE #}
|
||||||
{% if optionalFields.book_date %}
|
{% if optionalFields.book_date %}
|
||||||
<!-- BOOK DATE -->
|
|
||||||
{{ ExpandedForm.date('book_date') }}
|
{{ ExpandedForm.date('book_date') }}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
|
{# PROCESSING DATE #}
|
||||||
{% if optionalFields.process_date %}
|
{% if optionalFields.process_date %}
|
||||||
<!-- PROCESSING DATE -->
|
|
||||||
{{ ExpandedForm.date('process_date') }}
|
{{ ExpandedForm.date('process_date') }}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
|
{# DUE DATE #}
|
||||||
{% if optionalFields.due_date %}
|
{% if optionalFields.due_date %}
|
||||||
<!-- DUE DATE -->
|
|
||||||
{{ ExpandedForm.date('due_date') }}
|
{{ ExpandedForm.date('due_date') }}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
|
{# PAYMENT DATE #}
|
||||||
{% if optionalFields.payment_date %}
|
{% if optionalFields.payment_date %}
|
||||||
<!-- PAYMENT DATE -->
|
|
||||||
{{ ExpandedForm.date('payment_date') }}
|
{{ ExpandedForm.date('payment_date') }}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
|
{# INVOICE DATE #}
|
||||||
{% if optionalFields.invoice_date %}
|
{% if optionalFields.invoice_date %}
|
||||||
<!-- INVOICE DATE -->
|
|
||||||
{{ ExpandedForm.date('invoice_date') }}
|
{{ ExpandedForm.date('invoice_date') }}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
@@ -149,13 +148,14 @@
|
|||||||
<h3 class="box-title">{{ 'optional_field_meta_business'|_ }}</h3>
|
<h3 class="box-title">{{ 'optional_field_meta_business'|_ }}</h3>
|
||||||
</div>
|
</div>
|
||||||
<div class="box-body">
|
<div class="box-body">
|
||||||
|
|
||||||
|
{# REFERENCE #}
|
||||||
{% if optionalFields.internal_reference %}
|
{% if optionalFields.internal_reference %}
|
||||||
<!-- REFERENCE -->
|
|
||||||
{{ ExpandedForm.text('internal_reference') }}
|
{{ ExpandedForm.text('internal_reference') }}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
|
{# NOTES #}
|
||||||
{% if optionalFields.notes %}
|
{% if optionalFields.notes %}
|
||||||
<!-- NOTES -->
|
|
||||||
{{ ExpandedForm.textarea('notes') }}
|
{{ ExpandedForm.textarea('notes') }}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
@@ -170,8 +170,8 @@
|
|||||||
<h3 class="box-title">{{ 'optional_field_attachments'|_ }}</h3>
|
<h3 class="box-title">{{ 'optional_field_attachments'|_ }}</h3>
|
||||||
</div>
|
</div>
|
||||||
<div class="box-body">
|
<div class="box-body">
|
||||||
|
{# ATTACHMENTS #}
|
||||||
{% if optionalFields.attachments %}
|
{% if optionalFields.attachments %}
|
||||||
<!-- ATTACHMENTS -->
|
|
||||||
{{ ExpandedForm.file('attachments[]', {'multiple': 'multiple','helpText': trans('firefly.upload_max_file_size', {'size': uploadSize|filesize}) }) }}
|
{{ ExpandedForm.file('attachments[]', {'multiple': 'multiple','helpText': trans('firefly.upload_max_file_size', {'size': uploadSize|filesize}) }) }}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
@@ -193,6 +193,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
#}
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user