From e337bcf8bd0c705fedd29322b5c03c51b18b8d36 Mon Sep 17 00:00:00 2001 From: James Cole Date: Wed, 1 Jul 2020 06:33:21 +0200 Subject: [PATCH] Can sort and group bills. --- app/Http/Controllers/Bill/IndexController.php | 25 +++++ .../Controllers/PiggyBank/IndexController.php | 2 +- app/Repositories/Bill/BillRepository.php | 34 +++++++ .../Bill/BillRepositoryInterface.php | 23 +++++ app/Transformers/BillTransformer.php | 7 +- composer.lock | 30 +++--- public/v1/css/firefly.css | 3 + public/v1/js/ff/bills/index.js | 91 +++++++++++++++++++ resources/views/v1/bills/index.twig | 6 +- resources/views/v1/list/bills.twig | 4 +- routes/web.php | 2 + 11 files changed, 202 insertions(+), 25 deletions(-) create mode 100644 public/v1/js/ff/bills/index.js diff --git a/app/Http/Controllers/Bill/IndexController.php b/app/Http/Controllers/Bill/IndexController.php index e169953d46..f7f3edc381 100644 --- a/app/Http/Controllers/Bill/IndexController.php +++ b/app/Http/Controllers/Bill/IndexController.php @@ -32,6 +32,8 @@ use FireflyIII\Models\TransactionCurrency; use FireflyIII\Repositories\Bill\BillRepositoryInterface; use FireflyIII\Repositories\ObjectGroup\OrganisesObjectGroups; use FireflyIII\Transformers\BillTransformer; +use Illuminate\Http\JsonResponse; +use Illuminate\Http\Request; use Symfony\Component\HttpFoundation\ParameterBag; /** @@ -174,4 +176,27 @@ class IndexController extends Controller return $sums; } + + /** + * Set the order of a bill. + * + * @param Request $request + * @param Bill $bill + * + * @return JsonResponse + */ + public function setOrder(Request $request, Bill $bill): JsonResponse + { + $objectGroupTitle = (string)$request->get('objectGroupTitle'); + $newOrder = (int) $request->get('order'); + $this->repository->setOrder($bill, $newOrder); + if ('' !== $objectGroupTitle) { + $this->repository->setObjectGroup($bill, $objectGroupTitle); + } + if ('' === $objectGroupTitle) { + $this->repository->removeObjectGroup($bill); + } + + return response()->json(['data' => 'OK']); + } } diff --git a/app/Http/Controllers/PiggyBank/IndexController.php b/app/Http/Controllers/PiggyBank/IndexController.php index f6b4c5b03f..0687afa383 100644 --- a/app/Http/Controllers/PiggyBank/IndexController.php +++ b/app/Http/Controllers/PiggyBank/IndexController.php @@ -195,7 +195,7 @@ class IndexController extends Controller */ public function setOrder(Request $request, PiggyBank $piggyBank): JsonResponse { - $objectGroupTitle = $request->get('objectGroupTitle'); + $objectGroupTitle = (string) $request->get('objectGroupTitle'); $newOrder = (int) $request->get('order'); $this->piggyRepos->setOrder($piggyBank, $newOrder); if ('' !== $objectGroupTitle) { diff --git a/app/Repositories/Bill/BillRepository.php b/app/Repositories/Bill/BillRepository.php index 1fb3a1c0bf..4a14a7025e 100644 --- a/app/Repositories/Bill/BillRepository.php +++ b/app/Repositories/Bill/BillRepository.php @@ -32,6 +32,7 @@ use FireflyIII\Models\Note; use FireflyIII\Models\Transaction; use FireflyIII\Models\TransactionJournal; use FireflyIII\Repositories\Journal\JournalRepositoryInterface; +use FireflyIII\Repositories\ObjectGroup\CreatesObjectGroups; use FireflyIII\Services\Internal\Destroy\BillDestroyService; use FireflyIII\Services\Internal\Update\BillUpdateService; use FireflyIII\Support\CacheProperties; @@ -48,6 +49,7 @@ use Storage; */ class BillRepository implements BillRepositoryInterface { + use CreatesObjectGroups; /** @var User */ private $user; @@ -730,4 +732,36 @@ class BillRepository implements BillRepositoryInterface $current++; } } + + /** + * @inheritDoc + */ + public function setObjectGroup(Bill $bill, string $objectGroupTitle): Bill + { + $objectGroup = $this->findOrCreateObjectGroup($objectGroupTitle); + if (null !== $objectGroup) { + $bill->objectGroups()->sync([$objectGroup->id]); + } + + return $bill; + } + + /** + * @inheritDoc + */ + public function removeObjectGroup(Bill $bill): Bill + { + $bill->objectGroups()->sync([]); + + return $bill; + } + + /** + * @inheritDoc + */ + public function setOrder(Bill $bill, int $order): void + { + $bill->order = $order; + $bill->save(); + } } diff --git a/app/Repositories/Bill/BillRepositoryInterface.php b/app/Repositories/Bill/BillRepositoryInterface.php index dd5a090c57..91952f9fe7 100644 --- a/app/Repositories/Bill/BillRepositoryInterface.php +++ b/app/Repositories/Bill/BillRepositoryInterface.php @@ -35,6 +35,21 @@ use Illuminate\Support\Collection; interface BillRepositoryInterface { + /** + * @param Bill $bill + * @param string $objectGroupTitle + * + * @return Bill + */ + public function setObjectGroup(Bill $bill, string $objectGroupTitle): Bill; + + /** + * @param Bill $bill + * + * @return Bill + */ + public function removeObjectGroup(Bill $bill): Bill; + /** * @param Bill $bill */ @@ -45,6 +60,14 @@ interface BillRepositoryInterface */ public function correctOrder(): void; + /** + * Set specific piggy bank to specific order. + * + * @param Bill $bill + * @param int $order + */ + public function setOrder(Bill $bill, int $order): void; + /** * @param Bill $bill * diff --git a/app/Transformers/BillTransformer.php b/app/Transformers/BillTransformer.php index f2d4afbee4..3ee1cef220 100644 --- a/app/Transformers/BillTransformer.php +++ b/app/Transformers/BillTransformer.php @@ -89,12 +89,13 @@ class BillTransformer extends AbstractTransformer 'currency_symbol' => $currency->symbol, 'currency_decimal_places' => $currency->decimal_places, 'name' => $bill->name, - 'amount_min' => round((float)$bill->amount_min, $currency->decimal_places), - 'amount_max' => round((float)$bill->amount_max, $currency->decimal_places), + 'amount_min' => round((float) $bill->amount_min, $currency->decimal_places), + 'amount_max' => round((float) $bill->amount_max, $currency->decimal_places), 'date' => $bill->date->format('Y-m-d'), 'repeat_freq' => $bill->repeat_freq, - 'skip' => (int)$bill->skip, + 'skip' => (int) $bill->skip, 'active' => $bill->active, + 'order' => (int) $bill->order, 'notes' => $notes, 'next_expected_match' => $paidData['next_expected_match'], 'pay_dates' => $payDates, diff --git a/composer.lock b/composer.lock index 35fd319c14..423bce082b 100644 --- a/composer.lock +++ b/composer.lock @@ -1414,16 +1414,16 @@ }, { "name": "laravel/framework", - "version": "v7.17.2", + "version": "v7.18.0", "source": { "type": "git", "url": "https://github.com/laravel/framework.git", - "reference": "d16ff3a0a66d98e04163456b39c4b7302cf50a40" + "reference": "116b508bafd81de97b1c09744445d32a91076b60" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/framework/zipball/d16ff3a0a66d98e04163456b39c4b7302cf50a40", - "reference": "d16ff3a0a66d98e04163456b39c4b7302cf50a40", + "url": "https://api.github.com/repos/laravel/framework/zipball/116b508bafd81de97b1c09744445d32a91076b60", + "reference": "116b508bafd81de97b1c09744445d32a91076b60", "shasum": "" }, "require": { @@ -1567,7 +1567,7 @@ "framework", "laravel" ], - "time": "2020-06-24T13:11:25+00:00" + "time": "2020-06-30T13:52:36+00:00" }, { "name": "laravel/passport", @@ -1644,16 +1644,16 @@ }, { "name": "laravel/ui", - "version": "v2.0.3", + "version": "v2.1.0", "source": { "type": "git", "url": "https://github.com/laravel/ui.git", - "reference": "15368c5328efb7ce94f35ca750acde9b496ab1b1" + "reference": "da9350533d0da60d5dc42fb7de9c561c72129bba" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/ui/zipball/15368c5328efb7ce94f35ca750acde9b496ab1b1", - "reference": "15368c5328efb7ce94f35ca750acde9b496ab1b1", + "url": "https://api.github.com/repos/laravel/ui/zipball/da9350533d0da60d5dc42fb7de9c561c72129bba", + "reference": "da9350533d0da60d5dc42fb7de9c561c72129bba", "shasum": "" }, "require": { @@ -1695,7 +1695,7 @@ "laravel", "ui" ], - "time": "2020-04-29T15:06:45+00:00" + "time": "2020-06-30T20:56:33+00:00" }, { "name": "laravelcollective/html", @@ -7443,16 +7443,16 @@ }, { "name": "myclabs/deep-copy", - "version": "1.10.0", + "version": "1.10.1", "source": { "type": "git", "url": "https://github.com/myclabs/DeepCopy.git", - "reference": "5796d127b0c4ff505b77455148ea9d5269d99758" + "reference": "969b211f9a51aa1f6c01d1d2aef56d3bd91598e5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/5796d127b0c4ff505b77455148ea9d5269d99758", - "reference": "5796d127b0c4ff505b77455148ea9d5269d99758", + "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/969b211f9a51aa1f6c01d1d2aef56d3bd91598e5", + "reference": "969b211f9a51aa1f6c01d1d2aef56d3bd91598e5", "shasum": "" }, "require": { @@ -7493,7 +7493,7 @@ "type": "tidelift" } ], - "time": "2020-06-28T07:02:41+00:00" + "time": "2020-06-29T13:22:24+00:00" }, { "name": "netresearch/jsonmapper", diff --git a/public/v1/css/firefly.css b/public/v1/css/firefly.css index c4f3ba3cbf..520c32df7a 100644 --- a/public/v1/css/firefly.css +++ b/public/v1/css/firefly.css @@ -93,6 +93,9 @@ p.tagcloud .label { .rule-handle { cursor: move; } +.bill-handle { + cursor: move; +} body.waiting * { cursor: progress; diff --git a/public/v1/js/ff/bills/index.js b/public/v1/js/ff/bills/index.js new file mode 100644 index 0000000000..9cd07239fc --- /dev/null +++ b/public/v1/js/ff/bills/index.js @@ -0,0 +1,91 @@ +/* + * index.js + * Copyright (c) 2019 james@firefly-iii.org + * + * This file is part of Firefly III (https://github.com/firefly-iii). + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program 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 Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +/** global: token */ +var fixBillHelper = function (e, tr) { + "use strict"; + var $originals = tr.children(); + var $helper = tr.clone(); + $helper.children().each(function (index) { + // Set helper cell sizes to match the original sizes + $(this).width($originals.eq(index).width()); + }); + return $helper; +}; + +$(function () { + "use strict"; + $('#bill-sortable').find('tbody').sortable( + { + helper: fixBillHelper, + stop: stopSorting, + connectWith: '.bill-connected-list', + items: 'tr.bill-sortable', + handle: '.bill-handle', + start: function (event, ui) { + // Build a placeholder cell that spans all the cells in the row + var cellCount = 0; + $('td, th', ui.helper).each(function () { + // For each TD or TH try and get it's colspan attribute, and add that or 1 to the total + var colspan = 1; + var colspanAttr = $(this).attr('colspan'); + if (colspanAttr > 1) { + colspan = colspanAttr; + } + cellCount += colspan; + }); + + // Add the placeholder UI - note that this is the item's content, so TD rather than TR + ui.placeholder.html(' '); + } + } + ); +}); + + +function stopSorting() { + "use strict"; + + $.each($('#bill-sortable>tbody>tr.bill-sortable'), function (i, v) { + var holder = $(v); + var parentBody = holder.parent(); + var objectGroupTitle = parentBody.data('title'); + var position = parseInt(holder.data('position')); + var originalOrder = parseInt(holder.data('order')); + var name = holder.data('name'); + var id = parseInt(holder.data('id')); + var newOrder; + if (position === i) { + // not changed, position is what it should be. + return; + } + if (position < i) { + // position is less. + console.log('"' + name + '" ("' + objectGroupTitle + '") has moved down from position ' + originalOrder + ' to ' + (i + 1)); + } + if (position > i) { + console.log('"' + name + '" ("' + objectGroupTitle + '") has moved up from position ' + originalOrder + ' to ' + (i + 1)); + } + // update position: + holder.data('position', i); + newOrder = i+1; + + $.post('bills/set-order/' + id, {order: newOrder, objectGroupTitle: objectGroupTitle, _token: token}) + }); +} diff --git a/resources/views/v1/bills/index.twig b/resources/views/v1/bills/index.twig index d8d2e156be..bd52d986eb 100644 --- a/resources/views/v1/bills/index.twig +++ b/resources/views/v1/bills/index.twig @@ -41,8 +41,6 @@ {% endblock %} {% block scripts %} - + + {% endblock %} diff --git a/resources/views/v1/list/bills.twig b/resources/views/v1/list/bills.twig index 0c99f3c398..f51b54ffd8 100644 --- a/resources/views/v1/list/bills.twig +++ b/resources/views/v1/list/bills.twig @@ -1,7 +1,7 @@
{{ paginator.render|raw }}
- +
@@ -23,7 +23,7 @@ {% for entry in objectGroup.bills %} - + diff --git a/routes/web.php b/routes/web.php index f505e49d67..87c339eafb 100644 --- a/routes/web.php +++ b/routes/web.php @@ -194,6 +194,8 @@ Route::group( Route::post('store', ['uses' => 'Bill\CreateController@store', 'as' => 'store']); Route::post('update/{bill}', ['uses' => 'Bill\EditController@update', 'as' => 'update']); Route::post('destroy/{bill}', ['uses' => 'Bill\DeleteController@destroy', 'as' => 'destroy']); + + Route::post('set-order/{bill}', ['uses' => 'Bill\IndexController@setOrder', 'as' => 'set-order']); } );
{{ objectGroup.object_group_title }}