firefly-iii/app/Http/Controllers/Transaction/ConvertController.php

370 lines
14 KiB
PHP
Raw Normal View History

2016-10-29 08:14:33 -05:00
<?php
/**
* ConvertController.php
2017-10-21 01:40:00 -05:00
* Copyright (c) 2017 thegrumpydictator@gmail.com
2016-10-29 08:14:33 -05:00
*
2017-10-21 01:40:00 -05:00
* This file is part of Firefly III.
2016-10-29 08:14:33 -05:00
*
2017-10-21 01:40:00 -05:00
* Firefly III is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Firefly III is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
2017-12-17 07:41:58 -06:00
* along with Firefly III. If not, see <http://www.gnu.org/licenses/>.
2016-10-29 08:14:33 -05:00
*/
declare(strict_types=1);
2016-10-29 08:14:33 -05:00
namespace FireflyIII\Http\Controllers\Transaction;
use Carbon\Carbon;
2019-03-30 05:03:39 -05:00
use FireflyIII\Events\UpdatedTransactionGroup;
2016-10-30 00:14:07 -05:00
use FireflyIII\Exceptions\FireflyException;
2016-10-29 08:14:33 -05:00
use FireflyIII\Http\Controllers\Controller;
use FireflyIII\Models\Account;
use FireflyIII\Models\AccountType;
use FireflyIII\Models\TransactionGroup;
2016-10-29 08:14:33 -05:00
use FireflyIII\Models\TransactionJournal;
use FireflyIII\Models\TransactionType;
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
2016-10-30 00:14:07 -05:00
use FireflyIII\Repositories\Journal\JournalRepositoryInterface;
use FireflyIII\Services\Internal\Update\JournalUpdateService;
2018-08-09 09:13:13 -05:00
use FireflyIII\Support\Http\Controllers\ModelInformation;
2019-08-17 01:29:35 -05:00
use FireflyIII\Support\Http\Controllers\UserNavigation;
use FireflyIII\Transformers\TransactionGroupTransformer;
use FireflyIII\Validation\AccountValidator;
2016-10-29 08:14:33 -05:00
use Illuminate\Http\Request;
2018-07-01 02:27:22 -05:00
use Log;
2016-10-29 08:14:33 -05:00
use View;
2016-10-29 08:14:33 -05:00
/**
2017-11-15 05:25:49 -06:00
* Class ConvertController.
2019-07-13 13:57:06 -05:00
*
* TODO when converting to a split transfer, all sources and destinations must be the same.
2016-10-29 08:14:33 -05:00
*/
class ConvertController extends Controller
{
2019-08-17 01:29:35 -05:00
use ModelInformation, UserNavigation;
2018-08-09 09:13:13 -05:00
/** @var JournalRepositoryInterface Journals and transactions overview */
private $repository;
2016-10-29 08:14:33 -05:00
/**
* ConvertController constructor.
* @codeCoverageIgnore
2016-10-29 08:14:33 -05:00
*/
public function __construct()
{
parent::__construct();
// some useful repositories:
$this->middleware(
function ($request, $next) {
$this->repository = app(JournalRepositoryInterface::class);
2016-10-29 08:14:33 -05:00
2018-07-15 02:38:49 -05:00
app('view')->share('title', (string)trans('firefly.transactions'));
2017-12-16 12:46:36 -06:00
app('view')->share('mainTitleIcon', 'fa-exchange');
2016-10-29 08:14:33 -05:00
return $next($request);
}
);
}
2018-07-08 05:08:53 -05:00
2016-10-29 08:14:33 -05:00
/**
2018-07-22 01:10:16 -05:00
* Show overview of a to be converted transaction.
*
* @param TransactionType $destinationType
* @param TransactionGroup $group
2016-10-29 08:14:33 -05:00
*
* @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector|View
* @throws \Exception
2016-10-29 08:14:33 -05:00
*/
public function index(TransactionType $destinationType, TransactionGroup $group)
2016-10-29 08:14:33 -05:00
{
2019-08-17 01:29:35 -05:00
if (!$this->isEditableGroup($group)) {
return $this->redirectGroupToAccount($group); // @codeCoverageIgnore
}
/** @var TransactionGroupTransformer $transformer */
$transformer = app(TransactionGroupTransformer::class);
/** @var TransactionJournal $first */
$first = $group->transactionJournals()->first();
$sourceType = $first->transactionType;
$groupTitle = $group->title ?? $first->description;
$groupArray = $transformer->transformObject($group);
$subTitle = (string)trans('firefly.convert_to_' . $destinationType->type, ['description' => $groupTitle]);
$subTitleIcon = 'fa-exchange';
// get a list of asset accounts and liabilities and stuff, in various combinations:
$validDepositSources = $this->getValidDepositSources();
$validWithdrawalDests = $this->getValidWithdrawalDests();
$liabilities = $this->getLiabilities();
$assets = $this->getAssetAccounts();
// old input variables:
$preFilled = [
'source_name' => old('source_name'),
];
2016-10-29 08:14:33 -05:00
2018-07-20 07:34:56 -05:00
if ($sourceType->type === $destinationType->type) { // cannot convert to its own type.
2018-07-01 02:27:22 -05:00
Log::debug('This is already a transaction of the expected type..');
2018-07-15 02:38:49 -05:00
session()->flash('info', (string)trans('firefly.convert_is_already_type_' . $destinationType->type));
2016-10-29 08:14:33 -05:00
return redirect(route('transactions.show', [$group->id]));
2016-10-29 08:14:33 -05:00
}
2016-10-29 10:30:55 -05:00
2016-10-29 08:14:33 -05:00
return view(
2018-04-24 12:48:42 -05:00
'transactions.convert', compact(
'sourceType', 'destinationType',
'group', 'groupTitle', 'groupArray', 'assets', 'validDepositSources', 'liabilities',
'validWithdrawalDests', 'preFilled',
2018-06-06 14:23:00 -05:00
'subTitle', 'subTitleIcon'
)
2016-10-29 08:14:33 -05:00
);
}
2016-10-29 10:30:55 -05:00
/**
2018-07-22 01:10:16 -05:00
* Do the conversion.
*
* @param Request $request
* @param TransactionType $destinationType
* @param TransactionGroup $group
2016-10-30 00:14:07 -05:00
*
* @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector
2017-12-22 11:32:43 -06:00
*
2017-12-17 07:30:53 -06:00
* @throws FireflyException
2016-10-29 10:30:55 -05:00
*/
public function postIndex(Request $request, TransactionType $destinationType, TransactionGroup $group)
2016-10-29 08:14:33 -05:00
{
2019-08-17 01:29:35 -05:00
if (!$this->isEditableGroup($group)) {
return $this->redirectGroupToAccount($group); // @codeCoverageIgnore
}
/** @var TransactionJournal $journal */
foreach ($group->transactionJournals as $journal) {
// catch FF exception.
try {
$this->convertJournal($journal, $destinationType, $request->all());
} catch (FireflyException $e) {
session()->flash('error', $e->getMessage());
return redirect()->route('transactions.convert.index', [strtolower($destinationType->type), $group->id])->withInput();
}
2016-11-22 12:10:17 -06:00
}
2019-07-13 13:57:06 -05:00
// correct transfers:
$group->refresh();
$this->correctTransfer($group);
session()->flash('success', (string)trans('firefly.converted_to_' . $destinationType->type));
event(new UpdatedTransactionGroup($group));
2016-11-22 12:10:17 -06:00
return redirect(route('transactions.show', [$group->id]));
}
2016-10-29 10:30:55 -05:00
/**
* @return array
* @throws \Exception
*/
private function getAssetAccounts(): array
{
// make repositories
/** @var AccountRepositoryInterface $repository */
$repository = app(AccountRepositoryInterface::class);
$accountList = $repository->getActiveAccountsByType([AccountType::ASSET]);
$defaultCurrency = app('amount')->getDefaultCurrency();
$grouped = [];
// group accounts:
/** @var Account $account */
foreach ($accountList as $account) {
$balance = app('steam')->balance($account, new Carbon);
$currency = $repository->getAccountCurrency($account) ?? $defaultCurrency;
$role = (string)$repository->getMetaValue($account, 'account_role');
if ('' === $role) {
$role = 'no_account_type'; // @codeCoverageIgnore
}
2016-10-29 10:30:55 -05:00
$key = (string)trans('firefly.opt_group_' . $role);
$grouped[$key][$account->id] = $account->name . ' (' . app('amount')->formatAnything($currency, $balance, false) . ')';
2016-10-29 10:30:55 -05:00
}
return $grouped;
}
2016-10-29 10:30:55 -05:00
/**
* @return array
* @throws \Exception
*/
private function getLiabilities(): array
{
// make repositories
/** @var AccountRepositoryInterface $repository */
$repository = app(AccountRepositoryInterface::class);
$accountList = $repository->getActiveAccountsByType([AccountType::LOAN, AccountType::DEBT, AccountType::MORTGAGE]);
$defaultCurrency = app('amount')->getDefaultCurrency();
$grouped = [];
// group accounts:
/** @var Account $account */
foreach ($accountList as $account) {
$balance = app('steam')->balance($account, new Carbon);
$currency = $repository->getAccountCurrency($account) ?? $defaultCurrency;
2019-07-13 13:57:06 -05:00
$role = 'l_' . $account->accountType->type;
$key = (string)trans('firefly.opt_group_' . $role);
$grouped[$key][$account->id] = $account->name . ' (' . app('amount')->formatAnything($currency, $balance, false) . ')';
2016-10-29 10:30:55 -05:00
}
return $grouped;
}
2016-10-30 00:14:07 -05:00
/**
* @return array
*/
private function getValidDepositSources(): array
{
// make repositories
/** @var AccountRepositoryInterface $repository */
$repository = app(AccountRepositoryInterface::class);
$liabilityTypes = [AccountType::MORTGAGE, AccountType::DEBT, AccountType::CREDITCARD, AccountType::LOAN];
$accountList = $repository
->getActiveAccountsByType([AccountType::REVENUE, AccountType::CASH, AccountType::LOAN, AccountType::DEBT, AccountType::MORTGAGE]);
$grouped = [];
// group accounts:
/** @var Account $account */
foreach ($accountList as $account) {
$role = (string)$repository->getMetaValue($account, 'account_role');
$name = $account->name;
if ('' === $role) {
$role = 'no_account_type'; // @codeCoverageIgnore
}
2016-10-29 10:30:55 -05:00
// maybe it's a liability thing:
if (in_array($account->accountType->type, $liabilityTypes, true)) {
$role = 'l_' . $account->accountType->type; // @codeCoverageIgnore
}
if (AccountType::CASH === $account->accountType->type) {
2019-07-13 13:57:06 -05:00
// @codeCoverageIgnoreStart
$role = 'cash_account';
$name = sprintf('(%s)', trans('firefly.cash'));
2019-07-13 13:57:06 -05:00
// @codeCoverageIgnoreEnd
}
if (AccountType::REVENUE === $account->accountType->type) {
2019-07-13 13:57:06 -05:00
$role = 'revenue_account'; // @codeCoverageIgnore
}
2018-12-31 01:11:57 -06:00
$key = (string)trans('firefly.opt_group_' . $role);
$grouped[$key][$account->id] = $name;
2016-10-30 00:14:07 -05:00
}
2016-10-29 10:30:55 -05:00
return $grouped;
}
2018-09-13 13:01:41 -05:00
/**
* @return array
*/
private function getValidWithdrawalDests(): array
{
// make repositories
/** @var AccountRepositoryInterface $repository */
$repository = app(AccountRepositoryInterface::class);
$liabilityTypes = [AccountType::MORTGAGE, AccountType::DEBT, AccountType::CREDITCARD, AccountType::LOAN];
$accountList = $repository
->getActiveAccountsByType([AccountType::EXPENSE, AccountType::CASH, AccountType::LOAN, AccountType::DEBT, AccountType::MORTGAGE]);
$grouped = [];
// group accounts:
/** @var Account $account */
foreach ($accountList as $account) {
$role = (string)$repository->getMetaValue($account, 'account_role');
$name = $account->name;
if ('' === $role) {
$role = 'no_account_type'; // @codeCoverageIgnore
}
2018-09-13 13:01:41 -05:00
// maybe it's a liability thing:
if (in_array($account->accountType->type, $liabilityTypes, true)) {
$role = 'l_' . $account->accountType->type; // @codeCoverageIgnore
}
if (AccountType::CASH === $account->accountType->type) {
2019-07-13 13:57:06 -05:00
// @codeCoverageIgnoreStart
$role = 'cash_account';
$name = sprintf('(%s)', trans('firefly.cash'));
2019-07-13 13:57:06 -05:00
// @codeCoverageIgnoreEnd
}
if (AccountType::EXPENSE === $account->accountType->type) {
2019-07-13 13:57:06 -05:00
$role = 'expense_account'; // @codeCoverageIgnore
}
$key = (string)trans('firefly.opt_group_' . $role);
$grouped[$key][$account->id] = $name;
}
return $grouped;
}
/**
* @param TransactionJournal $journal
* @param TransactionType $transactionType
* @param array $data
* @return TransactionJournal
* @throws FireflyException
*/
private function convertJournal(TransactionJournal $journal, TransactionType $transactionType, array $data): TransactionJournal
{
/** @var AccountValidator $validator */
$validator = app(AccountValidator::class);
$validator->setUser(auth()->user());
$validator->setTransactionType($transactionType->type);
$sourceId = $data['source_id'][$journal->id] ?? null;
$sourceName = $data['source_name'][$journal->id] ?? null;
$destinationId = $data['destination_id'][$journal->id] ?? null;
$destinationName = $data['destination_name'][$journal->id] ?? null;
// double check its not an empty string.
$sourceId = '' === $sourceId || null === $sourceId ? null : (int)$sourceId;
$sourceName = '' === $sourceName ? null : $sourceName;
$destinationId = '' === $destinationId || null === $destinationId ? null : (int)$destinationId;
$destinationName = '' === $destinationName ? null : $destinationName;
$validSource = $validator->validateSource($sourceId, $sourceName);
$validDestination = $validator->validateDestination($destinationId, $destinationName);
if (false === $validSource) {
throw new FireflyException(sprintf(trans('firefly.convert_invalid_source'), $journal->id));
}
if (false === $validDestination) {
throw new FireflyException(sprintf(trans('firefly.convert_invalid_destination'), $journal->id));
}
2016-10-29 10:30:55 -05:00
$update = [
'source_id' => $sourceId,
'source_name' => $sourceName,
'destination_id' => $destinationId,
'destination_name' => $destinationName,
'type' => $transactionType->type,
];
/** @var JournalUpdateService $service */
$service = app(JournalUpdateService::class);
$service->setTransactionJournal($journal);
$service->setData($update);
$service->update();
$journal->refresh();
return $journal;
2016-10-30 00:14:07 -05:00
}
2019-07-13 13:57:06 -05:00
/**
* @param TransactionGroup $group
*/
private function correctTransfer(TransactionGroup $group): void
{
}
}