mirror of
https://github.com/firefly-iii/firefly-iii.git
synced 2024-11-25 18:30:55 -06:00
Can sort and group bills.
This commit is contained in:
parent
029774687c
commit
e337bcf8bd
@ -32,6 +32,8 @@ use FireflyIII\Models\TransactionCurrency;
|
|||||||
use FireflyIII\Repositories\Bill\BillRepositoryInterface;
|
use FireflyIII\Repositories\Bill\BillRepositoryInterface;
|
||||||
use FireflyIII\Repositories\ObjectGroup\OrganisesObjectGroups;
|
use FireflyIII\Repositories\ObjectGroup\OrganisesObjectGroups;
|
||||||
use FireflyIII\Transformers\BillTransformer;
|
use FireflyIII\Transformers\BillTransformer;
|
||||||
|
use Illuminate\Http\JsonResponse;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
use Symfony\Component\HttpFoundation\ParameterBag;
|
use Symfony\Component\HttpFoundation\ParameterBag;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -174,4 +176,27 @@ class IndexController extends Controller
|
|||||||
|
|
||||||
return $sums;
|
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']);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -195,7 +195,7 @@ class IndexController extends Controller
|
|||||||
*/
|
*/
|
||||||
public function setOrder(Request $request, PiggyBank $piggyBank): JsonResponse
|
public function setOrder(Request $request, PiggyBank $piggyBank): JsonResponse
|
||||||
{
|
{
|
||||||
$objectGroupTitle = $request->get('objectGroupTitle');
|
$objectGroupTitle = (string) $request->get('objectGroupTitle');
|
||||||
$newOrder = (int) $request->get('order');
|
$newOrder = (int) $request->get('order');
|
||||||
$this->piggyRepos->setOrder($piggyBank, $newOrder);
|
$this->piggyRepos->setOrder($piggyBank, $newOrder);
|
||||||
if ('' !== $objectGroupTitle) {
|
if ('' !== $objectGroupTitle) {
|
||||||
|
@ -32,6 +32,7 @@ use FireflyIII\Models\Note;
|
|||||||
use FireflyIII\Models\Transaction;
|
use FireflyIII\Models\Transaction;
|
||||||
use FireflyIII\Models\TransactionJournal;
|
use FireflyIII\Models\TransactionJournal;
|
||||||
use FireflyIII\Repositories\Journal\JournalRepositoryInterface;
|
use FireflyIII\Repositories\Journal\JournalRepositoryInterface;
|
||||||
|
use FireflyIII\Repositories\ObjectGroup\CreatesObjectGroups;
|
||||||
use FireflyIII\Services\Internal\Destroy\BillDestroyService;
|
use FireflyIII\Services\Internal\Destroy\BillDestroyService;
|
||||||
use FireflyIII\Services\Internal\Update\BillUpdateService;
|
use FireflyIII\Services\Internal\Update\BillUpdateService;
|
||||||
use FireflyIII\Support\CacheProperties;
|
use FireflyIII\Support\CacheProperties;
|
||||||
@ -48,6 +49,7 @@ use Storage;
|
|||||||
*/
|
*/
|
||||||
class BillRepository implements BillRepositoryInterface
|
class BillRepository implements BillRepositoryInterface
|
||||||
{
|
{
|
||||||
|
use CreatesObjectGroups;
|
||||||
/** @var User */
|
/** @var User */
|
||||||
private $user;
|
private $user;
|
||||||
|
|
||||||
@ -730,4 +732,36 @@ class BillRepository implements BillRepositoryInterface
|
|||||||
$current++;
|
$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();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -35,6 +35,21 @@ use Illuminate\Support\Collection;
|
|||||||
interface BillRepositoryInterface
|
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
|
* @param Bill $bill
|
||||||
*/
|
*/
|
||||||
@ -45,6 +60,14 @@ interface BillRepositoryInterface
|
|||||||
*/
|
*/
|
||||||
public function correctOrder(): void;
|
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
|
* @param Bill $bill
|
||||||
*
|
*
|
||||||
|
@ -89,12 +89,13 @@ class BillTransformer extends AbstractTransformer
|
|||||||
'currency_symbol' => $currency->symbol,
|
'currency_symbol' => $currency->symbol,
|
||||||
'currency_decimal_places' => $currency->decimal_places,
|
'currency_decimal_places' => $currency->decimal_places,
|
||||||
'name' => $bill->name,
|
'name' => $bill->name,
|
||||||
'amount_min' => round((float)$bill->amount_min, $currency->decimal_places),
|
'amount_min' => round((float) $bill->amount_min, $currency->decimal_places),
|
||||||
'amount_max' => round((float)$bill->amount_max, $currency->decimal_places),
|
'amount_max' => round((float) $bill->amount_max, $currency->decimal_places),
|
||||||
'date' => $bill->date->format('Y-m-d'),
|
'date' => $bill->date->format('Y-m-d'),
|
||||||
'repeat_freq' => $bill->repeat_freq,
|
'repeat_freq' => $bill->repeat_freq,
|
||||||
'skip' => (int)$bill->skip,
|
'skip' => (int) $bill->skip,
|
||||||
'active' => $bill->active,
|
'active' => $bill->active,
|
||||||
|
'order' => (int) $bill->order,
|
||||||
'notes' => $notes,
|
'notes' => $notes,
|
||||||
'next_expected_match' => $paidData['next_expected_match'],
|
'next_expected_match' => $paidData['next_expected_match'],
|
||||||
'pay_dates' => $payDates,
|
'pay_dates' => $payDates,
|
||||||
|
30
composer.lock
generated
30
composer.lock
generated
@ -1414,16 +1414,16 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "laravel/framework",
|
"name": "laravel/framework",
|
||||||
"version": "v7.17.2",
|
"version": "v7.18.0",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/laravel/framework.git",
|
"url": "https://github.com/laravel/framework.git",
|
||||||
"reference": "d16ff3a0a66d98e04163456b39c4b7302cf50a40"
|
"reference": "116b508bafd81de97b1c09744445d32a91076b60"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/laravel/framework/zipball/d16ff3a0a66d98e04163456b39c4b7302cf50a40",
|
"url": "https://api.github.com/repos/laravel/framework/zipball/116b508bafd81de97b1c09744445d32a91076b60",
|
||||||
"reference": "d16ff3a0a66d98e04163456b39c4b7302cf50a40",
|
"reference": "116b508bafd81de97b1c09744445d32a91076b60",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
@ -1567,7 +1567,7 @@
|
|||||||
"framework",
|
"framework",
|
||||||
"laravel"
|
"laravel"
|
||||||
],
|
],
|
||||||
"time": "2020-06-24T13:11:25+00:00"
|
"time": "2020-06-30T13:52:36+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "laravel/passport",
|
"name": "laravel/passport",
|
||||||
@ -1644,16 +1644,16 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "laravel/ui",
|
"name": "laravel/ui",
|
||||||
"version": "v2.0.3",
|
"version": "v2.1.0",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/laravel/ui.git",
|
"url": "https://github.com/laravel/ui.git",
|
||||||
"reference": "15368c5328efb7ce94f35ca750acde9b496ab1b1"
|
"reference": "da9350533d0da60d5dc42fb7de9c561c72129bba"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/laravel/ui/zipball/15368c5328efb7ce94f35ca750acde9b496ab1b1",
|
"url": "https://api.github.com/repos/laravel/ui/zipball/da9350533d0da60d5dc42fb7de9c561c72129bba",
|
||||||
"reference": "15368c5328efb7ce94f35ca750acde9b496ab1b1",
|
"reference": "da9350533d0da60d5dc42fb7de9c561c72129bba",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
@ -1695,7 +1695,7 @@
|
|||||||
"laravel",
|
"laravel",
|
||||||
"ui"
|
"ui"
|
||||||
],
|
],
|
||||||
"time": "2020-04-29T15:06:45+00:00"
|
"time": "2020-06-30T20:56:33+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "laravelcollective/html",
|
"name": "laravelcollective/html",
|
||||||
@ -7443,16 +7443,16 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "myclabs/deep-copy",
|
"name": "myclabs/deep-copy",
|
||||||
"version": "1.10.0",
|
"version": "1.10.1",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/myclabs/DeepCopy.git",
|
"url": "https://github.com/myclabs/DeepCopy.git",
|
||||||
"reference": "5796d127b0c4ff505b77455148ea9d5269d99758"
|
"reference": "969b211f9a51aa1f6c01d1d2aef56d3bd91598e5"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/5796d127b0c4ff505b77455148ea9d5269d99758",
|
"url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/969b211f9a51aa1f6c01d1d2aef56d3bd91598e5",
|
||||||
"reference": "5796d127b0c4ff505b77455148ea9d5269d99758",
|
"reference": "969b211f9a51aa1f6c01d1d2aef56d3bd91598e5",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
@ -7493,7 +7493,7 @@
|
|||||||
"type": "tidelift"
|
"type": "tidelift"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"time": "2020-06-28T07:02:41+00:00"
|
"time": "2020-06-29T13:22:24+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "netresearch/jsonmapper",
|
"name": "netresearch/jsonmapper",
|
||||||
|
3
public/v1/css/firefly.css
vendored
3
public/v1/css/firefly.css
vendored
@ -93,6 +93,9 @@ p.tagcloud .label {
|
|||||||
.rule-handle {
|
.rule-handle {
|
||||||
cursor: move;
|
cursor: move;
|
||||||
}
|
}
|
||||||
|
.bill-handle {
|
||||||
|
cursor: move;
|
||||||
|
}
|
||||||
|
|
||||||
body.waiting * {
|
body.waiting * {
|
||||||
cursor: progress;
|
cursor: progress;
|
||||||
|
91
public/v1/js/ff/bills/index.js
vendored
Normal file
91
public/v1/js/ff/bills/index.js
vendored
Normal file
@ -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 <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
/** 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('<td colspan="' + cellCount + '"> </td>');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
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})
|
||||||
|
});
|
||||||
|
}
|
@ -41,8 +41,6 @@
|
|||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block scripts %}
|
{% block scripts %}
|
||||||
<script type="text/javascript" nonce="{{ JS_NONCE }}">
|
<script src="v1/js/lib/jquery-ui.min.js?v={{ FF_VERSION }}" type="text/javascript" nonce="{{ JS_NONCE }}"></script>
|
||||||
var start = '2018-01-01';
|
<script type="text/javascript" src="v1/js/ff/bills/index.js?v={{ FF_VERSION }}" nonce="{{ JS_NONCE }}"></script>
|
||||||
var end = '2018-01-31';
|
|
||||||
</script>
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
<div style="padding-left:8px;">
|
<div style="padding-left:8px;">
|
||||||
{{ paginator.render|raw }}
|
{{ paginator.render|raw }}
|
||||||
</div>
|
</div>
|
||||||
<table class="table table-hover">
|
<table class="table table-hover" id="bill-sortable">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th class="hidden-sm hidden-xs"> </th>
|
<th class="hidden-sm hidden-xs"> </th>
|
||||||
@ -23,7 +23,7 @@
|
|||||||
<td colspan="6"><small>{{ objectGroup.object_group_title }}</small></td>
|
<td colspan="6"><small>{{ objectGroup.object_group_title }}</small></td>
|
||||||
</tr>
|
</tr>
|
||||||
{% for entry in objectGroup.bills %}
|
{% for entry in objectGroup.bills %}
|
||||||
<tr{% if not entry.active %} data-disablesort="true"{% endif %}>
|
<tr class="bill-sortable" data-id="{{ entry.id }}" data-name="{{ entry.name }}" data-order="{{ entry.order }}" data-position="{{ loop.index0 }}">
|
||||||
<td class="hidden-sm hidden-xs">
|
<td class="hidden-sm hidden-xs">
|
||||||
<i class="fa fa-fw fa-bars bill-handle"></i>
|
<i class="fa fa-fw fa-bars bill-handle"></i>
|
||||||
</td>
|
</td>
|
||||||
|
@ -194,6 +194,8 @@ Route::group(
|
|||||||
Route::post('store', ['uses' => 'Bill\CreateController@store', 'as' => 'store']);
|
Route::post('store', ['uses' => 'Bill\CreateController@store', 'as' => 'store']);
|
||||||
Route::post('update/{bill}', ['uses' => 'Bill\EditController@update', 'as' => 'update']);
|
Route::post('update/{bill}', ['uses' => 'Bill\EditController@update', 'as' => 'update']);
|
||||||
Route::post('destroy/{bill}', ['uses' => 'Bill\DeleteController@destroy', 'as' => 'destroy']);
|
Route::post('destroy/{bill}', ['uses' => 'Bill\DeleteController@destroy', 'as' => 'destroy']);
|
||||||
|
|
||||||
|
Route::post('set-order/{bill}', ['uses' => 'Bill\IndexController@setOrder', 'as' => 'set-order']);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user