Files
firefly-iii/app/Http/Controllers/Account/ReconcileController.php

275 lines
9.5 KiB
PHP
Raw Normal View History

2017-11-15 06:29:49 +01:00
<?php
2022-12-29 19:41:57 +01:00
2017-11-15 06:29:49 +01:00
/**
* ReconcileController.php
2020-01-31 07:32:04 +01:00
* Copyright (c) 2019 james@firefly-iii.org
2017-11-15 06:29:49 +01:00
*
* This file is part of Firefly III (https://github.com/firefly-iii).
2017-11-15 06:29:49 +01:00
*
* 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.
2017-11-15 06:29:49 +01:00
*
* This program is distributed in the hope that it will be useful,
2017-11-15 06:29:49 +01:00
* 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.
2017-11-15 06:29:49 +01:00
*
* 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/>.
2017-11-15 06:29:49 +01:00
*/
declare(strict_types=1);
namespace FireflyIII\Http\Controllers\Account;
use Carbon\Carbon;
use FireflyIII\Exceptions\DuplicateTransactionException;
2019-06-23 05:53:01 +02:00
use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Factory\TransactionGroupFactory;
2017-11-15 06:29:49 +01:00
use FireflyIII\Http\Controllers\Controller;
2018-02-23 15:13:01 +01:00
use FireflyIII\Http\Requests\ReconciliationStoreRequest;
2017-11-15 06:29:49 +01:00
use FireflyIII\Models\Account;
use FireflyIII\Models\AccountType;
2019-06-23 05:53:01 +02:00
use FireflyIII\Models\TransactionType;
2017-11-22 17:49:06 +01:00
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
2017-11-22 16:54:49 +01:00
use FireflyIII\Repositories\Journal\JournalRepositoryInterface;
2019-06-23 05:53:01 +02:00
use FireflyIII\User;
use Illuminate\Contracts\View\Factory;
use Illuminate\Http\RedirectResponse;
use Illuminate\Routing\Redirector;
2023-05-29 13:56:55 +02:00
use Illuminate\Support\Facades\Log;
use Illuminate\View\View;
2022-12-29 19:41:57 +01:00
use JsonException;
use Psr\Container\ContainerExceptionInterface;
use Psr\Container\NotFoundExceptionInterface;
2017-11-15 06:29:49 +01:00
/**
2017-11-15 12:25:49 +01:00
* Class ReconcileController.
2017-11-15 06:29:49 +01:00
*/
class ReconcileController extends Controller
{
2023-01-03 06:48:53 +01:00
private AccountRepositoryInterface $accountRepos;
private JournalRepositoryInterface $repository;
2017-11-15 06:29:49 +01:00
/**
* ReconcileController constructor.
*
2023-02-12 07:23:57 +01:00
2017-11-15 06:29:49 +01:00
*/
public function __construct()
{
parent::__construct();
// translations:
$this->middleware(
function ($request, $next) {
2017-12-16 19:46:36 +01:00
app('view')->share('mainTitleIcon', 'fa-credit-card');
2022-12-29 19:41:57 +01:00
app('view')->share('title', (string)trans('firefly.accounts'));
2023-01-03 06:48:53 +01:00
$this->repository = app(JournalRepositoryInterface::class);
$this->accountRepos = app(AccountRepositoryInterface::class);
2017-11-15 06:29:49 +01:00
return $next($request);
}
);
}
/**
* Reconciliation overview.
*
2022-12-29 19:41:57 +01:00
* @param Account $account
* @param Carbon|null $start
* @param Carbon|null $end
2017-11-15 06:29:49 +01:00
*
* @return Factory|RedirectResponse|Redirector|View
2022-03-29 15:10:05 +02:00
* @throws FireflyException
2022-12-29 19:41:57 +01:00
* @throws JsonException
* @throws ContainerExceptionInterface
* @throws NotFoundExceptionInterface
2017-11-15 06:29:49 +01:00
*/
2022-12-31 07:33:44 +01:00
public function reconcile(Account $account, Carbon $start = null, Carbon $end = null)
2017-11-15 06:29:49 +01:00
{
2019-08-17 08:29:35 +02:00
if (!$this->isEditableAccount($account)) {
return $this->redirectAccountToAccount($account);
2019-08-17 08:29:35 +02:00
}
2017-11-22 17:49:06 +01:00
if (AccountType::ASSET !== $account->accountType->type) {
2022-12-29 19:41:57 +01:00
session()->flash('error', (string)trans('firefly.must_be_asset_account'));
2017-11-22 17:49:06 +01:00
2019-06-22 13:09:11 +02:00
return redirect(route('accounts.index', [config(sprintf('firefly.shortNamesByFullName.%s', $account->accountType->type))]));
2017-11-15 06:29:49 +01:00
}
2019-06-22 13:09:11 +02:00
$currency = $this->accountRepos->getAccountCurrency($account) ?? app('amount')->getDefaultCurrency();
2017-11-15 06:29:49 +01:00
// no start or end:
2023-02-11 07:36:45 +01:00
$range = app('navigation')->getViewRange(false);
2017-11-15 06:29:49 +01:00
// get start and end
2021-04-07 07:28:43 +02:00
2017-11-15 12:25:49 +01:00
if (null === $start && null === $end) {
2018-07-27 04:46:21 +02:00
/** @var Carbon $start */
2022-10-30 14:24:19 +01:00
$start = clone session('start', app('navigation')->startOfPeriod(new Carbon(), $range));
2018-07-27 04:46:21 +02:00
/** @var Carbon $end */
2022-10-30 14:24:19 +01:00
$end = clone session('end', app('navigation')->endOfPeriod(new Carbon(), $range));
2017-11-15 06:29:49 +01:00
}
2017-11-15 12:25:49 +01:00
if (null === $end) {
2018-07-27 04:46:21 +02:00
/** @var Carbon $end */
$end = app('navigation')->endOfPeriod($start, $range);
2017-11-15 06:29:49 +01:00
}
2021-04-07 07:28:43 +02:00
2020-01-05 08:04:55 +01:00
if ($end->lt($start)) {
[$start, $end] = [$end, $start];
}
2017-11-15 06:29:49 +01:00
$startDate = clone $start;
2019-06-22 13:09:11 +02:00
$startDate->subDay();
2022-12-24 05:06:39 +01:00
$startBalance = app('steam')->bcround(app('steam')->balance($account, $startDate), $currency->decimal_places);
$endBalance = app('steam')->bcround(app('steam')->balance($account, $end), $currency->decimal_places);
2019-06-22 13:09:11 +02:00
$subTitleIcon = config(sprintf('firefly.subIconsByIdentifier.%s', $account->accountType->type));
2022-12-29 19:41:57 +01:00
$subTitle = (string)trans('firefly.reconcile_account', ['account' => $account->name]);
2017-11-15 06:29:49 +01:00
// various links
2022-04-12 18:19:30 +02:00
$transactionsUrl = route('accounts.reconcile.transactions', [$account->id, '%start%', '%end%']);
$overviewUrl = route('accounts.reconcile.overview', [$account->id, '%start%', '%end%']);
$indexUrl = route('accounts.reconcile', [$account->id, '%start%', '%end%']);
2019-06-22 13:09:11 +02:00
$objectType = 'asset';
2018-04-24 19:26:16 +02:00
return view(
'accounts.reconcile.index',
compact(
'account',
'currency',
'objectType',
'subTitleIcon',
'start',
'end',
'subTitle',
'startBalance',
'endBalance',
2022-04-12 18:19:30 +02:00
'transactionsUrl',
'overviewUrl',
'indexUrl'
)
);
2017-11-15 06:29:49 +01:00
}
2017-11-22 17:49:06 +01:00
/**
* Submit a new reconciliation.
*
2022-12-29 19:41:57 +01:00
* @param ReconciliationStoreRequest $request
* @param Account $account
* @param Carbon $start
* @param Carbon $end
2017-11-25 15:20:53 +01:00
*
* @return RedirectResponse|Redirector
2021-05-24 08:54:58 +02:00
* @throws DuplicateTransactionException
2017-11-22 17:49:06 +01:00
*/
2018-07-08 12:08:53 +02:00
public function submit(ReconciliationStoreRequest $request, Account $account, Carbon $start, Carbon $end)
2017-11-22 17:49:06 +01:00
{
2019-08-17 08:29:35 +02:00
if (!$this->isEditableAccount($account)) {
return $this->redirectAccountToAccount($account);
2019-08-17 08:29:35 +02:00
}
2018-02-23 15:13:01 +01:00
Log::debug('In ReconcileController::submit()');
$data = $request->getAll();
2019-06-23 05:53:01 +02:00
/** @var string $journalId */
foreach ($data['journals'] as $journalId) {
2022-12-29 19:41:57 +01:00
$this->repository->reconcileById((int)$journalId);
2017-11-22 17:49:06 +01:00
}
2018-02-23 15:13:01 +01:00
Log::debug('Reconciled all transactions.');
2017-11-22 17:49:06 +01:00
2020-01-05 08:04:55 +01:00
// switch dates if necessary
if ($end->lt($start)) {
[$start, $end] = [$end, $start];
}
2017-11-22 17:49:06 +01:00
// create reconciliation transaction (if necessary):
2019-06-23 05:53:01 +02:00
$result = '';
2018-02-23 15:13:01 +01:00
if ('create' === $data['reconcile']) {
2019-06-23 05:53:01 +02:00
$result = $this->createReconciliation($account, $start, $end, $data['difference']);
2017-11-22 17:49:06 +01:00
}
2018-02-23 15:13:01 +01:00
Log::debug('End of routine.');
2018-07-08 12:08:53 +02:00
app('preferences')->mark();
2019-06-23 05:53:01 +02:00
if ('' === $result) {
2022-12-29 19:41:57 +01:00
session()->flash('success', (string)trans('firefly.reconciliation_stored'));
2019-06-23 05:53:01 +02:00
}
if ('' !== $result) {
2022-12-29 19:41:57 +01:00
session()->flash('error', (string)trans('firefly.reconciliation_error', ['error' => $result]));
2019-06-23 05:53:01 +02:00
}
2017-11-22 17:49:06 +01:00
return redirect(route('accounts.show', [$account->id]));
}
2019-06-23 05:53:01 +02:00
/**
* Creates a reconciliation group.
2020-01-05 08:04:55 +01:00
*
2022-12-29 19:41:57 +01:00
* @param Account $account
* @param Carbon $start
* @param Carbon $end
* @param string $difference
2020-01-05 08:04:55 +01:00
*
2021-03-28 11:46:23 +02:00
* @return RedirectResponse|Redirector|string
* @throws DuplicateTransactionException
2023-02-22 18:03:31 +01:00
* @throws JsonException
2019-06-23 05:53:01 +02:00
*/
2020-01-05 08:04:55 +01:00
private function createReconciliation(Account $account, Carbon $start, Carbon $end, string $difference)
2019-06-23 05:53:01 +02:00
{
2019-08-17 08:29:35 +02:00
if (!$this->isEditableAccount($account)) {
return $this->redirectAccountToAccount($account);
2019-08-17 08:29:35 +02:00
}
2019-06-23 05:53:01 +02:00
$reconciliation = $this->accountRepos->getReconciliation($account);
$currency = $this->accountRepos->getAccountCurrency($account) ?? app('amount')->getDefaultCurrency();
$source = $reconciliation;
$destination = $account;
if (1 === bccomp($difference, '0')) {
$source = $account;
$destination = $reconciliation;
}
2020-01-05 08:04:55 +01:00
if ($end->lt($start)) {
[$start, $end] = [$end, $start];
}
2019-06-23 05:53:01 +02:00
// title:
$description = trans(
'firefly.reconciliation_transaction_title',
2022-12-29 19:41:57 +01:00
[
'from' => $start->isoFormat($this->monthAndDayFormat),
'to' => $end->isoFormat($this->monthAndDayFormat),
]
);
$submission = [
2019-06-23 05:53:01 +02:00
'user' => auth()->user()->id,
'group_title' => null,
'transactions' => [
[
'user' => auth()->user()->id,
'type' => strtolower(TransactionType::RECONCILIATION),
'date' => $end,
'order' => 0,
'currency_id' => $currency->id,
'foreign_currency_id' => null,
'amount' => $difference,
'foreign_amount' => null,
'description' => $description,
'source_id' => $source->id,
'destination_id' => $destination->id,
'reconciled' => true,
],
],
];
/** @var TransactionGroupFactory $factory */
$factory = app(TransactionGroupFactory::class);
/** @var User $user */
$user = auth()->user();
$factory->setUser($user);
try {
$factory->create($submission);
} catch (FireflyException $e) {
return $e->getMessage();
}
return '';
}
2017-11-15 10:52:29 +01:00
}