mirror of
https://github.com/firefly-iii/firefly-iii.git
synced 2025-02-25 18:45:27 -06:00
Give bills groups too.
This commit is contained in:
parent
bb5de8bf7e
commit
fad2331d80
@ -27,6 +27,7 @@ namespace FireflyIII\Factory;
|
|||||||
use FireflyIII\Exceptions\FireflyException;
|
use FireflyIII\Exceptions\FireflyException;
|
||||||
use FireflyIII\Models\Bill;
|
use FireflyIII\Models\Bill;
|
||||||
use FireflyIII\Models\TransactionCurrency;
|
use FireflyIII\Models\TransactionCurrency;
|
||||||
|
use FireflyIII\Repositories\ObjectGroup\CreatesObjectGroups;
|
||||||
use FireflyIII\Services\Internal\Support\BillServiceTrait;
|
use FireflyIII\Services\Internal\Support\BillServiceTrait;
|
||||||
use FireflyIII\User;
|
use FireflyIII\User;
|
||||||
use Illuminate\Database\QueryException;
|
use Illuminate\Database\QueryException;
|
||||||
@ -37,7 +38,7 @@ use Log;
|
|||||||
*/
|
*/
|
||||||
class BillFactory
|
class BillFactory
|
||||||
{
|
{
|
||||||
use BillServiceTrait;
|
use BillServiceTrait, CreatesObjectGroups;
|
||||||
|
|
||||||
/** @var User */
|
/** @var User */
|
||||||
private $user;
|
private $user;
|
||||||
@ -97,6 +98,24 @@ class BillFactory
|
|||||||
$this->updateNote($bill, $data['notes']);
|
$this->updateNote($bill, $data['notes']);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$objectGroupTitle = $data['object_group'] ?? '';
|
||||||
|
if ('' !== $objectGroupTitle) {
|
||||||
|
$objectGroup = $this->findOrCreateObjectGroup($objectGroupTitle);
|
||||||
|
if (null !== $objectGroup) {
|
||||||
|
$bill->objectGroups()->sync([$objectGroup->id]);
|
||||||
|
$bill->save();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// try also with ID:
|
||||||
|
$objectGroupId = (int) ($data['object_group_id'] ?? 0);
|
||||||
|
if (0 !== $objectGroupId) {
|
||||||
|
$objectGroup = $this->findObjectGroupById($objectGroupId);
|
||||||
|
if (null !== $objectGroup) {
|
||||||
|
$bill->objectGroups()->sync([$objectGroup->id]);
|
||||||
|
$bill->save();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return $bill;
|
return $bill;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
130
app/Http/Controllers/Bill/CreateController.php
Normal file
130
app/Http/Controllers/Bill/CreateController.php
Normal file
@ -0,0 +1,130 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* CreateController.php
|
||||||
|
* Copyright (c) 2020 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/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace FireflyIII\Http\Controllers\Bill;
|
||||||
|
|
||||||
|
|
||||||
|
use FireflyIII\Exceptions\FireflyException;
|
||||||
|
use FireflyIII\Helpers\Attachments\AttachmentHelperInterface;
|
||||||
|
use FireflyIII\Http\Controllers\Controller;
|
||||||
|
use FireflyIII\Http\Requests\BillStoreRequest;
|
||||||
|
use FireflyIII\Repositories\Bill\BillRepositoryInterface;
|
||||||
|
use Illuminate\Http\RedirectResponse;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class CreateController
|
||||||
|
*/
|
||||||
|
class CreateController extends Controller
|
||||||
|
{
|
||||||
|
private AttachmentHelperInterface $attachments;
|
||||||
|
private BillRepositoryInterface $repository;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* BillController constructor.
|
||||||
|
*
|
||||||
|
* @codeCoverageIgnore
|
||||||
|
*/
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
parent::__construct();
|
||||||
|
|
||||||
|
$this->middleware(
|
||||||
|
function ($request, $next) {
|
||||||
|
app('view')->share('title', (string) trans('firefly.bills'));
|
||||||
|
app('view')->share('mainTitleIcon', 'fa-calendar-o');
|
||||||
|
$this->attachments = app(AttachmentHelperInterface::class);
|
||||||
|
$this->repository = app(BillRepositoryInterface::class);
|
||||||
|
|
||||||
|
return $next($request);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new bill.
|
||||||
|
*
|
||||||
|
* @param Request $request
|
||||||
|
*
|
||||||
|
* @return \Illuminate\Contracts\Foundation\Application|\Illuminate\Contracts\View\Factory|\Illuminate\View\View
|
||||||
|
*/
|
||||||
|
public function create(Request $request)
|
||||||
|
{
|
||||||
|
$periods = [];
|
||||||
|
/** @var array $billPeriods */
|
||||||
|
$billPeriods = config('firefly.bill_periods');
|
||||||
|
foreach ($billPeriods as $current) {
|
||||||
|
$periods[$current] = strtolower((string) trans('firefly.repeat_freq_' . $current));
|
||||||
|
}
|
||||||
|
$subTitle = (string) trans('firefly.create_new_bill');
|
||||||
|
$defaultCurrency = app('amount')->getDefaultCurrency();
|
||||||
|
|
||||||
|
// put previous url in session if not redirect from store (not "create another").
|
||||||
|
if (true !== session('bills.create.fromStore')) {
|
||||||
|
$this->rememberPreviousUri('bills.create.uri');
|
||||||
|
}
|
||||||
|
$request->session()->forget('bills.create.fromStore');
|
||||||
|
|
||||||
|
return view('bills.create', compact('periods', 'subTitle', 'defaultCurrency'));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Store a new bill.
|
||||||
|
*
|
||||||
|
* @param BillStoreRequest $request
|
||||||
|
*
|
||||||
|
* @return RedirectResponse
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public function store(BillStoreRequest $request): RedirectResponse
|
||||||
|
{
|
||||||
|
$billData = $request->getBillData();
|
||||||
|
$billData['active'] = true;
|
||||||
|
try {
|
||||||
|
$bill = $this->repository->store($billData);
|
||||||
|
} catch (FireflyException $e) {
|
||||||
|
Log::error($e->getMessage());
|
||||||
|
$request->session()->flash('error', (string) trans('firefly.bill_store_error'));
|
||||||
|
|
||||||
|
return redirect(route('bills.create'))->withInput();
|
||||||
|
}
|
||||||
|
$request->session()->flash('success', (string) trans('firefly.stored_new_bill', ['name' => $bill->name]));
|
||||||
|
app('preferences')->mark();
|
||||||
|
|
||||||
|
/** @var array $files */
|
||||||
|
$files = $request->hasFile('attachments') ? $request->file('attachments') : null;
|
||||||
|
if (null !== $files && !auth()->user()->hasRole('demo')) {
|
||||||
|
$this->attachments->saveAttachmentsForModel($bill, $files);
|
||||||
|
}
|
||||||
|
if (null !== $files && auth()->user()->hasRole('demo')) {
|
||||||
|
session()->flash('info', (string) trans('firefly.no_att_demo_user'));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (count($this->attachments->getMessages()->get('attachments')) > 0) {
|
||||||
|
$request->session()->flash('info', $this->attachments->getMessages()->get('attachments')); // @codeCoverageIgnore
|
||||||
|
}
|
||||||
|
|
||||||
|
return redirect(route('rules.create-from-bill', [$bill->id]));
|
||||||
|
}
|
||||||
|
}
|
99
app/Http/Controllers/Bill/DeleteController.php
Normal file
99
app/Http/Controllers/Bill/DeleteController.php
Normal file
@ -0,0 +1,99 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* DeleteController.php
|
||||||
|
* Copyright (c) 2020 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/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace FireflyIII\Http\Controllers\Bill;
|
||||||
|
|
||||||
|
|
||||||
|
use FireflyIII\Http\Controllers\Controller;
|
||||||
|
use FireflyIII\Models\Bill;
|
||||||
|
use FireflyIII\Repositories\Bill\BillRepositoryInterface;
|
||||||
|
use Illuminate\Contracts\View\Factory;
|
||||||
|
use Illuminate\Http\RedirectResponse;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
use Illuminate\Routing\Redirector;
|
||||||
|
use Illuminate\View\View;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class DeleteController
|
||||||
|
*/
|
||||||
|
class DeleteController extends Controller
|
||||||
|
{
|
||||||
|
private BillRepositoryInterface $repository;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* BillController constructor.
|
||||||
|
*
|
||||||
|
* @codeCoverageIgnore
|
||||||
|
*/
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
parent::__construct();
|
||||||
|
|
||||||
|
app('view')->share('showBudget', true);
|
||||||
|
|
||||||
|
$this->middleware(
|
||||||
|
function ($request, $next) {
|
||||||
|
app('view')->share('title', (string) trans('firefly.bills'));
|
||||||
|
app('view')->share('mainTitleIcon', 'fa-calendar-o');
|
||||||
|
$this->repository = app(BillRepositoryInterface::class);
|
||||||
|
|
||||||
|
return $next($request);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Delete a bill.
|
||||||
|
*
|
||||||
|
* @param Bill $bill
|
||||||
|
*
|
||||||
|
* @return Factory|View
|
||||||
|
*/
|
||||||
|
public function delete(Bill $bill)
|
||||||
|
{
|
||||||
|
// put previous url in session
|
||||||
|
$this->rememberPreviousUri('bills.delete.uri');
|
||||||
|
$subTitle = (string) trans('firefly.delete_bill', ['name' => $bill->name]);
|
||||||
|
|
||||||
|
return view('bills.delete', compact('bill', 'subTitle'));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Destroy a bill.
|
||||||
|
*
|
||||||
|
* @param Request $request
|
||||||
|
* @param Bill $bill
|
||||||
|
*
|
||||||
|
* @return RedirectResponse|Redirector
|
||||||
|
*/
|
||||||
|
public function destroy(Request $request, Bill $bill)
|
||||||
|
{
|
||||||
|
$name = $bill->name;
|
||||||
|
$this->repository->destroy($bill);
|
||||||
|
|
||||||
|
$request->session()->flash('success', (string) trans('firefly.deleted_bill', ['name' => $name]));
|
||||||
|
app('preferences')->mark();
|
||||||
|
|
||||||
|
return redirect($this->getPreviousUri('bills.delete.uri'));
|
||||||
|
}
|
||||||
|
}
|
155
app/Http/Controllers/Bill/EditController.php
Normal file
155
app/Http/Controllers/Bill/EditController.php
Normal file
@ -0,0 +1,155 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* EditController.php
|
||||||
|
* Copyright (c) 2020 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/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace FireflyIII\Http\Controllers\Bill;
|
||||||
|
|
||||||
|
|
||||||
|
use FireflyIII\Helpers\Attachments\AttachmentHelperInterface;
|
||||||
|
use FireflyIII\Http\Controllers\Controller;
|
||||||
|
use FireflyIII\Http\Requests\BillUpdateRequest;
|
||||||
|
use FireflyIII\Models\Bill;
|
||||||
|
use FireflyIII\Repositories\Bill\BillRepositoryInterface;
|
||||||
|
use Illuminate\Http\RedirectResponse;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class EditController
|
||||||
|
*/
|
||||||
|
class EditController extends Controller
|
||||||
|
{
|
||||||
|
private AttachmentHelperInterface $attachments;
|
||||||
|
private BillRepositoryInterface $repository;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* BillController constructor.
|
||||||
|
*
|
||||||
|
* @codeCoverageIgnore
|
||||||
|
*/
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
parent::__construct();
|
||||||
|
|
||||||
|
$this->middleware(
|
||||||
|
function ($request, $next) {
|
||||||
|
app('view')->share('title', (string) trans('firefly.bills'));
|
||||||
|
app('view')->share('mainTitleIcon', 'fa-calendar-o');
|
||||||
|
$this->attachments = app(AttachmentHelperInterface::class);
|
||||||
|
$this->repository = app(BillRepositoryInterface::class);
|
||||||
|
|
||||||
|
return $next($request);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Edit a bill.
|
||||||
|
*
|
||||||
|
* @param Request $request
|
||||||
|
* @param Bill $bill
|
||||||
|
*
|
||||||
|
* @return \Illuminate\Contracts\Foundation\Application|\Illuminate\Contracts\View\Factory|\Illuminate\View\View
|
||||||
|
*/
|
||||||
|
public function edit(Request $request, Bill $bill)
|
||||||
|
{
|
||||||
|
$periods = [];
|
||||||
|
/** @var array $billPeriods */
|
||||||
|
$billPeriods = config('firefly.bill_periods');
|
||||||
|
|
||||||
|
foreach ($billPeriods as $current) {
|
||||||
|
$periods[$current] = (string) trans('firefly.' . $current);
|
||||||
|
}
|
||||||
|
|
||||||
|
$subTitle = (string) trans('firefly.edit_bill', ['name' => $bill->name]);
|
||||||
|
|
||||||
|
// put previous url in session if not redirect from store (not "return_to_edit").
|
||||||
|
if (true !== session('bills.edit.fromUpdate')) {
|
||||||
|
$this->rememberPreviousUri('bills.edit.uri');
|
||||||
|
}
|
||||||
|
|
||||||
|
$currency = app('amount')->getDefaultCurrency();
|
||||||
|
$bill->amount_min = round((float) $bill->amount_min, $currency->decimal_places);
|
||||||
|
$bill->amount_max = round((float) $bill->amount_max, $currency->decimal_places);
|
||||||
|
$rules = $this->repository->getRulesForBill($bill);
|
||||||
|
$defaultCurrency = app('amount')->getDefaultCurrency();
|
||||||
|
|
||||||
|
// code to handle active-checkboxes
|
||||||
|
$hasOldInput = null !== $request->old('_token');
|
||||||
|
|
||||||
|
$preFilled = [
|
||||||
|
'notes' => $this->repository->getNoteText($bill),
|
||||||
|
'transaction_currency_id' => $bill->transaction_currency_id,
|
||||||
|
'active' => $hasOldInput ? (bool) $request->old('active') : $bill->active,
|
||||||
|
'object_group' => $bill->objectGroups->first() ? $bill->objectGroups->first()->title : '',
|
||||||
|
];
|
||||||
|
|
||||||
|
$request->session()->flash('preFilled', $preFilled);
|
||||||
|
$request->session()->forget('bills.edit.fromUpdate');
|
||||||
|
|
||||||
|
return view('bills.edit', compact('subTitle', 'periods', 'rules', 'bill', 'defaultCurrency', 'preFilled'));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update a bill.
|
||||||
|
*
|
||||||
|
* @param BillUpdateRequest $request
|
||||||
|
* @param Bill $bill
|
||||||
|
*
|
||||||
|
* @return RedirectResponse
|
||||||
|
*/
|
||||||
|
public function update(BillUpdateRequest $request, Bill $bill): RedirectResponse
|
||||||
|
{
|
||||||
|
$billData = $request->getBillData();
|
||||||
|
$bill = $this->repository->update($bill, $billData);
|
||||||
|
|
||||||
|
$request->session()->flash('success', (string) trans('firefly.updated_bill', ['name' => $bill->name]));
|
||||||
|
app('preferences')->mark();
|
||||||
|
|
||||||
|
/** @var array $files */
|
||||||
|
$files = $request->hasFile('attachments') ? $request->file('attachments') : null;
|
||||||
|
if (null !== $files && !auth()->user()->hasRole('demo')) {
|
||||||
|
$this->attachments->saveAttachmentsForModel($bill, $files);
|
||||||
|
}
|
||||||
|
if (null !== $files && auth()->user()->hasRole('demo')) {
|
||||||
|
session()->flash('info',(string)trans('firefly.no_att_demo_user'));
|
||||||
|
}
|
||||||
|
|
||||||
|
// flash messages
|
||||||
|
if (count($this->attachments->getMessages()->get('attachments')) > 0) {
|
||||||
|
$request->session()->flash('info', $this->attachments->getMessages()->get('attachments')); // @codeCoverageIgnore
|
||||||
|
}
|
||||||
|
$redirect = redirect($this->getPreviousUri('bills.edit.uri'));
|
||||||
|
|
||||||
|
if (1 === (int) $request->get('return_to_edit')) {
|
||||||
|
// @codeCoverageIgnoreStart
|
||||||
|
$request->session()->put('bills.edit.fromUpdate', true);
|
||||||
|
|
||||||
|
$redirect = redirect(route('bills.edit', [$bill->id]))->withInput(['return_to_edit' => 1]);
|
||||||
|
// @codeCoverageIgnoreEnd
|
||||||
|
}
|
||||||
|
|
||||||
|
return $redirect;
|
||||||
|
}
|
||||||
|
}
|
171
app/Http/Controllers/Bill/IndexController.php
Normal file
171
app/Http/Controllers/Bill/IndexController.php
Normal file
@ -0,0 +1,171 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* IndexController.php
|
||||||
|
* Copyright (c) 2020 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/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace FireflyIII\Http\Controllers\Bill;
|
||||||
|
|
||||||
|
|
||||||
|
use Carbon\Carbon;
|
||||||
|
use FireflyIII\Http\Controllers\Controller;
|
||||||
|
use FireflyIII\Models\Bill;
|
||||||
|
use FireflyIII\Models\TransactionCurrency;
|
||||||
|
use FireflyIII\Repositories\Bill\BillRepositoryInterface;
|
||||||
|
use FireflyIII\Transformers\BillTransformer;
|
||||||
|
use Symfony\Component\HttpFoundation\ParameterBag;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class IndexController
|
||||||
|
*/
|
||||||
|
class IndexController extends Controller
|
||||||
|
{
|
||||||
|
private BillRepositoryInterface $repository;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* BillController constructor.
|
||||||
|
*
|
||||||
|
* @codeCoverageIgnore
|
||||||
|
*/
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
parent::__construct();
|
||||||
|
|
||||||
|
$this->middleware(
|
||||||
|
function ($request, $next) {
|
||||||
|
app('view')->share('title', (string) trans('firefly.bills'));
|
||||||
|
app('view')->share('mainTitleIcon', 'fa-calendar-o');
|
||||||
|
$this->repository = app(BillRepositoryInterface::class);
|
||||||
|
|
||||||
|
return $next($request);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Show all bills.
|
||||||
|
*/
|
||||||
|
public function index()
|
||||||
|
{
|
||||||
|
$start = session('start');
|
||||||
|
$end = session('end');
|
||||||
|
$collection = $this->repository->getBills();
|
||||||
|
$total = $collection->count();
|
||||||
|
|
||||||
|
$defaultCurrency = app('amount')->getDefaultCurrency();
|
||||||
|
$parameters = new ParameterBag;
|
||||||
|
$parameters->set('start', $start);
|
||||||
|
$parameters->set('end', $end);
|
||||||
|
|
||||||
|
/** @var BillTransformer $transformer */
|
||||||
|
$transformer = app(BillTransformer::class);
|
||||||
|
$transformer->setParameters($parameters);
|
||||||
|
|
||||||
|
// loop all bills, convert to array and add rules and stuff.
|
||||||
|
$rules = $this->repository->getRulesForBills($collection);
|
||||||
|
|
||||||
|
// make bill groups:
|
||||||
|
$bills = [
|
||||||
|
0 => [ // the index is the order, not the ID.
|
||||||
|
'object_group_id' => 0,
|
||||||
|
'object_group_title' => (string) trans('firefly.default_group_title_name'),
|
||||||
|
'bills' => [],
|
||||||
|
],
|
||||||
|
];
|
||||||
|
|
||||||
|
|
||||||
|
/** @var Bill $bill */
|
||||||
|
foreach ($collection as $bill) {
|
||||||
|
$array = $transformer->transform($bill);
|
||||||
|
$groupOrder = (int) $array['object_group_order'];
|
||||||
|
// make group array if necessary:
|
||||||
|
$bills[$groupOrder] = $bills[$groupOrder] ?? [
|
||||||
|
'object_group_id' => $array['object_group_id'],
|
||||||
|
'object_group_title' => $array['object_group_title'],
|
||||||
|
'bills' => [],
|
||||||
|
];
|
||||||
|
|
||||||
|
$nextExpectedMatch = new Carbon($array['next_expected_match']);
|
||||||
|
$array['next_expected_match_diff'] = $nextExpectedMatch->isToday()
|
||||||
|
? trans('firefly.today')
|
||||||
|
: $nextExpectedMatch->diffForHumans(
|
||||||
|
today(), Carbon::DIFF_RELATIVE_TO_NOW
|
||||||
|
);
|
||||||
|
$currency = $bill->transactionCurrency ?? $defaultCurrency;
|
||||||
|
$array['currency_id'] = $currency->id;
|
||||||
|
$array['currency_name'] = $currency->name;
|
||||||
|
$array['currency_symbol'] = $currency->symbol;
|
||||||
|
$array['currency_code'] = $currency->code;
|
||||||
|
$array['currency_decimal_places'] = $currency->decimal_places;
|
||||||
|
$array['attachments'] = $this->repository->getAttachments($bill);
|
||||||
|
$array['rules'] = $rules[$bill['id']] ?? [];
|
||||||
|
$bills[$groupOrder]['bills'][] = $array;
|
||||||
|
}
|
||||||
|
|
||||||
|
// order by key
|
||||||
|
ksort($bills);
|
||||||
|
|
||||||
|
// summarise per currency / per group.
|
||||||
|
$sums = $this->getSums($bills);
|
||||||
|
|
||||||
|
return view('bills.index', compact('bills', 'sums', 'total'));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param array $bills
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
private function getSums(array $bills): array
|
||||||
|
{
|
||||||
|
$sums = [];
|
||||||
|
|
||||||
|
/** @var array $group */
|
||||||
|
foreach ($bills as $groupOrder => $group) {
|
||||||
|
/** @var array $bill */
|
||||||
|
foreach ($group['bills'] as $bill) {
|
||||||
|
if (false === $bill['active']) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (0 === count($bill['pay_dates'])) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
/** @var TransactionCurrency $currency */
|
||||||
|
$currencyId = $bill['currency_id'];
|
||||||
|
$sums[$groupOrder][$currencyId] = $sums[$groupOrder][$currencyId] ?? [
|
||||||
|
'currency_id' => $currencyId,
|
||||||
|
'currency_code' => $bill['currency_code'],
|
||||||
|
'currency_name' => $bill['currency_name'],
|
||||||
|
'currency_symbol' => $bill['currency_symbol'],
|
||||||
|
'currency_decimal_places' => $bill['currency_decimal_places'],
|
||||||
|
'avg' => '0',
|
||||||
|
];
|
||||||
|
|
||||||
|
$avg = bcdiv(bcadd((string) $bill['amount_min'], (string) $bill['amount_max']), '2');
|
||||||
|
$avg = bcmul($avg, (string) count($bill['pay_dates']));
|
||||||
|
$sums[$groupOrder][$currencyId]['avg'] = bcadd($sums[$groupOrder][$currencyId]['avg'], $avg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $sums;
|
||||||
|
}
|
||||||
|
}
|
195
app/Http/Controllers/Bill/ShowController.php
Normal file
195
app/Http/Controllers/Bill/ShowController.php
Normal file
@ -0,0 +1,195 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* ShowController.php
|
||||||
|
* Copyright (c) 2020 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/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace FireflyIII\Http\Controllers\Bill;
|
||||||
|
|
||||||
|
|
||||||
|
use Carbon\Carbon;
|
||||||
|
use FireflyIII\Exceptions\FireflyException;
|
||||||
|
use FireflyIII\Helpers\Collector\GroupCollectorInterface;
|
||||||
|
use FireflyIII\Http\Controllers\Controller;
|
||||||
|
use FireflyIII\Models\Attachment;
|
||||||
|
use FireflyIII\Models\Bill;
|
||||||
|
use FireflyIII\Repositories\Bill\BillRepositoryInterface;
|
||||||
|
use FireflyIII\TransactionRules\TransactionMatcher;
|
||||||
|
use FireflyIII\Transformers\AttachmentTransformer;
|
||||||
|
use FireflyIII\Transformers\BillTransformer;
|
||||||
|
use Illuminate\Contracts\View\Factory;
|
||||||
|
use Illuminate\Http\RedirectResponse;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
use Illuminate\Routing\Redirector;
|
||||||
|
use Illuminate\Support\Collection;
|
||||||
|
use Illuminate\View\View;
|
||||||
|
use League\Fractal\Manager;
|
||||||
|
use League\Fractal\Resource\Item;
|
||||||
|
use League\Fractal\Serializer\DataArraySerializer;
|
||||||
|
use Symfony\Component\HttpFoundation\ParameterBag;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class ShowController
|
||||||
|
*/
|
||||||
|
class ShowController extends Controller
|
||||||
|
{
|
||||||
|
private BillRepositoryInterface $repository;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* BillController constructor.
|
||||||
|
*
|
||||||
|
* @codeCoverageIgnore
|
||||||
|
*/
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
parent::__construct();
|
||||||
|
|
||||||
|
app('view')->share('showBudget', true);
|
||||||
|
|
||||||
|
$this->middleware(
|
||||||
|
function ($request, $next) {
|
||||||
|
app('view')->share('title', (string) trans('firefly.bills'));
|
||||||
|
app('view')->share('mainTitleIcon', 'fa-calendar-o');
|
||||||
|
$this->repository = app(BillRepositoryInterface::class);
|
||||||
|
|
||||||
|
return $next($request);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Rescan bills for transactions.
|
||||||
|
*
|
||||||
|
* @param Request $request
|
||||||
|
* @param Bill $bill
|
||||||
|
*
|
||||||
|
* @throws FireflyException
|
||||||
|
* @return RedirectResponse|Redirector
|
||||||
|
*/
|
||||||
|
public function rescan(Request $request, Bill $bill)
|
||||||
|
{
|
||||||
|
$total = 0;
|
||||||
|
if (false === $bill->active) {
|
||||||
|
$request->session()->flash('warning', (string) trans('firefly.cannot_scan_inactive_bill'));
|
||||||
|
|
||||||
|
return redirect(route('bills.show', [$bill->id]));
|
||||||
|
}
|
||||||
|
$set = new Collection;
|
||||||
|
if (true === $bill->active) {
|
||||||
|
$set = $this->repository->getRulesForBill($bill);
|
||||||
|
$total = 0;
|
||||||
|
}
|
||||||
|
if (0 === $set->count()) {
|
||||||
|
$request->session()->flash('error', (string) trans('firefly.no_rules_for_bill'));
|
||||||
|
|
||||||
|
return redirect(route('bills.show', [$bill->id]));
|
||||||
|
}
|
||||||
|
|
||||||
|
// unlink all journals:
|
||||||
|
$this->repository->unlinkAll($bill);
|
||||||
|
|
||||||
|
foreach ($set as $rule) {
|
||||||
|
// simply fire off all rules?
|
||||||
|
/** @var TransactionMatcher $matcher */
|
||||||
|
$matcher = app(TransactionMatcher::class);
|
||||||
|
$matcher->setSearchLimit(100000); // large upper limit
|
||||||
|
$matcher->setTriggeredLimit(100000); // large upper limit
|
||||||
|
$matcher->setRule($rule);
|
||||||
|
$matchingTransactions = $matcher->findTransactionsByRule();
|
||||||
|
$total += count($matchingTransactions);
|
||||||
|
$this->repository->linkCollectionToBill($bill, $matchingTransactions);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
$request->session()->flash('success', (string) trans_choice('firefly.rescanned_bill', $total));
|
||||||
|
app('preferences')->mark();
|
||||||
|
|
||||||
|
return redirect(route('bills.show', [$bill->id]));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Show a bill.
|
||||||
|
*
|
||||||
|
* @param Request $request
|
||||||
|
* @param Bill $bill
|
||||||
|
*
|
||||||
|
* @return Factory|View
|
||||||
|
*/
|
||||||
|
public function show(Request $request, Bill $bill)
|
||||||
|
{
|
||||||
|
// add info about rules:
|
||||||
|
$rules = $this->repository->getRulesForBill($bill);
|
||||||
|
$subTitle = $bill->name;
|
||||||
|
/** @var Carbon $start */
|
||||||
|
$start = session('start');
|
||||||
|
/** @var Carbon $end */
|
||||||
|
$end = session('end');
|
||||||
|
$year = $start->year;
|
||||||
|
$page = (int) $request->get('page');
|
||||||
|
$pageSize = (int) app('preferences')->get('listPageSize', 50)->data;
|
||||||
|
$yearAverage = $this->repository->getYearAverage($bill, $start);
|
||||||
|
$overallAverage = $this->repository->getOverallAverage($bill);
|
||||||
|
$manager = new Manager();
|
||||||
|
$manager->setSerializer(new DataArraySerializer());
|
||||||
|
$manager->parseIncludes(['attachments', 'notes']);
|
||||||
|
|
||||||
|
// Make a resource out of the data and
|
||||||
|
$parameters = new ParameterBag();
|
||||||
|
$parameters->set('start', $start);
|
||||||
|
$parameters->set('end', $end);
|
||||||
|
|
||||||
|
/** @var BillTransformer $transformer */
|
||||||
|
$transformer = app(BillTransformer::class);
|
||||||
|
$transformer->setParameters($parameters);
|
||||||
|
|
||||||
|
$resource = new Item($bill, $transformer, 'bill');
|
||||||
|
$object = $manager->createData($resource)->toArray();
|
||||||
|
$object['data']['currency'] = $bill->transactionCurrency;
|
||||||
|
|
||||||
|
/** @var GroupCollectorInterface $collector */
|
||||||
|
$collector = app(GroupCollectorInterface::class);
|
||||||
|
$collector->setBill($bill)->setLimit($pageSize)->setPage($page)->withBudgetInformation()
|
||||||
|
->withCategoryInformation()->withAccountInformation();
|
||||||
|
$groups = $collector->getPaginatedGroups();
|
||||||
|
$groups->setPath(route('bills.show', [$bill->id]));
|
||||||
|
|
||||||
|
// transform any attachments as well.
|
||||||
|
$collection = $this->repository->getAttachments($bill);
|
||||||
|
$attachments = new Collection;
|
||||||
|
|
||||||
|
// @codeCoverageIgnoreStart
|
||||||
|
if ($collection->count() > 0) {
|
||||||
|
/** @var AttachmentTransformer $transformer */
|
||||||
|
$transformer = app(AttachmentTransformer::class);
|
||||||
|
$attachments = $collection->each(
|
||||||
|
static function (Attachment $attachment) use ($transformer) {
|
||||||
|
return $transformer->transform($attachment);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// @codeCoverageIgnoreEnd
|
||||||
|
|
||||||
|
|
||||||
|
return view('bills.show', compact('attachments', 'groups', 'rules', 'yearAverage', 'overallAverage', 'year', 'object', 'bill', 'subTitle'));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -1,483 +0,0 @@
|
|||||||
<?php
|
|
||||||
/**
|
|
||||||
* BillController.php
|
|
||||||
* 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/>.
|
|
||||||
*/
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
namespace FireflyIII\Http\Controllers;
|
|
||||||
|
|
||||||
use Carbon\Carbon;
|
|
||||||
use FireflyIII\Exceptions\FireflyException;
|
|
||||||
use FireflyIII\Helpers\Attachments\AttachmentHelperInterface;
|
|
||||||
use FireflyIII\Helpers\Collector\GroupCollectorInterface;
|
|
||||||
use FireflyIII\Http\Requests\BillFormRequest;
|
|
||||||
use FireflyIII\Models\Attachment;
|
|
||||||
use FireflyIII\Models\Bill;
|
|
||||||
use FireflyIII\Models\TransactionCurrency;
|
|
||||||
use FireflyIII\Repositories\Bill\BillRepositoryInterface;
|
|
||||||
use FireflyIII\TransactionRules\TransactionMatcher;
|
|
||||||
use FireflyIII\Transformers\AttachmentTransformer;
|
|
||||||
use FireflyIII\Transformers\BillTransformer;
|
|
||||||
use Illuminate\Contracts\View\Factory;
|
|
||||||
use Illuminate\Http\RedirectResponse;
|
|
||||||
use Illuminate\Http\Request;
|
|
||||||
use Illuminate\Routing\Redirector;
|
|
||||||
use Illuminate\Support\Collection;
|
|
||||||
use Illuminate\View\View;
|
|
||||||
use League\Fractal\Manager;
|
|
||||||
use League\Fractal\Resource\Item;
|
|
||||||
use League\Fractal\Serializer\DataArraySerializer;
|
|
||||||
use Log;
|
|
||||||
use Symfony\Component\HttpFoundation\ParameterBag;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Class BillController.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
class BillController extends Controller
|
|
||||||
{
|
|
||||||
/** @var AttachmentHelperInterface Helper for attachments. */
|
|
||||||
private $attachments;
|
|
||||||
/** @var BillRepositoryInterface Bill repository */
|
|
||||||
private $billRepository;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* BillController constructor.
|
|
||||||
*
|
|
||||||
* @codeCoverageIgnore
|
|
||||||
*/
|
|
||||||
public function __construct()
|
|
||||||
{
|
|
||||||
parent::__construct();
|
|
||||||
|
|
||||||
app('view')->share('showBudget', true);
|
|
||||||
|
|
||||||
$this->middleware(
|
|
||||||
function ($request, $next) {
|
|
||||||
app('view')->share('title', (string) trans('firefly.bills'));
|
|
||||||
app('view')->share('mainTitleIcon', 'fa-calendar-o');
|
|
||||||
$this->attachments = app(AttachmentHelperInterface::class);
|
|
||||||
$this->billRepository = app(BillRepositoryInterface::class);
|
|
||||||
|
|
||||||
return $next($request);
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a new bill.
|
|
||||||
*
|
|
||||||
* @param Request $request
|
|
||||||
*
|
|
||||||
* @return Factory|View
|
|
||||||
*/
|
|
||||||
public function create(Request $request)
|
|
||||||
{
|
|
||||||
$periods = [];
|
|
||||||
/** @var array $billPeriods */
|
|
||||||
$billPeriods = config('firefly.bill_periods');
|
|
||||||
foreach ($billPeriods as $current) {
|
|
||||||
$periods[$current] = strtolower((string) trans('firefly.repeat_freq_' . $current));
|
|
||||||
}
|
|
||||||
$subTitle = (string) trans('firefly.create_new_bill');
|
|
||||||
$defaultCurrency = app('amount')->getDefaultCurrency();
|
|
||||||
|
|
||||||
// put previous url in session if not redirect from store (not "create another").
|
|
||||||
if (true !== session('bills.create.fromStore')) {
|
|
||||||
$this->rememberPreviousUri('bills.create.uri');
|
|
||||||
}
|
|
||||||
$request->session()->forget('bills.create.fromStore');
|
|
||||||
|
|
||||||
return view('bills.create', compact('periods', 'subTitle', 'defaultCurrency'));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Delete a bill.
|
|
||||||
*
|
|
||||||
* @param Bill $bill
|
|
||||||
*
|
|
||||||
* @return Factory|View
|
|
||||||
*/
|
|
||||||
public function delete(Bill $bill)
|
|
||||||
{
|
|
||||||
// put previous url in session
|
|
||||||
$this->rememberPreviousUri('bills.delete.uri');
|
|
||||||
$subTitle = (string) trans('firefly.delete_bill', ['name' => $bill->name]);
|
|
||||||
|
|
||||||
return view('bills.delete', compact('bill', 'subTitle'));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Destroy a bill.
|
|
||||||
*
|
|
||||||
* @param Request $request
|
|
||||||
* @param Bill $bill
|
|
||||||
*
|
|
||||||
* @return RedirectResponse|Redirector
|
|
||||||
*/
|
|
||||||
public function destroy(Request $request, Bill $bill)
|
|
||||||
{
|
|
||||||
$name = $bill->name;
|
|
||||||
$this->billRepository->destroy($bill);
|
|
||||||
|
|
||||||
$request->session()->flash('success', (string) trans('firefly.deleted_bill', ['name' => $name]));
|
|
||||||
app('preferences')->mark();
|
|
||||||
|
|
||||||
return redirect($this->getPreviousUri('bills.delete.uri'));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Edit a bill.
|
|
||||||
*
|
|
||||||
* @param Request $request
|
|
||||||
* @param Bill $bill
|
|
||||||
*
|
|
||||||
* @return Factory|View
|
|
||||||
*/
|
|
||||||
public function edit(Request $request, Bill $bill)
|
|
||||||
{
|
|
||||||
$periods = [];
|
|
||||||
/** @var array $billPeriods */
|
|
||||||
$billPeriods = config('firefly.bill_periods');
|
|
||||||
|
|
||||||
foreach ($billPeriods as $current) {
|
|
||||||
$periods[$current] = (string) trans('firefly.' . $current);
|
|
||||||
}
|
|
||||||
|
|
||||||
$subTitle = (string) trans('firefly.edit_bill', ['name' => $bill->name]);
|
|
||||||
|
|
||||||
// put previous url in session if not redirect from store (not "return_to_edit").
|
|
||||||
if (true !== session('bills.edit.fromUpdate')) {
|
|
||||||
$this->rememberPreviousUri('bills.edit.uri');
|
|
||||||
}
|
|
||||||
|
|
||||||
$currency = app('amount')->getDefaultCurrency();
|
|
||||||
$bill->amount_min = round((float) $bill->amount_min, $currency->decimal_places);
|
|
||||||
$bill->amount_max = round((float) $bill->amount_max, $currency->decimal_places);
|
|
||||||
$rules = $this->billRepository->getRulesForBill($bill);
|
|
||||||
$defaultCurrency = app('amount')->getDefaultCurrency();
|
|
||||||
|
|
||||||
// code to handle active-checkboxes
|
|
||||||
$hasOldInput = null !== $request->old('_token');
|
|
||||||
|
|
||||||
$preFilled = [
|
|
||||||
'notes' => $this->billRepository->getNoteText($bill),
|
|
||||||
'transaction_currency_id' => $bill->transaction_currency_id,
|
|
||||||
'active' => $hasOldInput ? (bool) $request->old('active') : $bill->active,
|
|
||||||
];
|
|
||||||
|
|
||||||
$request->session()->flash('preFilled', $preFilled);
|
|
||||||
$request->session()->forget('bills.edit.fromUpdate');
|
|
||||||
|
|
||||||
return view('bills.edit', compact('subTitle', 'periods', 'rules', 'bill', 'defaultCurrency', 'preFilled'));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Show all bills.
|
|
||||||
*
|
|
||||||
* @return Factory|View
|
|
||||||
*/
|
|
||||||
public function index()
|
|
||||||
{
|
|
||||||
$start = session('start');
|
|
||||||
$end = session('end');
|
|
||||||
$unfiltered = $this->billRepository->getBills();
|
|
||||||
|
|
||||||
$defaultCurrency = app('amount')->getDefaultCurrency();
|
|
||||||
$parameters = new ParameterBag();
|
|
||||||
$parameters->set('start', $start);
|
|
||||||
$parameters->set('end', $end);
|
|
||||||
|
|
||||||
/** @var BillTransformer $transformer */
|
|
||||||
$transformer = app(BillTransformer::class);
|
|
||||||
$transformer->setParameters($parameters);
|
|
||||||
|
|
||||||
/** @var Collection $bills */
|
|
||||||
$bills = $unfiltered->map(
|
|
||||||
function (Bill $bill) use ($transformer, $defaultCurrency) {
|
|
||||||
$return = $transformer->transform($bill);
|
|
||||||
$nextExpectedMatch = new Carbon($return['next_expected_match']);
|
|
||||||
$return['next_expected_match_diff'] = $nextExpectedMatch->isToday()
|
|
||||||
? trans('firefly.today')
|
|
||||||
: $nextExpectedMatch->diffForHumans(
|
|
||||||
today(), Carbon::DIFF_RELATIVE_TO_NOW
|
|
||||||
);
|
|
||||||
$currency = $bill->transactionCurrency ?? $defaultCurrency;
|
|
||||||
$return['currency_id'] = $currency->id;
|
|
||||||
$return['currency_name'] = $currency->name;
|
|
||||||
$return['currency_symbol'] = $currency->symbol;
|
|
||||||
$return['currency_code'] = $currency->code;
|
|
||||||
$return['currency_decimal_places'] = $currency->decimal_places;
|
|
||||||
$return['attachments'] = $this->billRepository->getAttachments($bill);
|
|
||||||
|
|
||||||
return $return;
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
// add info about rules:
|
|
||||||
$rules = $this->billRepository->getRulesForBills($unfiltered);
|
|
||||||
$bills = $bills->map(
|
|
||||||
static function (array $bill) use ($rules) {
|
|
||||||
$bill['rules'] = $rules[$bill['id']] ?? [];
|
|
||||||
|
|
||||||
return $bill;
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
// summarise per currency:
|
|
||||||
$sums = $this->getSums($bills);
|
|
||||||
|
|
||||||
return view('bills.index', compact('bills', 'sums'));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Rescan bills for transactions.
|
|
||||||
*
|
|
||||||
* @param Request $request
|
|
||||||
* @param Bill $bill
|
|
||||||
*
|
|
||||||
* @throws FireflyException
|
|
||||||
* @return RedirectResponse|Redirector
|
|
||||||
*/
|
|
||||||
public function rescan(Request $request, Bill $bill)
|
|
||||||
{
|
|
||||||
$total = 0;
|
|
||||||
if (false === $bill->active) {
|
|
||||||
$request->session()->flash('warning', (string) trans('firefly.cannot_scan_inactive_bill'));
|
|
||||||
|
|
||||||
return redirect(route('bills.show', [$bill->id]));
|
|
||||||
}
|
|
||||||
$set = new Collection;
|
|
||||||
if (true === $bill->active) {
|
|
||||||
$set = $this->billRepository->getRulesForBill($bill);
|
|
||||||
$total = 0;
|
|
||||||
}
|
|
||||||
if (0 === $set->count()) {
|
|
||||||
$request->session()->flash('error', (string) trans('firefly.no_rules_for_bill'));
|
|
||||||
|
|
||||||
return redirect(route('bills.show', [$bill->id]));
|
|
||||||
}
|
|
||||||
|
|
||||||
// unlink all journals:
|
|
||||||
$this->billRepository->unlinkAll($bill);
|
|
||||||
|
|
||||||
foreach ($set as $rule) {
|
|
||||||
// simply fire off all rules?
|
|
||||||
/** @var TransactionMatcher $matcher */
|
|
||||||
$matcher = app(TransactionMatcher::class);
|
|
||||||
$matcher->setSearchLimit(100000); // large upper limit
|
|
||||||
$matcher->setTriggeredLimit(100000); // large upper limit
|
|
||||||
$matcher->setRule($rule);
|
|
||||||
$matchingTransactions = $matcher->findTransactionsByRule();
|
|
||||||
$total += count($matchingTransactions);
|
|
||||||
$this->billRepository->linkCollectionToBill($bill, $matchingTransactions);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
$request->session()->flash('success', (string) trans_choice('firefly.rescanned_bill', $total));
|
|
||||||
app('preferences')->mark();
|
|
||||||
|
|
||||||
return redirect(route('bills.show', [$bill->id]));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Show a bill.
|
|
||||||
*
|
|
||||||
* @param Request $request
|
|
||||||
* @param Bill $bill
|
|
||||||
*
|
|
||||||
* @return Factory|View
|
|
||||||
*/
|
|
||||||
public function show(Request $request, Bill $bill)
|
|
||||||
{
|
|
||||||
// add info about rules:
|
|
||||||
$rules = $this->billRepository->getRulesForBill($bill);
|
|
||||||
$subTitle = $bill->name;
|
|
||||||
/** @var Carbon $start */
|
|
||||||
$start = session('start');
|
|
||||||
/** @var Carbon $end */
|
|
||||||
$end = session('end');
|
|
||||||
$year = $start->year;
|
|
||||||
$page = (int) $request->get('page');
|
|
||||||
$pageSize = (int) app('preferences')->get('listPageSize', 50)->data;
|
|
||||||
$yearAverage = $this->billRepository->getYearAverage($bill, $start);
|
|
||||||
$overallAverage = $this->billRepository->getOverallAverage($bill);
|
|
||||||
$manager = new Manager();
|
|
||||||
$manager->setSerializer(new DataArraySerializer());
|
|
||||||
$manager->parseIncludes(['attachments', 'notes']);
|
|
||||||
|
|
||||||
// Make a resource out of the data and
|
|
||||||
$parameters = new ParameterBag();
|
|
||||||
$parameters->set('start', $start);
|
|
||||||
$parameters->set('end', $end);
|
|
||||||
|
|
||||||
/** @var BillTransformer $transformer */
|
|
||||||
$transformer = app(BillTransformer::class);
|
|
||||||
$transformer->setParameters($parameters);
|
|
||||||
|
|
||||||
$resource = new Item($bill, $transformer, 'bill');
|
|
||||||
$object = $manager->createData($resource)->toArray();
|
|
||||||
$object['data']['currency'] = $bill->transactionCurrency;
|
|
||||||
|
|
||||||
/** @var GroupCollectorInterface $collector */
|
|
||||||
$collector = app(GroupCollectorInterface::class);
|
|
||||||
$collector->setBill($bill)->setLimit($pageSize)->setPage($page)->withBudgetInformation()
|
|
||||||
->withCategoryInformation()->withAccountInformation();
|
|
||||||
$groups = $collector->getPaginatedGroups();
|
|
||||||
$groups->setPath(route('bills.show', [$bill->id]));
|
|
||||||
|
|
||||||
// transform any attachments as well.
|
|
||||||
$collection = $this->billRepository->getAttachments($bill);
|
|
||||||
$attachments = new Collection;
|
|
||||||
|
|
||||||
// @codeCoverageIgnoreStart
|
|
||||||
if ($collection->count() > 0) {
|
|
||||||
/** @var AttachmentTransformer $transformer */
|
|
||||||
$transformer = app(AttachmentTransformer::class);
|
|
||||||
$attachments = $collection->each(
|
|
||||||
static function (Attachment $attachment) use ($transformer) {
|
|
||||||
return $transformer->transform($attachment);
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// @codeCoverageIgnoreEnd
|
|
||||||
|
|
||||||
|
|
||||||
return view('bills.show', compact('attachments', 'groups', 'rules', 'yearAverage', 'overallAverage', 'year', 'object', 'bill', 'subTitle'));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Store a new bill.
|
|
||||||
*
|
|
||||||
* @param BillFormRequest $request
|
|
||||||
*
|
|
||||||
* @return RedirectResponse
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
public function store(BillFormRequest $request): RedirectResponse
|
|
||||||
{
|
|
||||||
$billData = $request->getBillData();
|
|
||||||
$billData['active'] = true;
|
|
||||||
try {
|
|
||||||
$bill = $this->billRepository->store($billData);
|
|
||||||
} catch (FireflyException $e) {
|
|
||||||
Log::error($e->getMessage());
|
|
||||||
$request->session()->flash('error', (string) trans('firefly.bill_store_error'));
|
|
||||||
|
|
||||||
return redirect(route('bills.create'))->withInput();
|
|
||||||
}
|
|
||||||
$request->session()->flash('success', (string) trans('firefly.stored_new_bill', ['name' => $bill->name]));
|
|
||||||
app('preferences')->mark();
|
|
||||||
|
|
||||||
/** @var array $files */
|
|
||||||
$files = $request->hasFile('attachments') ? $request->file('attachments') : null;
|
|
||||||
if (null !== $files && !auth()->user()->hasRole('demo')) {
|
|
||||||
$this->attachments->saveAttachmentsForModel($bill, $files);
|
|
||||||
}
|
|
||||||
if (null !== $files && auth()->user()->hasRole('demo')) {
|
|
||||||
session()->flash('info',(string)trans('firefly.no_att_demo_user'));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (count($this->attachments->getMessages()->get('attachments')) > 0) {
|
|
||||||
$request->session()->flash('info', $this->attachments->getMessages()->get('attachments')); // @codeCoverageIgnore
|
|
||||||
}
|
|
||||||
|
|
||||||
return redirect(route('rules.create-from-bill', [$bill->id]));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Update a bill.
|
|
||||||
*
|
|
||||||
* @param BillFormRequest $request
|
|
||||||
* @param Bill $bill
|
|
||||||
*
|
|
||||||
* @return RedirectResponse
|
|
||||||
*/
|
|
||||||
public function update(BillFormRequest $request, Bill $bill): RedirectResponse
|
|
||||||
{
|
|
||||||
$billData = $request->getBillData();
|
|
||||||
$bill = $this->billRepository->update($bill, $billData);
|
|
||||||
|
|
||||||
$request->session()->flash('success', (string) trans('firefly.updated_bill', ['name' => $bill->name]));
|
|
||||||
app('preferences')->mark();
|
|
||||||
|
|
||||||
/** @var array $files */
|
|
||||||
$files = $request->hasFile('attachments') ? $request->file('attachments') : null;
|
|
||||||
if (null !== $files && !auth()->user()->hasRole('demo')) {
|
|
||||||
$this->attachments->saveAttachmentsForModel($bill, $files);
|
|
||||||
}
|
|
||||||
if (null !== $files && auth()->user()->hasRole('demo')) {
|
|
||||||
session()->flash('info',(string)trans('firefly.no_att_demo_user'));
|
|
||||||
}
|
|
||||||
|
|
||||||
// flash messages
|
|
||||||
if (count($this->attachments->getMessages()->get('attachments')) > 0) {
|
|
||||||
$request->session()->flash('info', $this->attachments->getMessages()->get('attachments')); // @codeCoverageIgnore
|
|
||||||
}
|
|
||||||
$redirect = redirect($this->getPreviousUri('bills.edit.uri'));
|
|
||||||
|
|
||||||
if (1 === (int) $request->get('return_to_edit')) {
|
|
||||||
// @codeCoverageIgnoreStart
|
|
||||||
$request->session()->put('bills.edit.fromUpdate', true);
|
|
||||||
|
|
||||||
$redirect = redirect(route('bills.edit', [$bill->id]))->withInput(['return_to_edit' => 1]);
|
|
||||||
// @codeCoverageIgnoreEnd
|
|
||||||
}
|
|
||||||
|
|
||||||
return $redirect;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param Collection $bills
|
|
||||||
*
|
|
||||||
* @return array
|
|
||||||
*/
|
|
||||||
private function getSums(Collection $bills): array
|
|
||||||
{
|
|
||||||
$sums = [];
|
|
||||||
|
|
||||||
/** @var array $bill */
|
|
||||||
foreach ($bills as $bill) {
|
|
||||||
if (false === $bill['active']) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (0 === count($bill['pay_dates'])) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
/** @var TransactionCurrency $currency */
|
|
||||||
$currencyId = $bill['currency_id'];
|
|
||||||
$sums[$currencyId] = $sums[$currencyId] ?? [
|
|
||||||
'currency_id' => $currencyId,
|
|
||||||
'currency_code' => $bill['currency_code'],
|
|
||||||
'currency_name' => $bill['currency_name'],
|
|
||||||
'currency_symbol' => $bill['currency_symbol'],
|
|
||||||
'currency_decimal_places' => $bill['currency_decimal_places'],
|
|
||||||
'avg' => '0',
|
|
||||||
];
|
|
||||||
|
|
||||||
$avg = bcdiv(bcadd((string) $bill['amount_min'], (string) $bill['amount_max']), '2');
|
|
||||||
$avg = bcmul($avg, (string) count($bill['pay_dates']));
|
|
||||||
$sums[$currencyId]['avg'] = bcadd($sums[$currencyId]['avg'], $avg);
|
|
||||||
}
|
|
||||||
|
|
||||||
return $sums;
|
|
||||||
}
|
|
||||||
}
|
|
81
app/Http/Requests/BillStoreRequest.php
Normal file
81
app/Http/Requests/BillStoreRequest.php
Normal file
@ -0,0 +1,81 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* BillStoreRequest.php
|
||||||
|
* 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/>.
|
||||||
|
*/
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace FireflyIII\Http\Requests;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class BillStoreRequest.
|
||||||
|
*/
|
||||||
|
class BillStoreRequest extends Request
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Verify the request.
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function authorize(): bool
|
||||||
|
{
|
||||||
|
// Only allow logged in users
|
||||||
|
return auth()->check();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the data required by the controller.
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function getBillData(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'name' => $this->string('name'),
|
||||||
|
'amount_min' => $this->string('amount_min'),
|
||||||
|
'currency_id' => $this->integer('transaction_currency_id'),
|
||||||
|
'currency_code' => '',
|
||||||
|
'amount_max' => $this->string('amount_max'),
|
||||||
|
'date' => $this->date('date'),
|
||||||
|
'repeat_freq' => $this->string('repeat_freq'),
|
||||||
|
'skip' => $this->integer('skip'),
|
||||||
|
'notes' => $this->nlString('notes'),
|
||||||
|
'active' => $this->boolean('active'),
|
||||||
|
'object_group' => $this->string('object_group'),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Rules for this request.
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function rules(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'name' => 'required|between:1,255|uniqueObjectForUser:bills,name',
|
||||||
|
'amount_min' => 'required|numeric|more:0|max:1000000000',
|
||||||
|
'amount_max' => 'required|numeric|more:0|max:1000000000',
|
||||||
|
'transaction_currency_id' => 'required|exists:transaction_currencies,id',
|
||||||
|
'date' => 'required|date',
|
||||||
|
'repeat_freq' => 'required|in:weekly,monthly,quarterly,half-year,yearly',
|
||||||
|
'skip' => 'required|between:0,31',
|
||||||
|
'active' => 'boolean',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
@ -1,6 +1,6 @@
|
|||||||
<?php
|
<?php
|
||||||
/**
|
/**
|
||||||
* BillFormRequest.php
|
* BillUpdateRequest.php
|
||||||
* Copyright (c) 2019 james@firefly-iii.org
|
* Copyright (c) 2019 james@firefly-iii.org
|
||||||
*
|
*
|
||||||
* This file is part of Firefly III (https://github.com/firefly-iii).
|
* This file is part of Firefly III (https://github.com/firefly-iii).
|
||||||
@ -25,9 +25,9 @@ namespace FireflyIII\Http\Requests;
|
|||||||
use FireflyIII\Models\Bill;
|
use FireflyIII\Models\Bill;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class BillFormRequest.
|
* Class BillUpdateRequest.
|
||||||
*/
|
*/
|
||||||
class BillFormRequest extends Request
|
class BillUpdateRequest extends Request
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* Verify the request.
|
* Verify the request.
|
||||||
@ -58,6 +58,7 @@ class BillFormRequest extends Request
|
|||||||
'skip' => $this->integer('skip'),
|
'skip' => $this->integer('skip'),
|
||||||
'notes' => $this->nlString('notes'),
|
'notes' => $this->nlString('notes'),
|
||||||
'active' => $this->boolean('active'),
|
'active' => $this->boolean('active'),
|
||||||
|
'object_group' => $this->string('object_group'),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -68,15 +69,11 @@ class BillFormRequest extends Request
|
|||||||
*/
|
*/
|
||||||
public function rules(): array
|
public function rules(): array
|
||||||
{
|
{
|
||||||
$nameRule = 'required|between:1,255|uniqueObjectForUser:bills,name';
|
|
||||||
/** @var Bill $bill */
|
/** @var Bill $bill */
|
||||||
$bill = $this->route()->parameter('bill');
|
$bill = $this->route()->parameter('bill');
|
||||||
if (null !== $bill) {
|
|
||||||
$nameRule = 'required|between:1,255|uniqueObjectForUser:bills,name,' . $bill->id;
|
return [
|
||||||
}
|
'name' => sprintf('required|between:1,255|uniqueObjectForUser:bills,name,%d', $bill->id),
|
||||||
// is OK
|
|
||||||
$rules = [
|
|
||||||
'name' => $nameRule,
|
|
||||||
'amount_min' => 'required|numeric|more:0|max:1000000000',
|
'amount_min' => 'required|numeric|more:0|max:1000000000',
|
||||||
'amount_max' => 'required|numeric|more:0|max:1000000000',
|
'amount_max' => 'required|numeric|more:0|max:1000000000',
|
||||||
'transaction_currency_id' => 'required|exists:transaction_currencies,id',
|
'transaction_currency_id' => 'required|exists:transaction_currencies,id',
|
||||||
@ -85,7 +82,5 @@ class BillFormRequest extends Request
|
|||||||
'skip' => 'required|between:0,31',
|
'skip' => 'required|between:0,31',
|
||||||
'active' => 'boolean',
|
'active' => 'boolean',
|
||||||
];
|
];
|
||||||
|
|
||||||
return $rules;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -161,6 +161,14 @@ class Bill extends Model
|
|||||||
return $this->morphMany(Note::class, 'noteable');
|
return $this->morphMany(Note::class, 'noteable');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get all of the tags for the post.
|
||||||
|
*/
|
||||||
|
public function objectGroups()
|
||||||
|
{
|
||||||
|
return $this->morphToMany(ObjectGroup::class, 'object_groupable');
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @codeCoverageIgnore
|
* @codeCoverageIgnore
|
||||||
*
|
*
|
||||||
|
@ -29,6 +29,7 @@ use FireflyIII\Models\Rule;
|
|||||||
use FireflyIII\Models\RuleTrigger;
|
use FireflyIII\Models\RuleTrigger;
|
||||||
use FireflyIII\Models\TransactionCurrency;
|
use FireflyIII\Models\TransactionCurrency;
|
||||||
use FireflyIII\Repositories\Bill\BillRepositoryInterface;
|
use FireflyIII\Repositories\Bill\BillRepositoryInterface;
|
||||||
|
use FireflyIII\Repositories\ObjectGroup\CreatesObjectGroups;
|
||||||
use FireflyIII\Services\Internal\Support\BillServiceTrait;
|
use FireflyIII\Services\Internal\Support\BillServiceTrait;
|
||||||
use Illuminate\Support\Collection;
|
use Illuminate\Support\Collection;
|
||||||
use Log;
|
use Log;
|
||||||
@ -39,7 +40,9 @@ use Log;
|
|||||||
*/
|
*/
|
||||||
class BillUpdateService
|
class BillUpdateService
|
||||||
{
|
{
|
||||||
use BillServiceTrait;
|
use BillServiceTrait, CreatesObjectGroups;
|
||||||
|
|
||||||
|
protected $user;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor.
|
* Constructor.
|
||||||
@ -59,6 +62,7 @@ class BillUpdateService
|
|||||||
*/
|
*/
|
||||||
public function update(Bill $bill, array $data): Bill
|
public function update(Bill $bill, array $data): Bill
|
||||||
{
|
{
|
||||||
|
$this->user = $bill->user;
|
||||||
/** @var TransactionCurrencyFactory $factory */
|
/** @var TransactionCurrencyFactory $factory */
|
||||||
$factory = app(TransactionCurrencyFactory::class);
|
$factory = app(TransactionCurrencyFactory::class);
|
||||||
/** @var TransactionCurrency $currency */
|
/** @var TransactionCurrency $currency */
|
||||||
@ -103,6 +107,25 @@ class BillUpdateService
|
|||||||
$this->updateBillActions($bill, $oldData['name'], $data['name']);
|
$this->updateBillActions($bill, $oldData['name'], $data['name']);
|
||||||
$this->updateBillTriggers($bill, $oldData, $data);
|
$this->updateBillTriggers($bill, $oldData, $data);
|
||||||
|
|
||||||
|
// update using name:
|
||||||
|
$objectGroupTitle = $data['object_group'] ?? '';
|
||||||
|
if ('' !== $objectGroupTitle) {
|
||||||
|
$objectGroup = $this->findOrCreateObjectGroup($objectGroupTitle);
|
||||||
|
if (null !== $objectGroup) {
|
||||||
|
$bill->objectGroups()->sync([$objectGroup->id]);
|
||||||
|
$bill->save();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// try also with ID:
|
||||||
|
$objectGroupId = (int) ($data['object_group_id'] ?? 0);
|
||||||
|
if (0 !== $objectGroupId) {
|
||||||
|
$objectGroup = $this->findObjectGroupById($objectGroupId);
|
||||||
|
if (null !== $objectGroup) {
|
||||||
|
$bill->objectGroups()->sync([$objectGroup->id]);
|
||||||
|
$bill->save();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return $bill;
|
return $bill;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -25,6 +25,7 @@ namespace FireflyIII\Transformers;
|
|||||||
|
|
||||||
use Carbon\Carbon;
|
use Carbon\Carbon;
|
||||||
use FireflyIII\Models\Bill;
|
use FireflyIII\Models\Bill;
|
||||||
|
use FireflyIII\Models\ObjectGroup;
|
||||||
use FireflyIII\Models\TransactionJournal;
|
use FireflyIII\Models\TransactionJournal;
|
||||||
use FireflyIII\Repositories\Bill\BillRepositoryInterface;
|
use FireflyIII\Repositories\Bill\BillRepositoryInterface;
|
||||||
use Illuminate\Support\Collection;
|
use Illuminate\Support\Collection;
|
||||||
@ -67,6 +68,18 @@ class BillTransformer extends AbstractTransformer
|
|||||||
$notes = $this->repository->getNoteText($bill);
|
$notes = $this->repository->getNoteText($bill);
|
||||||
$notes = '' === $notes ? null : $notes;
|
$notes = '' === $notes ? null : $notes;
|
||||||
$this->repository->setUser($bill->user);
|
$this->repository->setUser($bill->user);
|
||||||
|
|
||||||
|
$objectGroupId = null;
|
||||||
|
$objectGroupOrder = null;
|
||||||
|
$objectGroupTitle = null;
|
||||||
|
/** @var ObjectGroup $objectGroup */
|
||||||
|
$objectGroup = $bill->objectGroups->first();
|
||||||
|
if (null !== $objectGroup) {
|
||||||
|
$objectGroupId = (int) $objectGroup->id;
|
||||||
|
$objectGroupOrder = (int) $objectGroup->order;
|
||||||
|
$objectGroupTitle = $objectGroup->title;
|
||||||
|
}
|
||||||
|
|
||||||
$data = [
|
$data = [
|
||||||
'id' => (int)$bill->id,
|
'id' => (int)$bill->id,
|
||||||
'created_at' => $bill->created_at->toAtomString(),
|
'created_at' => $bill->created_at->toAtomString(),
|
||||||
@ -86,6 +99,9 @@ class BillTransformer extends AbstractTransformer
|
|||||||
'next_expected_match' => $paidData['next_expected_match'],
|
'next_expected_match' => $paidData['next_expected_match'],
|
||||||
'pay_dates' => $payDates,
|
'pay_dates' => $payDates,
|
||||||
'paid_dates' => $paidData['paid_dates'],
|
'paid_dates' => $paidData['paid_dates'],
|
||||||
|
'object_group_id' => $objectGroupId,
|
||||||
|
'object_group_order' => $objectGroupOrder,
|
||||||
|
'object_group_title' => $objectGroupTitle,
|
||||||
'links' => [
|
'links' => [
|
||||||
[
|
[
|
||||||
'rel' => 'self',
|
'rel' => 'self',
|
||||||
|
@ -35,6 +35,7 @@
|
|||||||
{{ ExpandedForm.textarea('notes',null,{helpText: trans('firefly.field_supports_markdown')}) }}
|
{{ ExpandedForm.textarea('notes',null,{helpText: trans('firefly.field_supports_markdown')}) }}
|
||||||
{{ 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}) }) }}
|
||||||
{{ ExpandedForm.integer('skip',0) }}
|
{{ ExpandedForm.integer('skip',0) }}
|
||||||
|
{{ ExpandedForm.objectGroup() }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -72,4 +73,8 @@
|
|||||||
<script type="text/javascript" src="v1/js/lib/modernizr-custom.js?v={{ FF_VERSION }}" nonce="{{ JS_NONCE }}"></script>
|
<script type="text/javascript" src="v1/js/lib/modernizr-custom.js?v={{ FF_VERSION }}" nonce="{{ JS_NONCE }}"></script>
|
||||||
<script type="text/javascript" src="v1/js/lib/jquery-ui.min.js?v={{ FF_VERSION }}" nonce="{{ JS_NONCE }}"></script>
|
<script type="text/javascript" src="v1/js/lib/jquery-ui.min.js?v={{ FF_VERSION }}" nonce="{{ JS_NONCE }}"></script>
|
||||||
<script type="text/javascript" src="v1/js/ff/bills/create.js?v={{ FF_VERSION }}" nonce="{{ JS_NONCE }}"></script>
|
<script type="text/javascript" src="v1/js/ff/bills/create.js?v={{ FF_VERSION }}" nonce="{{ JS_NONCE }}"></script>
|
||||||
|
|
||||||
|
{# auto complete for object groups #}
|
||||||
|
<script type="text/javascript" src="v1/js/lib/typeahead/typeahead.bundle.min.js?v={{ FF_VERSION }}" nonce="{{ JS_NONCE }}"></script>
|
||||||
|
<script type="text/javascript" src="v1/js/ff/object-groups/create-edit.js?v={{ FF_VERSION }}" nonce="{{ JS_NONCE }}"></script>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
@ -41,6 +41,7 @@
|
|||||||
{{ ExpandedForm.textarea('notes',null,{helpText: trans('firefly.field_supports_markdown')}) }}
|
{{ ExpandedForm.textarea('notes',null,{helpText: trans('firefly.field_supports_markdown')}) }}
|
||||||
{{ 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}) }) }}
|
||||||
{{ ExpandedForm.integer('skip') }}
|
{{ ExpandedForm.integer('skip') }}
|
||||||
|
{{ ExpandedForm.objectGroup() }}
|
||||||
{# only correct way to do active checkbox #}
|
{# only correct way to do active checkbox #}
|
||||||
{{ ExpandedForm.checkbox('active', 1) }}
|
{{ ExpandedForm.checkbox('active', 1) }}
|
||||||
|
|
||||||
@ -78,4 +79,8 @@
|
|||||||
<script type="text/javascript" src="v1/js/lib/modernizr-custom.js?v={{ FF_VERSION }}" nonce="{{ JS_NONCE }}"></script>
|
<script type="text/javascript" src="v1/js/lib/modernizr-custom.js?v={{ FF_VERSION }}" nonce="{{ JS_NONCE }}"></script>
|
||||||
<script type="text/javascript" src="v1/js/lib/jquery-ui.min.js?v={{ FF_VERSION }}" nonce="{{ JS_NONCE }}"></script>
|
<script type="text/javascript" src="v1/js/lib/jquery-ui.min.js?v={{ FF_VERSION }}" nonce="{{ JS_NONCE }}"></script>
|
||||||
<script type="text/javascript" src="v1/js/ff/accounts/edit.js?v={{ FF_VERSION }}" nonce="{{ JS_NONCE }}"></script>
|
<script type="text/javascript" src="v1/js/ff/accounts/edit.js?v={{ FF_VERSION }}" nonce="{{ JS_NONCE }}"></script>
|
||||||
|
|
||||||
|
{# auto complete for object groups #}
|
||||||
|
<script type="text/javascript" src="v1/js/lib/typeahead/typeahead.bundle.min.js?v={{ FF_VERSION }}" nonce="{{ JS_NONCE }}"></script>
|
||||||
|
<script type="text/javascript" src="v1/js/ff/object-groups/create-edit.js?v={{ FF_VERSION }}" nonce="{{ JS_NONCE }}"></script>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
{% if bills.count == 0 %}
|
{% if total == 0 %}
|
||||||
{% include 'partials.empty' with {objectType: 'default', type: 'bills',route: route('bills.create')} %}
|
{% include 'partials.empty' with {objectType: 'default', type: 'bills',route: route('bills.create')} %}
|
||||||
{% else %}
|
{% else %}
|
||||||
<div class="row">
|
<div class="row">
|
||||||
@ -38,7 +38,6 @@
|
|||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block styles %}
|
{% block styles %}
|
||||||
<link href="v1/css/bootstrap-sortable.css?v={{ FF_VERSION }}" type="text/css" rel="stylesheet" media="all">
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block scripts %}
|
{% block scripts %}
|
||||||
@ -46,5 +45,4 @@
|
|||||||
var start = '2018-01-01';
|
var start = '2018-01-01';
|
||||||
var end = '2018-01-31';
|
var end = '2018-01-31';
|
||||||
</script>
|
</script>
|
||||||
<script type="text/javascript" src="v1/js/lib/bootstrap-sortable.js?v={{ FF_VERSION }}" nonce="{{ JS_NONCE }}"></script>
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
@ -1,138 +1,152 @@
|
|||||||
<div style="padding-left:8px;">
|
<div style="padding-left:8px;">
|
||||||
{{ paginator.render|raw }}
|
{{ paginator.render|raw }}
|
||||||
</div>
|
</div>
|
||||||
<table class="table table-hover sortable">
|
<table class="table table-hover">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th class="hidden-sm hidden-xs" data-defaultsort="disabled"> </th>
|
<th class="hidden-sm hidden-xs"> </th>
|
||||||
<th data-defaultsign="az">{{ trans('list.name') }}</th>
|
<th class="hidden-sm hidden-xs"> </th>
|
||||||
<th data-defaultsort="disabled" class="hidden-sm hidden-md hidden-xs">{{ trans('list.linked_to_rules') }}</th>
|
<th>{{ trans('list.name') }}</th>
|
||||||
<th data-defaultsign="_19" style="text-align: right;">{{ trans('list.matchingAmount') }}</th>
|
<th class="hidden-sm hidden-md hidden-xs">{{ trans('list.linked_to_rules') }}</th>
|
||||||
<th data-defaultsign="month" class="hidden-sm hidden-xs">{{ trans('list.paid_current_period') }}</th>
|
<th style="text-align: right;">{{ trans('list.matchingAmount') }}</th>
|
||||||
<th data-defaultsign="month" data-dateformat="{{ madMomentJS }}" class="hidden-sm hidden-xs">{{ trans('list.next_expected_match') }}</th>
|
<th class="hidden-sm hidden-xs">{{ trans('list.paid_current_period') }}</th>
|
||||||
<th data-defaultsign="az" class="hidden-sm hidden-xs">{{ trans('list.repeat_freq') }}</th>
|
<th class="hidden-sm hidden-xs">{{ trans('list.next_expected_match') }}</th>
|
||||||
|
<th class="hidden-sm hidden-xs">{{ trans('list.repeat_freq') }}</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
{% for objectGroupOrder, objectGroup in bills %}
|
||||||
{% for entry in bills %}
|
{% if objectGroup.bills|length > 0 %}
|
||||||
<tr{% if not entry.active %} data-disablesort="true"{% endif %}>
|
<tbody class="bill-connected-list" {% if objectGroupOrder != 0 %}data-title="{{ objectGroup.object_group_title }}" {% else %}data-title=""{% endif %}>
|
||||||
<td class="hidden-sm hidden-xs">
|
<tr>
|
||||||
<div class="btn-group btn-group-xs edit_tr_buttons"><a href="{{ route('bills.edit',entry.id) }}" class="btn btn-default btn-xs"><i
|
<td class="hidden-sm hidden-xs"> </td>
|
||||||
class="fa fa-fw fa-pencil"></i></a><a href="{{ route('bills.delete',entry.id) }}" class="btn btn-danger btn-xs"><i
|
<td class="hidden-sm hidden-xs"> </td>
|
||||||
class="fa fa-fw fa-trash-o"></i></a></div>
|
<td colspan="6"><small>{{ objectGroup.object_group_title }}</small></td>
|
||||||
</td>
|
</tr>
|
||||||
<td data-value="{{ entry.name }}">
|
{% for entry in objectGroup.bills %}
|
||||||
{% if not entry.active %}
|
<tr{% if not entry.active %} data-disablesort="true"{% endif %}>
|
||||||
<i class="fa fa-fw fa-ban"></i>
|
<td class="hidden-sm hidden-xs">
|
||||||
{% endif %}
|
<i class="fa fa-fw fa-bars bill-handle"></i>
|
||||||
<a href="{{ route('bills.show',entry.id) }}" title="{{ entry.name }}">{{ entry.name }}</a>
|
</td>
|
||||||
{# count attachments #}
|
<td class="hidden-sm hidden-xs">
|
||||||
{% if entry.attachments.count > 0 %}
|
<div class="btn-group btn-group-xs edit_tr_buttons"><a href="{{ route('bills.edit',entry.id) }}" class="btn btn-default btn-xs"><i
|
||||||
<i class="fa fa-paperclip"></i>
|
class="fa fa-fw fa-pencil"></i></a><a href="{{ route('bills.delete',entry.id) }}" class="btn btn-danger btn-xs"><i
|
||||||
{% endif %}
|
class="fa fa-fw fa-trash-o"></i></a></div>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
{% if not entry.active %}
|
||||||
|
<i class="fa fa-fw fa-ban"></i>
|
||||||
|
{% endif %}
|
||||||
|
<a href="{{ route('bills.show',entry.id) }}" title="{{ entry.name }}">{{ entry.name }}</a>
|
||||||
|
{# count attachments #}
|
||||||
|
{% if entry.attachments.count > 0 %}
|
||||||
|
<i class="fa fa-paperclip"></i>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
</td>
|
</td>
|
||||||
<td class="hidden-sm hidden-md hidden-xs rules">
|
<td class="hidden-sm hidden-md hidden-xs rules">
|
||||||
{% if entry.rules|length > 0 %}
|
{% if entry.rules|length > 0 %}
|
||||||
<ul class="list-unstyled">
|
<ul class="list-unstyled">
|
||||||
{% for rule in entry.rules %}
|
{% for rule in entry.rules %}
|
||||||
<li>
|
<li>
|
||||||
<a href="{{ route('rules.edit', [rule.id]) }}">
|
<a href="{{ route('rules.edit', [rule.id]) }}">
|
||||||
{{ rule.title }}
|
{{ rule.title }}
|
||||||
</a>
|
</a>
|
||||||
{% if not rule.active %}({{ 'list_inactive_rule'|_|lower }}){% endif %}
|
{% if not rule.active %}({{ 'list_inactive_rule'|_|lower }}){% endif %}
|
||||||
</li>
|
</li>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</ul>
|
</ul>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</td>
|
</td>
|
||||||
<td data-value="{{ entry.amount_min }}" style="text-align: right;">
|
<td style="text-align: right;">
|
||||||
<span style="margin-right:5px;"
|
<span style="margin-right:5px;"
|
||||||
title="{{ formatAmountBySymbol(entry.amount_min, entry.currency_symbol, entry.currency_decimal_places, false)|escape }} -- {{ formatAmountBySymbol(entry.amount_max, entry.currency_symbol, entry.currency_decimal_places, false)|escape }}"
|
title="{{ formatAmountBySymbol(entry.amount_min, entry.currency_symbol, entry.currency_decimal_places, false)|escape }} -- {{ formatAmountBySymbol(entry.amount_max, entry.currency_symbol, entry.currency_decimal_places, false)|escape }}"
|
||||||
>
|
>
|
||||||
~ {{ formatAmountBySymbol((entry.amount_max + entry.amount_min)/2, entry.currency_symbol, entry.currency_decimal_places) }}
|
~ {{ formatAmountBySymbol((entry.amount_max + entry.amount_min)/2, entry.currency_symbol, entry.currency_decimal_places) }}
|
||||||
</span>
|
</span>
|
||||||
</td>
|
</td>
|
||||||
|
|
||||||
{#
|
{#
|
||||||
paidDates = 0 (bill not paid in period)
|
paidDates = 0 (bill not paid in period)
|
||||||
pay_dates = 0 (bill not expected to be paid in this period)
|
pay_dates = 0 (bill not expected to be paid in this period)
|
||||||
bill is active.
|
bill is active.
|
||||||
#}
|
#}
|
||||||
{% if entry.paid_dates|length == 0 and entry.pay_dates|length == 0 and entry.active %}
|
{% if entry.paid_dates|length == 0 and entry.pay_dates|length == 0 and entry.active %}
|
||||||
<td class="paid_in_period text-muted" data-value="0001-01-01">
|
<td class="paid_in_period text-muted">
|
||||||
{{ trans('firefly.not_expected_period') }}
|
{{ trans('firefly.not_expected_period') }}
|
||||||
</td>
|
</td>
|
||||||
<td class="expected_in_period hidden-sm hidden-xs" data-value="{{ entry.next_expected_match }}">
|
<td class="expected_in_period hidden-sm hidden-xs">
|
||||||
{{ formatDate(entry.next_expected_match, monthAndDayFormat) }}
|
{{ formatDate(entry.next_expected_match, monthAndDayFormat) }}
|
||||||
</td>
|
</td>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
{#
|
{#
|
||||||
paid_dates = 0 (bill not paid in period)
|
paid_dates = 0 (bill not paid in period)
|
||||||
pay_dates > 0 (bill IS expected to be paid in this period)
|
pay_dates > 0 (bill IS expected to be paid in this period)
|
||||||
bill is active
|
bill is active
|
||||||
#}
|
#}
|
||||||
{% if entry.paid_dates|length == 0 and entry.pay_dates|length > 0 and entry.active %}
|
{% if entry.paid_dates|length == 0 and entry.pay_dates|length > 0 and entry.active %}
|
||||||
<td class="paid_in_period text-danger" data-value="0002-00-00">
|
<td class="paid_in_period text-danger">
|
||||||
{{ trans('firefly.bill_expected_date', {date: entry.next_expected_match_diff }) }}
|
{{ trans('firefly.bill_expected_date', {date: entry.next_expected_match_diff }) }}
|
||||||
</td>
|
</td>
|
||||||
<td class="expected_in_period hidden-sm hidden-xs" data-value="{{ entry.next_expected_match }}">
|
<td class="expected_in_period hidden-sm hidden-xs">
|
||||||
{% for date in entry.pay_dates %}
|
{% for date in entry.pay_dates %}
|
||||||
{{ formatDate(date, monthAndDayFormat) }}<br>
|
{{ formatDate(date, monthAndDayFormat) }}<br>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
|
||||||
</td>
|
</td>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
{#
|
{#
|
||||||
paid_dates >= 0 (bill is paid X times).
|
paid_dates >= 0 (bill is paid X times).
|
||||||
Don't care about pay_dates.
|
Don't care about pay_dates.
|
||||||
#}
|
#}
|
||||||
{% if entry.paid_dates|length > 0 and entry.active %}
|
{% if entry.paid_dates|length > 0 and entry.active %}
|
||||||
<td class="paid_in_period text-success" data-value="{{ entry.paid_dates[0].date }}">
|
<td class="paid_in_period text-success">
|
||||||
{% for currentPaid in entry.paid_dates %}
|
{% for currentPaid in entry.paid_dates %}
|
||||||
<a href="{{ route('transactions.show',currentPaid.transaction_group_id) }}">
|
<a href="{{ route('transactions.show',currentPaid.transaction_group_id) }}">
|
||||||
{{ formatDate(currentPaid.date, monthAndDayFormat) }}
|
{{ formatDate(currentPaid.date, monthAndDayFormat) }}
|
||||||
</a>
|
</a>
|
||||||
<br/>
|
<br/>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
</td>
|
||||||
|
<td class="expected_in_period hidden-sm hidden-xs">
|
||||||
|
{{ formatDate(entry.next_expected_match, monthAndDayFormat) }}
|
||||||
|
</td>
|
||||||
|
{% endif %}
|
||||||
|
{# bill is not active #}
|
||||||
|
{% if not entry.active %}
|
||||||
|
<td class="paid_in_period text-muted">
|
||||||
|
~
|
||||||
|
</td>
|
||||||
|
<td class="expected_in_period text-muted hidden-sm hidden-xs">~</td>
|
||||||
|
{% endif %}
|
||||||
|
<td class="hidden-sm hidden-xs">
|
||||||
|
{{ ('repeat_freq_'~entry.repeat_freq)|_ }}
|
||||||
|
{% if entry.skip > 0 %}
|
||||||
|
{{ 'skips_over'|_ }} {{ entry.skip }}
|
||||||
|
{% endif %}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
{% for sum in sums[objectGroupOrder] %}
|
||||||
|
<tr>
|
||||||
|
<td class="hidden-sm hidden-xs"> </td> <!-- handle -->
|
||||||
|
<td class="hidden-sm hidden-xs"> </td> <!-- buttons -->
|
||||||
|
<td colspan="2" style="text-align: right;"> <!-- title -->
|
||||||
|
<small>{{ 'sum'|_ }} ({{ sum.currency_name }}) ({{ 'active_exp_bills_only'|_ }})</small>
|
||||||
</td>
|
</td>
|
||||||
<td class="expected_in_period hidden-sm hidden-xs" data-value="{{ entry.next_expected_match }}">
|
<td style="text-align: right;"> <!-- amount -->
|
||||||
{{ formatDate(entry.next_expected_match, monthAndDayFormat) }}
|
{{ formatAmountBySymbol(sum.avg, sum.currency_symbol, sum.currency_decimal_places) }}
|
||||||
</td>
|
</td>
|
||||||
{% endif %}
|
<td> </td> <!-- paid in period -->
|
||||||
{# bill is not active #}
|
<td class="hidden-sm hidden-xs"> </td> <!-- next expected match -->
|
||||||
{% if not entry.active %}
|
<td class="hidden-sm hidden-xs"> </td><!-- repeats -->
|
||||||
<td class="paid_in_period text-muted" data-value="3000-01-01">
|
</tr>
|
||||||
~
|
{% endfor %}
|
||||||
</td>
|
</tbody>
|
||||||
<td class="expected_in_period text-muted hidden-sm hidden-xs" data-value="3001-01-01">~</td>
|
{% endif %}
|
||||||
{% endif %}
|
|
||||||
<td class="hidden-sm hidden-xs" data-value="{{ entry.repeat_freq }}{{ entry.skip }}">
|
|
||||||
{{ ('repeat_freq_'~entry.repeat_freq)|_ }}
|
|
||||||
{% if entry.skip > 0 %}
|
|
||||||
{{ 'skips_over'|_ }} {{ entry.skip }}
|
|
||||||
{% endif %}
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</tbody>
|
|
||||||
<tfoot>
|
|
||||||
{% for sum in sums %}
|
|
||||||
<tr>
|
|
||||||
<td style="text-align:right;" colspan="3">
|
|
||||||
{{ 'sum'|_ }} ({{ sum.currency_name }}) ({{ 'active_exp_bills_only'|_ }})<br />
|
|
||||||
</td>
|
|
||||||
<td style="text-align:right;" colspan="1">
|
|
||||||
<span style="margin-right:5px;">
|
|
||||||
{{ formatAmountBySymbol(sum.avg, sum.currency_symbol, sum.currency_decimal_places) }}
|
|
||||||
</span>
|
|
||||||
</td>
|
|
||||||
<td colspan="6"> </td>
|
|
||||||
</tr>
|
|
||||||
{% endfor %}
|
|
||||||
</tfoot>
|
|
||||||
</table>
|
</table>
|
||||||
<div style="padding-left:8px;">
|
<div style="padding-left:8px;">
|
||||||
{{ paginator.render|raw }}
|
{{ paginator.render|raw }}
|
||||||
|
@ -45,6 +45,9 @@
|
|||||||
{% for piggyBank in objectGroup.piggyBanks %}
|
{% for piggyBank in objectGroup.piggyBanks %}
|
||||||
- {{ 'piggy_bank'|_ }}: <a href="{{ route('piggy-banks.show', [piggyBank.id]) }}">{{ piggyBank.name }}</a><br>
|
- {{ 'piggy_bank'|_ }}: <a href="{{ route('piggy-banks.show', [piggyBank.id]) }}">{{ piggyBank.name }}</a><br>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
{% for bill in objectGroup.bills %}
|
||||||
|
- {{ 'bill'|_ }}: <a href="{{ route('bills.show', [bill.id]) }}">{{ bill.name }}</a><br>
|
||||||
|
{% endfor %}
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<div class="btn-group btn-group-sm">
|
<div class="btn-group btn-group-sm">
|
||||||
|
Loading…
Reference in New Issue
Block a user