mirror of
https://github.com/firefly-iii/firefly-iii.git
synced 2025-02-25 18:45:27 -06:00
Refactored the bulk edit controller.
This commit is contained in:
parent
54623061d8
commit
3c5c14ff5a
@ -409,7 +409,7 @@ class GroupCollector implements GroupCollectorInterface
|
||||
*/
|
||||
public function setTag(Tag $tag): GroupCollectorInterface
|
||||
{
|
||||
$this->joinTagTables();
|
||||
$this->withTagInformation();
|
||||
$this->query->where('tag_transaction_journal.tag_id', $tag->id);
|
||||
|
||||
return $this;
|
||||
@ -537,7 +537,7 @@ class GroupCollector implements GroupCollectorInterface
|
||||
*/
|
||||
public function setTags(Collection $tags): GroupCollectorInterface
|
||||
{
|
||||
$this->joinTagTables();
|
||||
$this->withTagInformation();
|
||||
$this->query->whereIn('tag_transaction_journal.tag_id', $tags->pluck('id')->toArray());
|
||||
|
||||
return $this;
|
||||
@ -619,9 +619,11 @@ class GroupCollector implements GroupCollectorInterface
|
||||
$return = [];
|
||||
/** @var array $group */
|
||||
foreach ($selection as $group) {
|
||||
$count = count($group['transactions']);
|
||||
foreach ($group['transactions'] as $journalId => $journal) {
|
||||
$journal['group_title'] = $group['title'];
|
||||
$return[$journalId] = $journal;
|
||||
$journal['group_title'] = $group['title'];
|
||||
$journal['journals_in_group'] = $count;
|
||||
$return[$journalId] = $journal;
|
||||
}
|
||||
}
|
||||
|
||||
@ -776,6 +778,24 @@ class GroupCollector implements GroupCollectorInterface
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return GroupCollectorInterface
|
||||
*/
|
||||
public function withTagInformation(): GroupCollectorInterface
|
||||
{
|
||||
$this->fields[] = 'tags.id as tag_id';
|
||||
$this->fields[] = 'tags.tag as tag_name';
|
||||
$this->fields[] = 'tags.date as tag_date';
|
||||
$this->fields[] = 'tags.description as tag_description';
|
||||
$this->fields[] = 'tags.latitude as tag_latitude';
|
||||
$this->fields[] = 'tags.longitude as tag_longitude';
|
||||
$this->fields[] = 'tags.zoomLevel as tag_zoom_level';
|
||||
|
||||
$this->joinTagTables();
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Collection $collection
|
||||
*
|
||||
@ -789,16 +809,18 @@ class GroupCollector implements GroupCollectorInterface
|
||||
$groupId = $augmentedGroup->transaction_group_id;
|
||||
if (!isset($groups[$groupId])) {
|
||||
// make new array
|
||||
$parsedGroup = $this->parseAugmentedGroup($augmentedGroup);
|
||||
$groupArray = [
|
||||
'id' => $augmentedGroup->transaction_group_id,
|
||||
'user_id' => $augmentedGroup->user_id,
|
||||
'title' => $augmentedGroup->transaction_group_title,
|
||||
'count' => 1,
|
||||
'sums' => [],
|
||||
'transactions' => [],
|
||||
'id' => $augmentedGroup->transaction_group_id,
|
||||
'user_id' => $augmentedGroup->user_id,
|
||||
'title' => $augmentedGroup->transaction_group_title,
|
||||
'transaction_type' => $parsedGroup['transaction_type_type'],
|
||||
'count' => 1,
|
||||
'sums' => [],
|
||||
'transactions' => [],
|
||||
];
|
||||
$journalId = (int)$augmentedGroup->transaction_journal_id;
|
||||
$groupArray['transactions'][$journalId] = $this->parseAugmentedGroup($augmentedGroup);
|
||||
$groupArray['transactions'][$journalId] = $parsedGroup;
|
||||
$groups[$groupId] = $groupArray;
|
||||
continue;
|
||||
}
|
||||
@ -948,13 +970,6 @@ class GroupCollector implements GroupCollectorInterface
|
||||
$this->hasJoinedTagTables = true;
|
||||
$this->query->leftJoin('tag_transaction_journal', 'tag_transaction_journal.transaction_journal_id', '=', 'transaction_journals.id');
|
||||
$this->query->leftJoin('tags', 'tag_transaction_journal.tag_id', '=', 'tags.id');
|
||||
$this->fields[] = 'tags.id as tag_id';
|
||||
$this->fields[] = 'tags.tag as tag_name';
|
||||
$this->fields[] = 'tags.date as tag_date';
|
||||
$this->fields[] = 'tags.description as tag_description';
|
||||
$this->fields[] = 'tags.latitude as tag_latitude';
|
||||
$this->fields[] = 'tags.longitude as tag_longitude';
|
||||
$this->fields[] = 'tags.zoomLevel as tag_zoom_level';
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -53,6 +53,13 @@ interface GroupCollectorInterface
|
||||
*/
|
||||
public function getSum(): string;
|
||||
|
||||
/**
|
||||
* Add tag info.
|
||||
*
|
||||
* @return GroupCollectorInterface
|
||||
*/
|
||||
public function withTagInformation(): GroupCollectorInterface;
|
||||
|
||||
/**
|
||||
* Return the groups.
|
||||
*
|
||||
|
@ -244,7 +244,6 @@ class AutoCompleteController extends Controller
|
||||
$array[$index]['name'] = $item['tag'];
|
||||
}
|
||||
|
||||
|
||||
return response()->json($array);
|
||||
}
|
||||
|
||||
|
@ -43,6 +43,7 @@ class BulkController extends Controller
|
||||
|
||||
/**
|
||||
* BulkController constructor.
|
||||
* @codeCoverageIgnore
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
@ -66,7 +67,7 @@ class BulkController extends Controller
|
||||
*
|
||||
* @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View
|
||||
*/
|
||||
public function edit(Collection $journals)
|
||||
public function edit(array $journals)
|
||||
{
|
||||
$subTitle = (string)trans('firefly.mass_bulk_journals');
|
||||
|
||||
@ -74,12 +75,6 @@ class BulkController extends Controller
|
||||
/** @var BudgetRepositoryInterface $repository */
|
||||
$repository = app(BudgetRepositoryInterface::class);
|
||||
$budgetList = app('expandedform')->makeSelectListWithEmpty($repository->getActiveBudgets());
|
||||
// collect some useful meta data for the mass edit:
|
||||
$journals->each(
|
||||
function (TransactionJournal $journal) {
|
||||
$journal->transaction_count = $journal->transactions()->count();
|
||||
}
|
||||
);
|
||||
|
||||
return view('transactions.bulk.edit', compact('journals', 'subTitle', 'budgetList'));
|
||||
}
|
||||
@ -104,38 +99,72 @@ class BulkController extends Controller
|
||||
$count = 0;
|
||||
|
||||
foreach ($journalIds as $journalId) {
|
||||
$journal = $this->repository->findNull((int)$journalId);
|
||||
if (null === $journal) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$count++;
|
||||
Log::debug(sprintf('Found journal #%d', $journal->id));
|
||||
|
||||
// update category if not told to ignore
|
||||
if (false === $ignoreCategory) {
|
||||
Log::debug(sprintf('Set category to %s', $request->string('category')));
|
||||
|
||||
$this->repository->updateCategory($journal, $request->string('category'));
|
||||
}
|
||||
|
||||
// update budget if not told to ignore (and is withdrawal)
|
||||
if (false === $ignoreBudget) {
|
||||
Log::debug(sprintf('Set budget to %d', $request->integer('budget_id')));
|
||||
$this->repository->updateBudget($journal, $request->integer('budget_id'));
|
||||
}
|
||||
|
||||
// update tags:
|
||||
if (false === $ignoreTags) {
|
||||
Log::debug(sprintf('Set tags to %s', $request->string('budget_id')));
|
||||
$this->repository->updateTags($journal, ['tags' => explode(',', $request->string('tags'))]);
|
||||
$journalId = (int)$journalId;
|
||||
$journal = $this->repository->findNull($journalId);
|
||||
if (null !== $journal) {
|
||||
$resultA = $this->updateJournalBudget($journal, $ignoreBudget, $request->integer('budget_id'));
|
||||
$resultB = $this->updateJournalTags($journal, $ignoreTags, explode(',', $request->string('tags')));
|
||||
$resultC = $this->updateJournalCategory($journal, $ignoreCategory, $request->string('category'));
|
||||
if ($resultA || $resultB || $resultC) {
|
||||
$count++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
app('preferences')->mark();
|
||||
$request->session()->flash('success', (string)trans('firefly.mass_edited_transactions_success', ['amount' => $count]));
|
||||
|
||||
// redirect to previous URL:
|
||||
return redirect($this->getPreviousUri('transactions.bulk-edit.uri'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param TransactionJournal $journal
|
||||
* @param bool $ignoreUpdate
|
||||
* @param array $tags
|
||||
* @return bool
|
||||
*/
|
||||
public function updateJournalTags(TransactionJournal $journal, bool $ignoreUpdate, array $tags): bool
|
||||
{
|
||||
if (true === $ignoreUpdate) {
|
||||
return false;
|
||||
}
|
||||
Log::debug(sprintf('Set tags to %s', implode(',', $tags)));
|
||||
$this->repository->updateTags($journal, $tags);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param TransactionJournal $journal
|
||||
* @param bool $ignoreUpdate
|
||||
* @param string $category
|
||||
* @return bool
|
||||
*/
|
||||
private function updateJournalCategory(TransactionJournal $journal, bool $ignoreUpdate, string $category): bool
|
||||
{
|
||||
if (true === $ignoreUpdate) {
|
||||
return false;
|
||||
}
|
||||
Log::debug(sprintf('Set budget to %s', $category));
|
||||
$this->repository->updateCategory($journal, $category);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param TransactionJournal $journal
|
||||
* @param bool $ignoreUpdate
|
||||
* @param int $budgetId
|
||||
* @return bool
|
||||
*/
|
||||
private function updateJournalBudget(TransactionJournal $journal, bool $ignoreUpdate, int $budgetId): bool
|
||||
{
|
||||
if (true === $ignoreUpdate) {
|
||||
return false;
|
||||
}
|
||||
Log::debug(sprintf('Set budget to %d', $budgetId));
|
||||
$this->repository->updateBudget($journal, $budgetId);
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -778,7 +778,16 @@ class JournalRepository implements JournalRepositoryInterface
|
||||
/** @var JournalUpdateService $service */
|
||||
$service = app(JournalUpdateService::class);
|
||||
|
||||
return $service->updateBudget($journal, $budgetId);
|
||||
$service->setTransactionJournal($journal);
|
||||
$service->setData(
|
||||
[
|
||||
'budget_id' => $budgetId,
|
||||
]
|
||||
);
|
||||
$service->update();
|
||||
$journal->refresh();
|
||||
|
||||
return $journal;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -793,8 +802,16 @@ class JournalRepository implements JournalRepositoryInterface
|
||||
{
|
||||
/** @var JournalUpdateService $service */
|
||||
$service = app(JournalUpdateService::class);
|
||||
$service->setTransactionJournal($journal);
|
||||
$service->setData(
|
||||
[
|
||||
'category_name' => $category,
|
||||
]
|
||||
);
|
||||
$service->update();
|
||||
$journal->refresh();
|
||||
|
||||
return $service->updateCategory($journal, $category);
|
||||
return $journal;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -809,10 +826,16 @@ class JournalRepository implements JournalRepositoryInterface
|
||||
{
|
||||
/** @var JournalUpdateService $service */
|
||||
$service = app(JournalUpdateService::class);
|
||||
$service->connectTags($journal, $tags);
|
||||
$service->setTransactionJournal($journal);
|
||||
$service->setData(
|
||||
[
|
||||
'tags' => $tags,
|
||||
]
|
||||
);
|
||||
$service->update();
|
||||
$journal->refresh();
|
||||
|
||||
return $journal;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -23,11 +23,9 @@ declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\Support\Binder;
|
||||
|
||||
use FireflyIII\Models\TransactionJournal;
|
||||
use FireflyIII\Helpers\Collector\GroupCollectorInterface;
|
||||
use FireflyIII\Models\TransactionType;
|
||||
use FireflyIII\Repositories\Journal\JournalRepositoryInterface;
|
||||
use Illuminate\Routing\Route;
|
||||
use Illuminate\Support\Collection;
|
||||
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
|
||||
|
||||
/**
|
||||
@ -35,73 +33,46 @@ use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
|
||||
*/
|
||||
class SimpleJournalList implements BinderInterface
|
||||
{
|
||||
|
||||
/**
|
||||
* @param string $value
|
||||
* @param Route $route
|
||||
* @param Route $route
|
||||
*
|
||||
* @return mixed
|
||||
* @throws \Symfony\Component\HttpKernel\Exception\NotFoundHttpException
|
||||
* @SuppressWarnings(PHPMD.CyclomaticComplexity)
|
||||
* @SuppressWarnings(PHPMD.ExcessiveMethodLength)
|
||||
* @SuppressWarnings(PHPMD.NPathComplexity)
|
||||
* @throws NotFoundHttpException
|
||||
*/
|
||||
public static function routeBinder(string $value, Route $route): Collection
|
||||
public static function routeBinder(string $value, Route $route): array
|
||||
{
|
||||
if (auth()->check()) {
|
||||
$list = array_unique(array_map('\intval', explode(',', $value)));
|
||||
if (0 === count($list)) {
|
||||
throw new NotFoundHttpException; // @codeCoverageIgnore
|
||||
$list = self::parseList($value);
|
||||
|
||||
// get the journals by using the collector.
|
||||
/** @var GroupCollectorInterface $collector */
|
||||
$collector = app(GroupCollectorInterface::class);
|
||||
$collector->setTypes([TransactionType::WITHDRAWAL, TransactionType::DEPOSIT, TransactionType::TRANSFER]);
|
||||
$collector->withCategoryInformation()->withBudgetInformation()->withTagInformation();
|
||||
$collector->setJournalIds($list);
|
||||
$result = $collector->getExtractedJournals();
|
||||
if (0 === count($result)) {
|
||||
throw new NotFoundHttpException;
|
||||
}
|
||||
|
||||
// prep some vars
|
||||
$messages = [];
|
||||
$final = new Collection;
|
||||
/** @var JournalRepositoryInterface $repository */
|
||||
$repository = app(JournalRepositoryInterface::class);
|
||||
|
||||
// get all journals:
|
||||
/** @var \Illuminate\Support\Collection $collection */
|
||||
$collection = auth()->user()->transactionJournals()
|
||||
->whereIn('transaction_journals.id', $list)
|
||||
->where('transaction_journals.completed', 1)
|
||||
->get(['transaction_journals.*']);
|
||||
|
||||
// filter the list! Yay!
|
||||
/** @var TransactionJournal $journal */
|
||||
foreach ($collection as $journal) {
|
||||
$sources = $repository->getJournalSourceAccounts($journal);
|
||||
$destinations = $repository->getJournalDestinationAccounts($journal);
|
||||
if ($sources->count() > 1) {
|
||||
$messages[] = (string)trans('firefly.cannot_edit_multiple_source', ['description' => $journal->description, 'id' => $journal->id]);
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($destinations->count() > 1) {
|
||||
$messages[] = (string)trans('firefly.cannot_edit_multiple_dest', ['description' => $journal->description, 'id' => $journal->id]);
|
||||
continue;
|
||||
}
|
||||
if (TransactionType::OPENING_BALANCE === $repository->getTransactionType($journal)) {
|
||||
$messages[] = (string)trans('firefly.cannot_edit_opening_balance');
|
||||
continue;
|
||||
}
|
||||
|
||||
// cannot edit reconciled transactions / journals:
|
||||
if ($repository->isJournalReconciled($journal)) {
|
||||
$messages[] = (string)trans('firefly.cannot_edit_reconciled', ['description' => $journal->description, 'id' => $journal->id]);
|
||||
continue;
|
||||
}
|
||||
|
||||
$final->push($journal);
|
||||
}
|
||||
|
||||
if ($final->count() > 0) {
|
||||
if (count($messages) > 0) {
|
||||
session()->flash('info', $messages);
|
||||
}
|
||||
|
||||
return $final;
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
throw new NotFoundHttpException;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $value
|
||||
* @return array
|
||||
*/
|
||||
protected static function parseList(string $value): array
|
||||
{
|
||||
$list = array_unique(array_map('\intval', explode(',', $value)));
|
||||
if (0 === count($list)) {
|
||||
throw new NotFoundHttpException; // @codeCoverageIgnore
|
||||
}
|
||||
|
||||
return $list;
|
||||
}
|
||||
}
|
||||
|
16
public/v1/js/ff/common/autocomplete.js
vendored
16
public/v1/js/ff/common/autocomplete.js
vendored
@ -29,8 +29,8 @@ function initTagsAC() {
|
||||
prefetch: {
|
||||
url: 'json/tags?uid=' + uid,
|
||||
filter: function (list) {
|
||||
return $.map(list, function (tagTag) {
|
||||
return {name: tagTag};
|
||||
return $.map(list, function (item) {
|
||||
return {name: item.name};
|
||||
});
|
||||
}
|
||||
},
|
||||
@ -38,8 +38,8 @@ function initTagsAC() {
|
||||
url: 'json/tags?search=%QUERY&uid=' + uid,
|
||||
wildcard: '%QUERY',
|
||||
filter: function (list) {
|
||||
return $.map(list, function (name) {
|
||||
return {name: name};
|
||||
return $.map(list, function (item) {
|
||||
return {name: item.name};
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -145,8 +145,8 @@ function initCategoryAC() {
|
||||
prefetch: {
|
||||
url: 'json/categories?uid=' + uid,
|
||||
filter: function (list) {
|
||||
return $.map(list, function (name) {
|
||||
return {name: name};
|
||||
return $.map(list, function (object) {
|
||||
return {name: object.name};
|
||||
});
|
||||
}
|
||||
},
|
||||
@ -154,8 +154,8 @@ function initCategoryAC() {
|
||||
url: 'json/categories?search=%QUERY&uid=' + uid,
|
||||
wildcard: '%QUERY',
|
||||
filter: function (list) {
|
||||
return $.map(list, function (name) {
|
||||
return {name: name};
|
||||
return $.map(list, function (object) {
|
||||
return {name: object.name};
|
||||
});
|
||||
}
|
||||
}
|
||||
|
120
public/v1/js/ff/list/groups.js
vendored
Normal file
120
public/v1/js/ff/list/groups.js
vendored
Normal file
@ -0,0 +1,120 @@
|
||||
/*
|
||||
* groups.js
|
||||
* Copyright (c) 2019 thegrumpydictator@gmail.com
|
||||
*
|
||||
* This file is part of Firefly III.
|
||||
*
|
||||
* Firefly III is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Firefly III is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Firefly III. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
var count = 0;
|
||||
|
||||
$(document).ready(function () {
|
||||
// top button to select all / deselect all:
|
||||
$('input[name="select-all"]').change(function () {
|
||||
if (this.checked) {
|
||||
checkAll();
|
||||
countChecked();
|
||||
updateActionButtons();
|
||||
} else {
|
||||
uncheckAll();
|
||||
countChecked();
|
||||
updateActionButtons();
|
||||
}
|
||||
});
|
||||
|
||||
// click the mass edit button:
|
||||
$('.mass-edit').click(goToMassEdit);
|
||||
// click the bulk edit button:
|
||||
$('.bulk-edit').click(goToBulkEdit);
|
||||
// click the delete button:
|
||||
$('.mass-delete').click(goToMassDelete);
|
||||
|
||||
// click checkbox:
|
||||
$('.mass-select').unbind('change').change(function () {
|
||||
countChecked();
|
||||
updateActionButtons();
|
||||
});
|
||||
|
||||
|
||||
});
|
||||
|
||||
/**
|
||||
*
|
||||
* @returns {boolean}
|
||||
*/
|
||||
function goToMassEdit() {
|
||||
console.log(mass_edit_url + '/' + getCheckboxes());
|
||||
window.location.href = mass_edit_url + '/' + getCheckboxes();
|
||||
return false;
|
||||
}
|
||||
|
||||
function goToBulkEdit() {
|
||||
console.log(bulk_edit_url + '/' + getCheckboxes());
|
||||
window.location.href = bulk_edit_url + '/' + getCheckboxes();
|
||||
return false;
|
||||
}
|
||||
|
||||
function goToMassDelete() {
|
||||
console.log(mass_delete_url + '/' + getCheckboxes());
|
||||
window.location.href = mass_delete_url + '/' + getCheckboxes();
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @returns {Array}
|
||||
*/
|
||||
function getCheckboxes() {
|
||||
"use strict";
|
||||
var list = [];
|
||||
$.each($('.mass-select'), function (i, v) {
|
||||
var checkbox = $(v);
|
||||
if (checkbox.prop('checked')) {
|
||||
// add to list.
|
||||
list.push(checkbox.val());
|
||||
}
|
||||
});
|
||||
return list;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
function countChecked() {
|
||||
count = $('.mass-select:checked').length;
|
||||
}
|
||||
|
||||
function checkAll() {
|
||||
$('.mass-select').prop('checked', true);
|
||||
}
|
||||
|
||||
function uncheckAll() {
|
||||
$('.mass-select').prop('checked', false);
|
||||
}
|
||||
|
||||
function updateActionButtons() {
|
||||
if (0 !== count) {
|
||||
$('.action-menu').show();
|
||||
|
||||
// also update labels:
|
||||
$('.mass-edit span').text(edit_selected_txt + ' (' + count + ')');
|
||||
$('.bulk-edit span').text(edit_bulk_selected_txt + ' (' + count + ')');
|
||||
$('.mass-delete span').text(delete_selected_txt + ' (' + count + ')');
|
||||
|
||||
}
|
||||
if (0 === count) {
|
||||
$('.action-menu').hide();
|
||||
}
|
||||
}
|
18
public/v1/js/ff/transactions/mass/edit-bulk.js
vendored
18
public/v1/js/ff/transactions/mass/edit-bulk.js
vendored
@ -24,4 +24,22 @@ $(document).ready(function () {
|
||||
"use strict";
|
||||
initTagsAC();
|
||||
initCategoryAC();
|
||||
|
||||
// on change, remove the checkbox.
|
||||
$('input[name="category"]').change(function () {
|
||||
$('input[name="ignore_category"]').attr('checked', false);
|
||||
});
|
||||
|
||||
$('select[name="budget_id"]').change(function () {
|
||||
|
||||
$('input[name="ignore_budget"]').attr('checked', false);
|
||||
});
|
||||
|
||||
$('input[name="tags"]').on('itemAdded', function(event) {
|
||||
$('input[name="ignore_tags"]').attr('checked', false);
|
||||
|
||||
});
|
||||
|
||||
|
||||
|
||||
});
|
@ -839,12 +839,15 @@ return [
|
||||
'mass_delete_journals' => 'Delete a number of transactions',
|
||||
'mass_edit_journals' => 'Edit a number of transactions',
|
||||
'mass_bulk_journals' => 'Bulk edit a number of transactions',
|
||||
'mass_bulk_journals_explain' => 'If you do not want to change your transactions one-by-one using the mass-edit function, you can update them in one go. Simply select the preferred category, tag(s) or budget in the fields below, and all the transactions in the table will be updated.',
|
||||
'mass_bulk_journals_explain' => 'This form allows you to change properties if the transactions listed below in one sweeping update. All the transactions in the table will be updated when you change the parameters you see here.',
|
||||
'part_of_split' => 'This transaction is part of a split transaction. If you have not selected all the splits, you may end up with changing only half the transaction.',
|
||||
'bulk_set_new_values' => 'Use the inputs below to set new values. If you leave them empty, they will be made empty for all. Also, note that only withdrawals will be given a budget.',
|
||||
'no_bulk_category' => 'Don\'t update category',
|
||||
'no_bulk_budget' => 'Don\'t update budget',
|
||||
'no_bulk_tags' => 'Don\'t update tag(s)',
|
||||
'bulk_edit' => 'Bulk edit',
|
||||
'mass_edit' => 'Edit selected individually',
|
||||
'bulk_edit' => 'Edit selected in bulk',
|
||||
'mass_delete' => 'Delete selected',
|
||||
'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.',
|
||||
'no_budget' => '(no budget)',
|
||||
'no_budget_squared' => '(no budget)',
|
||||
|
@ -50,9 +50,13 @@ var helpPageTitle = "{{ trans('firefly.help_for_this_page')|escape('js') }}";
|
||||
var noHelpForPage = "{{ trans('firefly.no_help_could_be_found')|escape('js') }}";
|
||||
var noHelpForPageTitle = "{{ trans('firefly.no_help_title')|escape('js') }}";
|
||||
|
||||
var edit_selected_txt = "{{ trans('firefly.edit')|escape('js') }}";
|
||||
var edit_selected_txt = "{{ trans('firefly.mass_edit')|escape('js') }}";
|
||||
var edit_bulk_selected_txt = "{{ trans('firefly.bulk_edit')|escape('js') }}";
|
||||
var delete_selected_txt = "{{ trans('firefly.delete')|escape('js') }}";
|
||||
var delete_selected_txt = "{{ trans('firefly.mass_delete')|escape('js') }}";
|
||||
|
||||
var mass_edit_url = '{{ route('transactions.mass.edit', ['']) }}';
|
||||
var bulk_edit_url = '{{ route('transactions.bulk.edit', ['']) }}';
|
||||
var mass_delete_url = '{{ route('transactions.mass.delete', ['']) }}';
|
||||
|
||||
// for demo:
|
||||
var nextLabel = "{{ trans('firefly.intro_next_label')|escape('js') }}";
|
||||
|
@ -8,7 +8,7 @@ TODO: hide and show columns
|
||||
<td colspan="7" class="no-margin-pagination">{{ groups.render|raw }}</td>
|
||||
<td colspan="1">
|
||||
<div class="pull-right">
|
||||
<input id="list_ALL" value="1" name="all" type="checkbox" class="mass-select-all form-check-inline"/>
|
||||
<input id="list_ALL" value="1" name="select-all" type="checkbox" class="select-all form-check-inline"/>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
@ -23,8 +23,16 @@ TODO: hide and show columns
|
||||
</strong></small>
|
||||
</td>
|
||||
<td colspan="2" style="border-top:1px #aaa solid;">
|
||||
{% for sum in group.sums %}
|
||||
{{ formatAmountBySymbol(sum.amount, sum.currency_symbol, sum.currency_symbol_decimal_places) }}{% if loop.index != group.sums|length %},{% endif %}
|
||||
{% for sum in group.sums %}
|
||||
{% if group.transaction_type == 'Deposit' %}
|
||||
{{ formatAmountBySymbol(sum.amount*-1, sum.currency_symbol, sum.currency_symbol_decimal_places) }}{% if loop.index != group.sums|length %},{% endif %}
|
||||
{% elseif group.transaction_type == 'Transfer' %}
|
||||
<span class="text-info">
|
||||
{{ formatAmountBySymbol(sum.amount*-1, sum.currency_symbol, sum.currency_symbol_decimal_places, false) }}{% if loop.index != group.sums|length %},{% endif %}X
|
||||
</span>
|
||||
{% else %}
|
||||
{{ formatAmountBySymbol(sum.amount, sum.currency_symbol, sum.currency_symbol_decimal_places) }}{% if loop.index != group.sums|length %},{% endif %}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</td>
|
||||
<td colspan="2" style="border-top:1px #aaa solid;"> </td>
|
||||
@ -75,9 +83,23 @@ TODO: hide and show columns
|
||||
<a href="{{ route('transactions.show', [group.id]) }}" title="{{ transaction.description }}">{{ transaction.description }}</a>
|
||||
</td>
|
||||
<td style=" {{ style|raw }}">
|
||||
{{ formatAmountBySymbol(transaction.amount, transaction.currency_symbol, transaction.currency_symbol_decimal_places) }}
|
||||
{% if null != transaction.foreign_amount %}
|
||||
({{ formatAmountBySymbol(transaction.foreign_amount, transaction.foreign_currency_symbol, transaction.foreign_currency_symbol_decimal_places) }})
|
||||
{% if transaction.transaction_type_type == 'Deposit' %}
|
||||
{{ formatAmountBySymbol(transaction.amount*-1, transaction.currency_symbol, transaction.currency_symbol_decimal_places) }}
|
||||
{% if null != transaction.foreign_amount %}
|
||||
({{ formatAmountBySymbol(transaction.foreign_amount*-1, transaction.foreign_currency_symbol, transaction.foreign_currency_symbol_decimal_places) }})
|
||||
{% endif %}
|
||||
{% elseif transaction.transaction_type_type == 'Transfer' %}
|
||||
<span class="text-info">
|
||||
{{ formatAmountBySymbol(transaction.amount*-1, transaction.currency_symbol, transaction.currency_symbol_decimal_places, false) }}
|
||||
{% if null != transaction.foreign_amount %}
|
||||
({{ formatAmountBySymbol(transaction.foreign_amount*-1, transaction.foreign_currency_symbol, transaction.foreign_currency_symbol_decimal_places, false) }})
|
||||
{% endif %}
|
||||
</span>
|
||||
{% else %}
|
||||
{{ formatAmountBySymbol(transaction.amount, transaction.currency_symbol, transaction.currency_symbol_decimal_places) }}
|
||||
{% if null != transaction.foreign_amount %}
|
||||
({{ formatAmountBySymbol(transaction.foreign_amount, transaction.foreign_currency_symbol, transaction.foreign_currency_symbol_decimal_places) }})
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
</td>
|
||||
<td style=" {{ style|raw }}">
|
||||
@ -106,7 +128,7 @@ TODO: hide and show columns
|
||||
</td>
|
||||
<td style="{{ style|raw }}">
|
||||
<div class="pull-right">
|
||||
<input id="list_{{ transaction.transaction_journal_id }}" value="1" name="journals[{{ transaction.transaction_journal_id }}]"
|
||||
<input id="list_{{ transaction.transaction_journal_id }}" value="{{ transaction.transaction_journal_id }}" name="journals[{{ transaction.transaction_journal_id }}]"
|
||||
type="checkbox" class="mass-select form-check-inline" data-value="{{ transaction.transaction_journal_id }}"/>
|
||||
</div>
|
||||
</td>
|
||||
@ -119,16 +141,14 @@ TODO: hide and show columns
|
||||
<td colspan="8">
|
||||
<div class="pull-right">
|
||||
<!-- Single button -->
|
||||
<div class="btn-group">
|
||||
<div class="btn-group action-menu" style="display:none;">
|
||||
<button type="button" class="btn btn-default btn-sm dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
||||
{{ 'actions'|_ }} <span class="caret"></span>
|
||||
</button>
|
||||
<ul class="dropdown-menu">
|
||||
<li><a href="#">Edit individually (x)</a></li>
|
||||
<li><a href="#">Bulk edit (x)</a></li>
|
||||
<li><a href="#">Delete (x)</a></li>
|
||||
<li role="separator" class="divider"></li>
|
||||
<li><a href="#">Separated link</a></li>
|
||||
<li><a href="#" class="mass-edit"><i class="fa fa-fw fa-pencil"></i> <span>{{ 'mass_edit'|_ }}</span></a></li>
|
||||
<li><a href="#" class="bulk-edit"><i class="fa fa-fw fa-pencil-square-o"></i> <span>{{ 'bulk_edit'|_ }}</span></a></li>
|
||||
<li><a href="#" class="mass-delete"><i class="fa fa-fw fa-trash"></i> <span>{{ 'mass_delete'|_ }}</span></a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -89,13 +89,13 @@
|
||||
<a href="{{ route('accounts.show', [journal.destination_account_id]) }}" title="{{ journal.destination_account_iban|default(journal.destination_account_name) }}">{{ journal.destination_account_name }}</a>
|
||||
</td>
|
||||
<td class="hide-budget">
|
||||
TODO BUDGET
|
||||
{# TODO BUDGET #}
|
||||
</td>
|
||||
<td class="hide-category">
|
||||
TODO CATEGORY
|
||||
{# TODO CATEGORY#}
|
||||
</td>
|
||||
<td class="hide-bill">
|
||||
TODO BILL
|
||||
{#TODO BILL#}
|
||||
</td>
|
||||
|
||||
<!-- new optional fields (2x) -->
|
||||
|
@ -7,16 +7,19 @@
|
||||
{% block content %}
|
||||
<form method="POST" action="{{ route('transactions.bulk.update') }}" accept-charset="UTF-8" class="form-horizontal" id="update">
|
||||
<input name="_token" type="hidden" value="{{ csrf_token() }}">
|
||||
|
||||
<div class="row">
|
||||
<div class="col-lg-12 col-md-12 col-sm-12 col-xs-12">
|
||||
<div class="box box-default">
|
||||
<div class="box-header with-border">
|
||||
<h3 class="box-title">{{ 'mass_bulk_journals'|_ }}</h3>
|
||||
</div>
|
||||
|
||||
<div class="box-body">
|
||||
<p>
|
||||
{{ 'mass_bulk_journals_explain'|_ }}
|
||||
</p>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-lg-8 col-md-12 col-sm-12 col-xs-12">
|
||||
<table class="table table-striped table-condensed">
|
||||
@ -28,28 +31,63 @@
|
||||
<th>{{ trans('list.category') }}</th>
|
||||
<th>{{ trans('list.budget') }}</th>
|
||||
<th>{{ trans('list.tags') }}</th>
|
||||
<th> </th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for journal in journals %}
|
||||
{% if journal.transaction_count == 2 %}
|
||||
<input type="hidden" name="journals[]" value="{{ journal.id }}"/>
|
||||
<input type="hidden" name="journals[]" value="{{ journal.transaction_journal_id }}"/>
|
||||
<tr>
|
||||
<td>
|
||||
<a href="{{ route('transactions.show', [journal.id]) }}">
|
||||
<a href="{{ route('transactions.show', [journal.transaction_group_id]) }}">
|
||||
{{ journal.description }}</a></td>
|
||||
<td>{{ journal|journalTotalAmount }}</td>
|
||||
<td>
|
||||
{% if journal.transaction_type_type == 'Deposit' %}
|
||||
{{ formatAmountBySymbol(journal.amount*-1, journal.currency_symbol, journal.currency_symbol_decimal_places) }}
|
||||
{% if null != journal.foreign_amount %}
|
||||
({{ formatAmountBySymbol(journal.foreign_amount*-1, journal.foreign_currency_symbol, journal.foreign_currency_symbol_decimal_places) }})
|
||||
{% endif %}
|
||||
{% elseif journal.transaction_type_type == 'Transfer' %}
|
||||
<span class="text-info">
|
||||
{{ formatAmountBySymbol(journal.amount*-1, journal.currency_symbol, journal.currency_symbol_decimal_places, false) }}
|
||||
{% if null != journal.foreign_amount %}
|
||||
({{ formatAmountBySymbol(journal.foreign_amount*-1, journal.foreign_currency_symbol, journal.foreign_currency_symbol_decimal_places, false) }})
|
||||
{% endif %}
|
||||
</span>
|
||||
{% else %}
|
||||
{{ formatAmountBySymbol(journal.amount, journal.currency_symbol, journal.currency_symbol_decimal_places) }}
|
||||
{% if null != journal.foreign_amount %}
|
||||
({{ formatAmountBySymbol(journal.foreign_amount, journal.foreign_currency_symbol, journal.foreign_currency_symbol_decimal_places) }})
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
|
||||
|
||||
</td>
|
||||
<td>{{ journal.date.formatLocalized(monthAndDayFormat) }}</td>
|
||||
<td>{{ journalCategories(journal)|raw }}</td>
|
||||
<td>{{ journalBudgets(journal)|raw }}</td>
|
||||
<td>
|
||||
{% if journal.category_id != null %}
|
||||
<a href="{{ route('categories.show', [journal.category_id]) }}" title="{{ journal.category_name }}">{{ journal.category_name }}</a>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>
|
||||
{% if journal.budget_id != null %}
|
||||
<a href="{{ route('budgets.show', [journal.budget_id]) }}" title="{{ journal.budget_name }}">{{ journal.budget_name }}</a>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>
|
||||
{% for tag in journal.tags %}
|
||||
<a class="label label-success" href="{{ route('tags.show', [tag.id]) }}">
|
||||
<i class="fa fa-fw fa-tag"></i> {{ tag.tag }}</a>
|
||||
<span style="display: inline;"><a class="label label-success" href="{{ route('tags.show', [tag.name]) }}">
|
||||
<i class="fa fa-fw fa-tag"></i>
|
||||
{{ tag.name }}</a>
|
||||
</span>
|
||||
{% endfor %}
|
||||
</td>
|
||||
<td>
|
||||
{% if journal.journals_in_group > 1 %}
|
||||
<i title="{{ 'part_of_split'|_ }}" class="text-danger fa fa-fw fa-exclamation-triangle"></i>
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
@ -107,21 +145,30 @@
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
</table>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box-footer">
|
||||
{% if journals.count > 0 %}
|
||||
<input type="submit" name="submit" value="{{ trans('form.update_all_journals') }}" class="btn btn-success pull-right"/>
|
||||
{% endif %}
|
||||
<a href="{{ route('index') }}" class="btn-default btn">{{ trans('form.cancel') }}</a>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="box-footer">
|
||||
<div class="row">
|
||||
<div class="col-lg-8">
|
||||
<input type="submit" name="submit" value="{{ trans('form.update_all_journals') }}" class="btn btn-success pull-right"/>
|
||||
<a href="{{ route('index') }}" class="btn-default btn">{{ trans('form.cancel') }}</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</form>
|
||||
{% endblock %}
|
||||
{% block scripts %}
|
||||
|
@ -66,5 +66,11 @@
|
||||
|
||||
{% endblock %}
|
||||
{% block scripts %}
|
||||
<script type="text/javascript" src="v1/js/ff/transactions/list.js?v={{ FF_VERSION }}"></script>
|
||||
{# old list script #}
|
||||
{# <script type="text/javascript" src="v1/js/ff/transactions/list.js?v={{ FF_VERSION }}"></script> #}
|
||||
<script type="text/javascript">
|
||||
|
||||
|
||||
</script>
|
||||
<script type="text/javascript" src="v1/js/ff/list/groups.js?v={{ FF_VERSION }}"></script>
|
||||
{% endblock %}
|
||||
|
@ -43,6 +43,7 @@ use FireflyIII\Models\TransactionJournal;
|
||||
use FireflyIII\Models\TransactionJournalLink;
|
||||
use FireflyIII\Models\TransactionType;
|
||||
use FireflyIII\User;
|
||||
use Illuminate\Support\Arr;
|
||||
use Illuminate\Support\Collection;
|
||||
|
||||
if (!function_exists('limitStringLength')) {
|
||||
@ -302,7 +303,8 @@ try {
|
||||
$breadcrumbs->push(
|
||||
trans('firefly.delete_attachment', ['name' => limitStringLength($attachment->filename)]), route('attachments.edit', [$attachment])
|
||||
);
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
throw new FireflyException('Cannot make breadcrumb for attachment connected to object of type ' . get_class($object));
|
||||
}
|
||||
}
|
||||
@ -1120,19 +1122,17 @@ try {
|
||||
// BULK EDIT
|
||||
Breadcrumbs::register(
|
||||
'transactions.bulk.edit',
|
||||
function (BreadcrumbsGenerator $breadcrumbs, Collection $journals): void {
|
||||
if ($journals->count() > 0) {
|
||||
$journalIds = $journals->pluck('id')->toArray();
|
||||
$what = strtolower($journals->first()->transactionType->type);
|
||||
$breadcrumbs->parent('transactions.index', $what);
|
||||
$breadcrumbs->push(trans('firefly.mass_bulk_journals'), route('transactions.bulk.edit', $journalIds));
|
||||
static function (BreadcrumbsGenerator $breadcrumbs, array $journals): void {
|
||||
if (count($journals) > 0) {
|
||||
$ids = Arr::pluck($journals, 'transaction_journal_id');
|
||||
$first = reset($journals);
|
||||
$breadcrumbs->parent('transactions.index', strtolower($first['transaction_type_type']));
|
||||
$breadcrumbs->push(trans('firefly.mass_bulk_journals'), route('transactions.bulk.edit', $ids));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$breadcrumbs->parent('index');
|
||||
|
||||
return;
|
||||
}
|
||||
);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user