diff --git a/app/Http/Controllers/TransactionController.php b/app/Http/Controllers/TransactionController.php index 436bec9ecb..1a34d604c5 100644 --- a/app/Http/Controllers/TransactionController.php +++ b/app/Http/Controllers/TransactionController.php @@ -7,10 +7,10 @@ use Config; use ExpandedForm; use FireflyIII\Events\TransactionJournalStored; use FireflyIII\Events\TransactionJournalUpdated; -use FireflyIII\Exceptions\FireflyException; use FireflyIII\Helpers\Attachments\AttachmentHelperInterface; use FireflyIII\Http\Requests\JournalFormRequest; -use FireflyIII\Http\Requests\MassJournalRequest; +use FireflyIII\Http\Requests\MassDeleteJournalRequest; +use FireflyIII\Http\Requests\MassEditJournalRequest; use FireflyIII\Models\PiggyBank; use FireflyIII\Models\PiggyBankEvent; use FireflyIII\Models\Transaction; @@ -243,12 +243,12 @@ class TransactionController extends Controller } /** - * @param MassJournalRequest $request + * @param MassDeleteJournalRequest $request * @param JournalRepositoryInterface $repository * * @return mixed */ - public function massDestroy(MassJournalRequest $request, JournalRepositoryInterface $repository) + public function massDestroy(MassDeleteJournalRequest $request, JournalRepositoryInterface $repository) { $ids = $request->get('confirm_mass_delete'); $set = new Collection; @@ -263,14 +263,16 @@ class TransactionController extends Controller } } unset($journal); + $count = 0; + /** @var TransactionJournal $journal */ foreach ($set as $journal) { $repository->delete($journal); + $count++; } Preferences::mark(); - - Session::flash('success', trans('firefly.mass_deleted_transactions_success')); + Session::flash('success', trans('firefly.mass_deleted_transactions_success', ['amount' => $count])); // redirect to previous URL: return redirect(session('transactions.mass-delete.url')); @@ -282,15 +284,84 @@ class TransactionController extends Controller */ public function massEdit(Collection $journals) { - throw new FireflyException('Mass editing of journals is not yet supported.'); + $subTitle = trans('firefly.mass_edit_journals'); + /** @var ARI $accountRepository */ + $accountRepository = app('FireflyIII\Repositories\Account\AccountRepositoryInterface'); + $accountList = ExpandedForm::makeSelectList($accountRepository->getAccounts(['Default account', 'Asset account'])); + // put previous url in session + Session::put('transactions.mass-edit.url', URL::previous()); + Session::flash('gaEventCategory', 'transactions'); + Session::flash('gaEventAction', 'mass-edit'); + + return view('transactions.mass-edit', compact('journals', 'subTitle', 'accountList')); } /** * */ - public function massUpdate() + public function massUpdate(MassEditJournalRequest $request, JournalRepositoryInterface $repository) { + $journalIds = Input::get('journals'); + $count = 0; + if (is_array($journalIds)) { + foreach ($journalIds as $journalId) { + $journal = $repository->find(intval($journalId)); + if ($journal) { + // do update. + + // get optional fields: + $what = strtolower(TransactionJournal::transactionTypeStr($journal)); + $sourceAccountId = $request->get('source_account_id')[$journal->id] ?? 0; + $destAccountId = $request->get('destination_account_id')[$journal->id] ?? 0; + $expenseAccount = $request->get('expense_account')[$journal->id] ?? ''; + $revenueAccount = $request->get('revenue_account')[$journal->id] ?? ''; + $budgetId = $journal->budgets->first() ? $journal->budgets->first()->id : 0; + $category = $journal->categories->first() ? $journal->categories->first()->name : ''; + $tags = $journal->tags->pluck('tag')->toArray(); + + // for a deposit, the 'account_id' is the account the money is deposited on. + // needs a better way of handling. + // more uniform source/destination field names + $accountId = $sourceAccountId; + if ($what == 'deposit') { + $accountId = $destAccountId; + } + + // build data array + $data = [ + 'id' => $journal->id, + 'what' => $what, + 'description' => $request->get('description')[$journal->id], + 'account_id' => intval($accountId), + 'account_from_id' => intval($sourceAccountId), + 'account_to_id' => intval($destAccountId), + 'expense_account' => $expenseAccount, + 'revenue_account' => $revenueAccount, + 'amount' => round($request->get('amount')[$journal->id], 4), + 'user' => Auth::user()->id, + 'amount_currency_id_amount' => intval($request->get('amount_currency_id_amount_' . $journal->id)), + 'date' => new Carbon($request->get('date')[$journal->id]), + 'interest_date' => $journal->interest_date, + 'book_date' => $journal->book_date, + 'process_date' => $journal->process_date, + 'budget_id' => $budgetId, + 'category' => $category, + 'tags' => $tags, + + ]; + // call repository update function. + $repository->update($journal, $data); + + $count++; + } + } + } + Preferences::mark(); + Session::flash('success', trans('firefly.mass_edited_transactions_success', ['amount' => $count])); + + // redirect to previous URL: + return redirect(session('transactions.mass-edit.url')); } diff --git a/app/Http/Requests/MassJournalRequest.php b/app/Http/Requests/MassDeleteJournalRequest.php similarity index 88% rename from app/Http/Requests/MassJournalRequest.php rename to app/Http/Requests/MassDeleteJournalRequest.php index 63c9a4caf9..8426a96ab8 100644 --- a/app/Http/Requests/MassJournalRequest.php +++ b/app/Http/Requests/MassDeleteJournalRequest.php @@ -1,6 +1,6 @@ 'required|min:1,max:255', + 'source_account_id.*' => 'numeric|belongsToUser:accounts,id', + 'destination_account_id.*' => 'numeric|belongsToUser:accounts,id', + 'revenue_account' => 'max:255', + 'expense_account' => 'max:255', + ]; + } +} diff --git a/app/Http/routes.php b/app/Http/routes.php index 159f1647e7..c9caa03944 100644 --- a/app/Http/routes.php +++ b/app/Http/routes.php @@ -380,7 +380,7 @@ Route::group( // mass edit and mass delete. Route::get('/transactions/mass-edit/{journalList}', ['uses' => 'TransactionController@massEdit', 'as' => 'transactions.mass-edit']); Route::get('/transactions/mass-delete/{journalList}', ['uses' => 'TransactionController@massDelete', 'as' => 'transactions.mass-delete']); - Route::post('/transactions/mass-update/{journalList}', ['uses' => 'TransactionController@massUpdate', 'as' => 'transactions.mass-update']); + Route::post('/transactions/mass-update', ['uses' => 'TransactionController@massUpdate', 'as' => 'transactions.mass-update']); Route::post('/transactions/mass-destroy', ['uses' => 'TransactionController@massDestroy', 'as' => 'transactions.mass-destroy']); /** diff --git a/app/Repositories/Journal/JournalRepository.php b/app/Repositories/Journal/JournalRepository.php index 2775226b1f..71e7beb15d 100644 --- a/app/Repositories/Journal/JournalRepository.php +++ b/app/Repositories/Journal/JournalRepository.php @@ -407,15 +407,17 @@ class JournalRepository implements JournalRepositoryInterface $fromAccount = Account::find($data['account_from_id']); $toAccount = Account::find($data['account_to_id']); break; + default: + throw new FireflyException('Did not recognise transaction type.'); } if (is_null($toAccount)) { - Log::error('"to"-account is null, so we cannot continue!'); + Log::error('"to"-account is null, so we cannot continue!', ['data' => $data]); throw new FireflyException('"to"-account is null, so we cannot continue!'); } if (is_null($fromAccount)) { - Log::error('"from"-account is null, so we cannot continue!'); + Log::error('"from"-account is null, so we cannot continue!', ['data' => $data]); throw new FireflyException('"from"-account is null, so we cannot continue!'); } diff --git a/app/Support/ExpandedForm.php b/app/Support/ExpandedForm.php index c92580de77..4b266746c9 100644 --- a/app/Support/ExpandedForm.php +++ b/app/Support/ExpandedForm.php @@ -20,6 +20,31 @@ use Session; class ExpandedForm { + /** + * @param $name + * @param null $value + * @param array $options + * + * @return string + */ + public function amountSmall(string $name, $value = null, array $options = []): string + { + $label = $this->label($name, $options); + $options = $this->expandOptionArray($name, $label, $options); + $classes = $this->getHolderClasses($name); + $value = $this->fillFieldValue($name, $value); + $options['step'] = 'any'; + $options['min'] = '0.01'; + $defaultCurrency = isset($options['currency']) ? $options['currency'] : Amt::getDefaultCurrency(); + $currencies = Amt::getAllCurrencies(); + unset($options['currency']); + unset($options['placeholder']); + $html = view('form.amount-small', compact('defaultCurrency', 'currencies', 'classes', 'name', 'value', 'options'))->render(); + + return $html; + + } + /** * @param $name * @param null $value @@ -261,6 +286,7 @@ class ExpandedForm return $html; } + /** * @param $name diff --git a/config/twigbridge.php b/config/twigbridge.php index 4eea607b35..0aac166b97 100644 --- a/config/twigbridge.php +++ b/config/twigbridge.php @@ -144,7 +144,7 @@ return [ 'ExpandedForm' => [ 'is_safe' => [ 'date', 'text', 'select', 'balance', 'optionsList', 'checkbox', 'amount', 'tags', 'integer', 'textarea', 'location', - 'multiRadio', 'file', 'multiCheckbox', 'staticText' + 'multiRadio', 'file', 'multiCheckbox', 'staticText', 'amountSmall', ] ], 'Form' => [ diff --git a/resources/lang/en_US/firefly.php b/resources/lang/en_US/firefly.php index eb0e5408cd..168f57701b 100644 --- a/resources/lang/en_US/firefly.php +++ b/resources/lang/en_US/firefly.php @@ -561,7 +561,11 @@ return [ 'edit_selected' => 'Edit selected', 'delete_selected' => 'Delete selected', 'mass_delete_journals' => 'Delete a number of transactions', + 'mass_edit_journals' => 'Edit a number of transactions', + 'cannot_edit_other_fields' => 'You cannot mass-edit other fields than the ones here, because there is no room to show them. Please follow the link and edit them by one-by-one, if you need to edit these fields.', 'perm-delete-many' => 'Deleting many items in one go can be very disruptive. Please be cautious.', + 'mass_deleted_transactions_success' => 'Deleted :amount transaction(s).', + 'mass_edited_transactions_success' => 'Updated :amount transaction(s)', // new user: diff --git a/resources/lang/en_US/form.php b/resources/lang/en_US/form.php index e440a3d5b5..4b18bfdec4 100644 --- a/resources/lang/en_US/form.php +++ b/resources/lang/en_US/form.php @@ -117,6 +117,7 @@ return [ 'permDeleteWarning' => 'Deleting stuff from Firely is permanent and cannot be undone.', 'mass_make_selection' => 'You can still prevent items from being deleted by removing the checkbox.', 'delete_all_permanently' => 'Delete selected permanently', + 'update_all_journals' => 'Update these transactions', 'also_delete_transactions' => 'The only transaction connected to this account will be deleted as well.|All :count transactions connected to this account will be deleted as well.', 'also_delete_rules' => 'The only rule connected to this rule group will be deleted as well.|All :count rules connected to this rule group will be deleted as well.', 'also_delete_piggyBanks' => 'The only piggy bank connected to this account will be deleted as well.|All :count piggy bank connected to this account will be deleted as well.', diff --git a/resources/views/form/amount-small.twig b/resources/views/form/amount-small.twig new file mode 100644 index 0000000000..213c3f25ad --- /dev/null +++ b/resources/views/form/amount-small.twig @@ -0,0 +1,24 @@ +
+
+ + +
+ {{ Form.input('number', name, value, options) }} + + +
+ diff --git a/resources/views/transactions/mass-delete.twig b/resources/views/transactions/mass-delete.twig index c39b4eee43..8b9d26ee8b 100644 --- a/resources/views/transactions/mass-delete.twig +++ b/resources/views/transactions/mass-delete.twig @@ -1,7 +1,7 @@ {% extends "./layout/default.twig" %} {% block breadcrumbs %} - {{ Breadcrumbs.renderIfExists(Route.getCurrentRoute.getName, journal) }} + {{ Breadcrumbs.renderIfExists(Route.getCurrentRoute.getName) }} {% endblock %} {% block content %} diff --git a/resources/views/transactions/mass-edit.twig b/resources/views/transactions/mass-edit.twig new file mode 100644 index 0000000000..b7b6bb994d --- /dev/null +++ b/resources/views/transactions/mass-edit.twig @@ -0,0 +1,101 @@ +{% extends "./layout/default.twig" %} + +{% block breadcrumbs %} + {{ Breadcrumbs.renderIfExists(Route.getCurrentRoute.getName) }} +{% endblock %} + +{% block content %} + {{ Form.open({'class' : 'form-horizontal','id' : 'destroy','url' : route('transactions.mass-update')}) }} + +
+
+
+
+

{{ 'mass_edit_journals'|_ }}

+
+
+

+ {{ 'cannot_edit_other_fields'|_ }} +

+ + + + + + + + + + {% for journal in journals %} + + + + + + + + + {% endfor %} +
 {{ trans('list.description') }}{{ trans('list.amount') }}{{ trans('list.date') }}{{ trans('list.from') }}{{ trans('list.to') }}
+ + + + + + + + + + + {% if journal.destination_amount > 0 %} + {% set amount = journal.destination_amount %} + {% else %} + {% set amount = journal.source_amount %} + {% endif %} + + {{ ExpandedForm.amountSmall('amount_'~journal.id, amount, {'name' : 'amount['~journal.id~']', 'currency' : journal.transactionCurrency}) }} + + + + + + + {% if journal.transaction_type_type == 'Transfer' or journal.transaction_type_type == 'Withdrawal' %} + {{ Form.select('source_account_id['~journal.id~']', accountList, journal.source_account_id, {'class': 'form-control'}) }} + {% else %} + + {{ Form.input('text', 'revenue_account['~journal.id~']', journal.source_account_name, {'class': 'form-control', 'placeholder': trans('form.revenue_account')}) }} + {% endif %} + + + + {% if journal.transaction_type_type == 'Transfer' or journal.transaction_type_type == 'Deposit' %} + + {{ Form.select('destination_account_id['~journal.id~']', accountList, journal.destination_account_id, {'class': 'form-control'}) }} + {% else %} + + {{ Form.input('text', 'expense_account['~journal.id~']', journal.destination_account_name, {'class': 'form-control', 'placeholder': trans('form.expense_account')}) }} + {% endif %} + +
+
+ +
+
+
+ + {{ Form.close|raw }} +{% endblock %} +{% block scripts %} + + +{% endblock %} \ No newline at end of file