mirror of
https://github.com/firefly-iii/firefly-iii.git
synced 2025-02-25 18:45:27 -06:00
Is now capable of updating transactions over the API.
This commit is contained in:
parent
b692cccdfb
commit
c519b4d0df
@ -24,7 +24,8 @@ declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\Api\V1\Controllers;
|
||||
|
||||
use FireflyIII\Api\V1\Requests\TransactionRequest;
|
||||
use FireflyIII\Api\V1\Requests\TransactionStoreRequest;
|
||||
use FireflyIII\Api\V1\Requests\TransactionUpdateRequest;
|
||||
use FireflyIII\Events\StoredTransactionGroup;
|
||||
use FireflyIII\Events\UpdatedTransactionGroup;
|
||||
use FireflyIII\Exceptions\FireflyException;
|
||||
@ -32,6 +33,7 @@ use FireflyIII\Helpers\Collector\GroupCollectorInterface;
|
||||
use FireflyIII\Models\TransactionGroup;
|
||||
use FireflyIII\Models\TransactionJournal;
|
||||
use FireflyIII\Repositories\Journal\JournalRepositoryInterface;
|
||||
use FireflyIII\Repositories\TransactionGroup\TransactionGroupRepositoryInterface;
|
||||
use FireflyIII\Support\Http\Api\TransactionFilter;
|
||||
use FireflyIII\Transformers\AttachmentTransformer;
|
||||
use FireflyIII\Transformers\PiggyBankEventTransformer;
|
||||
@ -44,16 +46,18 @@ use League\Fractal\Pagination\IlluminatePaginatorAdapter;
|
||||
use League\Fractal\Resource\Collection as FractalCollection;
|
||||
use League\Fractal\Resource\Item;
|
||||
use League\Fractal\Serializer\JsonApiSerializer;
|
||||
use Log;
|
||||
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
|
||||
|
||||
/**
|
||||
* Class TransactionController
|
||||
* @SuppressWarnings(PHPMD.CouplingBetweenObjects)
|
||||
*/
|
||||
class TransactionController extends Controller
|
||||
{
|
||||
use TransactionFilter;
|
||||
|
||||
/** @var TransactionGroupRepositoryInterface Group repository. */
|
||||
private $groupRepository;
|
||||
/** @var JournalRepositoryInterface The journal repository */
|
||||
private $repository;
|
||||
|
||||
@ -68,9 +72,10 @@ class TransactionController extends Controller
|
||||
/** @var User $admin */
|
||||
$admin = auth()->user();
|
||||
|
||||
/** @var JournalRepositoryInterface repository */
|
||||
$this->repository = app(JournalRepositoryInterface::class);
|
||||
$this->repository = app(JournalRepositoryInterface::class);
|
||||
$this->groupRepository = app(TransactionGroupRepositoryInterface::class);
|
||||
$this->repository->setUser($admin);
|
||||
$this->groupRepository->setUser($admin);
|
||||
|
||||
return $next($request);
|
||||
}
|
||||
@ -247,18 +252,16 @@ class TransactionController extends Controller
|
||||
/**
|
||||
* Store a new transaction.
|
||||
*
|
||||
* @param TransactionRequest $request
|
||||
* @param TransactionStoreRequest $request
|
||||
*
|
||||
* @param JournalRepositoryInterface $repository
|
||||
*
|
||||
* @throws FireflyException
|
||||
* @return JsonResponse
|
||||
* @throws FireflyException
|
||||
*/
|
||||
public function store(TransactionRequest $request, JournalRepositoryInterface $repository): JsonResponse
|
||||
public function store(TransactionStoreRequest $request): JsonResponse
|
||||
{
|
||||
$data = $request->getAll();
|
||||
$data['user'] = auth()->user()->id;
|
||||
$transactionGroup = $repository->store($data);
|
||||
$transactionGroup = $this->groupRepository->store($data);
|
||||
|
||||
event(new StoredTransactionGroup($transactionGroup));
|
||||
|
||||
@ -294,16 +297,16 @@ class TransactionController extends Controller
|
||||
/**
|
||||
* Update a transaction.
|
||||
*
|
||||
* @param TransactionRequest $request
|
||||
* @param TransactionGroup $transactionGroup
|
||||
* @param TransactionUpdateRequest $request
|
||||
* @param TransactionGroup $transactionGroup
|
||||
*
|
||||
* @return JsonResponse
|
||||
*/
|
||||
public function update(TransactionRequest $request, TransactionGroup $transactionGroup): JsonResponse
|
||||
public function update(TransactionUpdateRequest $request, TransactionGroup $transactionGroup): JsonResponse
|
||||
{
|
||||
Log::debug('Now in update routine.');
|
||||
$data = $request->getAll();
|
||||
$data['user'] = auth()->user()->id;
|
||||
$transactionGroup = $this->repository->update($transactionGroup, $data);
|
||||
$transactionGroup = $this->groupRepository->update($transactionGroup, $data);
|
||||
$manager = new Manager();
|
||||
$baseUrl = $request->getSchemeAndHttpHost() . '/api/v1';
|
||||
$manager->setSerializer(new JsonApiSerializer($baseUrl));
|
||||
|
@ -1,7 +1,7 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* TransactionRequest.php
|
||||
* TransactionStoreRequest.php
|
||||
* Copyright (c) 2018 thegrumpydictator@gmail.com
|
||||
*
|
||||
* This file is part of Firefly III.
|
||||
@ -33,9 +33,9 @@ use Illuminate\Validation\Validator;
|
||||
|
||||
|
||||
/**
|
||||
* Class TransactionRequest
|
||||
* Class TransactionStoreRequest
|
||||
*/
|
||||
class TransactionRequest extends Request
|
||||
class TransactionStoreRequest extends Request
|
||||
{
|
||||
use TransactionValidation;
|
||||
|
||||
@ -82,6 +82,7 @@ class TransactionRequest extends Request
|
||||
// transaction rules (in array for splits):
|
||||
'transactions.*.type' => 'required|in:withdrawal,deposit,transfer,opening-balance,reconciliation',
|
||||
'transactions.*.date' => ['required', new IsDateOrTime],
|
||||
'transactions.*.order' => 'numeric|min:0',
|
||||
|
||||
// currency info
|
||||
'transactions.*.currency_id' => 'numeric|exists:transaction_currencies,id',
|
||||
@ -144,10 +145,6 @@ class TransactionRequest extends Request
|
||||
'transactions.*.invoice_date' => 'date|nullable',
|
||||
];
|
||||
|
||||
if ('PUT' === $this->method()) {
|
||||
unset($rules['transactions.*.type'], $rules['transactions.*.piggy_bank_id'], $rules['transactions.*.piggy_bank_name']);
|
||||
}
|
||||
|
||||
return $rules;
|
||||
|
||||
|
||||
@ -156,7 +153,7 @@ class TransactionRequest extends Request
|
||||
/**
|
||||
* Configure the validator instance.
|
||||
*
|
||||
* @param Validator $validator
|
||||
* @param Validator $validator
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
@ -170,14 +167,13 @@ class TransactionRequest extends Request
|
||||
// all journals must have a description
|
||||
$this->validateDescriptions($validator);
|
||||
|
||||
// all transaction types must be equal:
|
||||
$this->validateTransactionTypes($validator);
|
||||
// all transaction types must be equal:
|
||||
$this->validateTransactionTypes($validator);
|
||||
|
||||
// validate foreign currency info
|
||||
$this->validateForeignCurrencyInformation($validator);
|
||||
|
||||
|
||||
|
||||
// validate all account info
|
||||
$this->validateAccountInformation($validator);
|
||||
|
||||
@ -186,6 +182,8 @@ class TransactionRequest extends Request
|
||||
|
||||
// the group must have a description if > 1 journal.
|
||||
$this->validateGroupDescription($validator);
|
||||
|
||||
// TODO validate that the currency fits the source and/or destination account.
|
||||
}
|
||||
);
|
||||
}
|
||||
@ -207,9 +205,10 @@ class TransactionRequest extends Request
|
||||
foreach ($this->get('transactions') as $index => $transaction) {
|
||||
$object = new NullArrayObject($transaction);
|
||||
$return[] = [
|
||||
// $this->dateFromValue($object[''])
|
||||
'type' => $this->stringFromValue($object['type']),
|
||||
'date' => $this->dateFromValue($object['date']),
|
||||
'type' => $this->stringFromValue($object['type']),
|
||||
'date' => $this->dateFromValue($object['date']),
|
||||
'order' => $this->integerFromValue((string)$object['order']),
|
||||
|
||||
'currency_id' => $this->integerFromValue($object['currency_id']),
|
||||
'currency_code' => $this->stringFromValue($object['currency_code']),
|
||||
|
316
app/Api/V1/Requests/TransactionUpdateRequest.php
Normal file
316
app/Api/V1/Requests/TransactionUpdateRequest.php
Normal file
@ -0,0 +1,316 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* TransactionUpdateRequest.php
|
||||
* Copyright (c) 2019 thegrumpydictator@gmail.com
|
||||
*
|
||||
* This file is part of Firefly III.
|
||||
*
|
||||
* 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
|
||||
* along with Firefly III. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\Api\V1\Requests;
|
||||
|
||||
use FireflyIII\Models\TransactionGroup;
|
||||
use FireflyIII\Rules\BelongsUser;
|
||||
use FireflyIII\Rules\IsBoolean;
|
||||
use FireflyIII\Rules\IsDateOrTime;
|
||||
use FireflyIII\Validation\TransactionValidation;
|
||||
use Illuminate\Validation\Validator;
|
||||
|
||||
|
||||
/**
|
||||
* Class TransactionUpdateRequest
|
||||
*/
|
||||
class TransactionUpdateRequest extends Request
|
||||
{
|
||||
use TransactionValidation;
|
||||
|
||||
/** @var array Array values. */
|
||||
private $arrayFields;
|
||||
/** @var array Boolean values. */
|
||||
private $booleanFields;
|
||||
/** @var array Fields that contain date values. */
|
||||
private $dateFields;
|
||||
/** @var array Fields that contain integer values. */
|
||||
private $integerFields;
|
||||
/** @var array Fields that contain string values. */
|
||||
private $stringFields;
|
||||
|
||||
/**
|
||||
* Authorize logged in users.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function authorize(): bool
|
||||
{
|
||||
// Only allow authenticated users
|
||||
return auth()->check();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all data. Is pretty complex because of all the ??-statements.
|
||||
*
|
||||
* @SuppressWarnings(PHPMD.CyclomaticComplexity)
|
||||
* @SuppressWarnings(PHPMD.NPathComplexity)
|
||||
* @return array
|
||||
*/
|
||||
public function getAll(): array
|
||||
{
|
||||
$this->integerFields = [
|
||||
'order',
|
||||
'currency_id',
|
||||
'foreign_currency_id',
|
||||
'source_id',
|
||||
'destination_id',
|
||||
'budget_id',
|
||||
'category_id',
|
||||
'bill_id',
|
||||
'recurrence_id',
|
||||
];
|
||||
|
||||
$this->dateFields = [
|
||||
'date',
|
||||
'interest_date',
|
||||
'book_date',
|
||||
'process_date',
|
||||
'due_date',
|
||||
'payment_date',
|
||||
'invoice_date',
|
||||
];
|
||||
|
||||
$this->stringFields = [
|
||||
'type',
|
||||
'currency_code',
|
||||
'foreign_currency_code',
|
||||
'amount',
|
||||
'foreign_amount',
|
||||
'description',
|
||||
'source_name',
|
||||
'destination_name',
|
||||
'budget_name',
|
||||
'category_name',
|
||||
'bill_name',
|
||||
'notes',
|
||||
'internal_reference',
|
||||
'external_id',
|
||||
'bunq_payment_id',
|
||||
'sepa_cc',
|
||||
'sepa_ct_op',
|
||||
'sepa_ct_id',
|
||||
'sepa_db',
|
||||
'sepa_country',
|
||||
'sepa_ep',
|
||||
'sepa_ci',
|
||||
'sepa_batch_id',
|
||||
];
|
||||
$this->booleanFields = [
|
||||
'reconciled',
|
||||
];
|
||||
|
||||
$this->arrayFields = [
|
||||
'tags',
|
||||
];
|
||||
|
||||
|
||||
$data = [
|
||||
'transactions' => $this->getTransactionData(),
|
||||
];
|
||||
if ($this->has('group_title')) {
|
||||
$data['group_title'] = $this->string('group_title');
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* The rules that the incoming request must be matched against.
|
||||
*
|
||||
* @return array
|
||||
* @SuppressWarnings(PHPMD.ExcessiveMethodLength)
|
||||
*/
|
||||
public function rules(): array
|
||||
{
|
||||
$rules = [
|
||||
// basic fields for group:
|
||||
'group_title' => 'between:1,255',
|
||||
|
||||
// transaction rules (in array for splits):
|
||||
'transactions.*.type' => 'in:withdrawal,deposit,transfer,opening-balance,reconciliation',
|
||||
'transactions.*.date' => [new IsDateOrTime],
|
||||
'transactions.*.order' => 'numeric|min:0',
|
||||
|
||||
// currency info
|
||||
'transactions.*.currency_id' => 'numeric|exists:transaction_currencies,id',
|
||||
'transactions.*.currency_code' => 'min:3|max:3|exists:transaction_currencies,code',
|
||||
'transactions.*.foreign_currency_id' => 'numeric|exists:transaction_currencies,id',
|
||||
'transactions.*.foreign_currency_code' => 'min:3|max:3|exists:transaction_currencies,code',
|
||||
|
||||
// amount
|
||||
'transactions.*.amount' => 'numeric|more:0',
|
||||
'transactions.*.foreign_amount' => 'numeric|gte:0',
|
||||
|
||||
// description
|
||||
'transactions.*.description' => 'nullable|between:1,255',
|
||||
|
||||
// source of transaction
|
||||
'transactions.*.source_id' => ['numeric', 'nullable', new BelongsUser],
|
||||
'transactions.*.source_name' => 'between:1,255|nullable',
|
||||
|
||||
// destination of transaction
|
||||
'transactions.*.destination_id' => ['numeric', 'nullable', new BelongsUser],
|
||||
'transactions.*.destination_name' => 'between:1,255|nullable',
|
||||
|
||||
// budget, category, bill and piggy
|
||||
'transactions.*.budget_id' => ['mustExist:budgets,id', new BelongsUser],
|
||||
'transactions.*.budget_name' => ['between:1,255', 'nullable', new BelongsUser],
|
||||
'transactions.*.category_id' => ['mustExist:categories,id', new BelongsUser],
|
||||
'transactions.*.category_name' => 'between:1,255|nullable',
|
||||
'transactions.*.bill_id' => ['numeric', 'nullable', 'mustExist:bills,id', new BelongsUser],
|
||||
'transactions.*.bill_name' => ['between:1,255', 'nullable', new BelongsUser],
|
||||
|
||||
// other interesting fields
|
||||
'transactions.*.reconciled' => [new IsBoolean],
|
||||
'transactions.*.notes' => 'min:1,max:50000|nullable',
|
||||
'transactions.*.tags' => 'between:0,255',
|
||||
|
||||
// meta info fields
|
||||
'transactions.*.internal_reference' => 'min:1,max:255|nullable',
|
||||
'transactions.*.external_id' => 'min:1,max:255|nullable',
|
||||
'transactions.*.recurrence_id' => 'min:1,max:255|nullable',
|
||||
'transactions.*.bunq_payment_id' => 'min:1,max:255|nullable',
|
||||
|
||||
// SEPA fields:
|
||||
'transactions.*.sepa_cc' => 'min:1,max:255|nullable',
|
||||
'transactions.*.sepa_ct_op' => 'min:1,max:255|nullable',
|
||||
'transactions.*.sepa_ct_id' => 'min:1,max:255|nullable',
|
||||
'transactions.*.sepa_db' => 'min:1,max:255|nullable',
|
||||
'transactions.*.sepa_country' => 'min:1,max:255|nullable',
|
||||
'transactions.*.sepa_ep' => 'min:1,max:255|nullable',
|
||||
'transactions.*.sepa_ci' => 'min:1,max:255|nullable',
|
||||
'transactions.*.sepa_batch_id' => 'min:1,max:255|nullable',
|
||||
|
||||
// dates
|
||||
'transactions.*.interest_date' => 'date|nullable',
|
||||
'transactions.*.book_date' => 'date|nullable',
|
||||
'transactions.*.process_date' => 'date|nullable',
|
||||
'transactions.*.due_date' => 'date|nullable',
|
||||
'transactions.*.payment_date' => 'date|nullable',
|
||||
'transactions.*.invoice_date' => 'date|nullable',
|
||||
];
|
||||
|
||||
return $rules;
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure the validator instance.
|
||||
*
|
||||
* @param Validator $validator
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function withValidator(Validator $validator): void
|
||||
{
|
||||
/** @var TransactionGroup $transactionGroup */
|
||||
$transactionGroup = $this->route()->parameter('transactionGroup');
|
||||
$validator->after(
|
||||
function (Validator $validator) use ($transactionGroup) {
|
||||
// must submit at least one transaction.
|
||||
$this->validateOneTransaction($validator);
|
||||
|
||||
// if more than one, verify that there are journal ID's present.
|
||||
$this->validateJournalIds($validator, $transactionGroup);
|
||||
|
||||
// all transaction types must be equal:
|
||||
$this->validateTransactionTypesForUpdate($validator);
|
||||
|
||||
// if type is set, source + destination info is mandatory.
|
||||
$this->validateAccountPresence($validator);
|
||||
|
||||
// TODO validate that the currency fits the source and/or destination account.
|
||||
|
||||
// all journals must have a description
|
||||
//$this->validateDescriptions($validator);
|
||||
|
||||
// // validate foreign currency info
|
||||
// $this->validateForeignCurrencyInformation($validator);
|
||||
//
|
||||
//
|
||||
// // validate all account info
|
||||
// $this->validateAccountInformation($validator);
|
||||
//
|
||||
// // make sure all splits have valid source + dest info
|
||||
// $this->validateSplitAccounts($validator);
|
||||
// the group must have a description if > 1 journal.
|
||||
// $this->validateGroupDescription($validator);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get transaction data.
|
||||
*
|
||||
* @SuppressWarnings(PHPMD.CyclomaticComplexity)
|
||||
* @SuppressWarnings(PHPMD.NPathComplexity)
|
||||
* @return array
|
||||
*/
|
||||
private function getTransactionData(): array
|
||||
{
|
||||
$return = [];
|
||||
/**
|
||||
* @var int $index
|
||||
* @var array $transaction
|
||||
*/
|
||||
foreach ($this->get('transactions') as $index => $transaction) {
|
||||
// default response is to update nothing in the transaction:
|
||||
$current = [];
|
||||
|
||||
// for each field, add it to the array if a reference is present in the request:
|
||||
foreach ($this->integerFields as $fieldName) {
|
||||
if (array_key_exists($fieldName, $transaction)) {
|
||||
$current[$fieldName] = $this->integerFromValue((string)$transaction[$fieldName]);
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($this->stringFields as $fieldName) {
|
||||
if (array_key_exists($fieldName, $transaction)) {
|
||||
$current[$fieldName] = $this->stringFromValue((string)$transaction[$fieldName]);
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($this->dateFields as $fieldName) {
|
||||
if (array_key_exists($fieldName, $transaction)) {
|
||||
$current[$fieldName] = $this->dateFromValue((string)$transaction[$fieldName]);
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($this->booleanFields as $fieldName) {
|
||||
if (array_key_exists($fieldName, $transaction)) {
|
||||
$current[$fieldName] = $this->convertBoolean((string)$transaction[$fieldName]);
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($this->arrayFields as $fieldName) {
|
||||
if (array_key_exists($fieldName, $transaction)) {
|
||||
$current[$fieldName] = $this->arrayFromValue((string)$transaction[$fieldName]);
|
||||
}
|
||||
}
|
||||
$return[] = $current;
|
||||
}
|
||||
|
||||
return $return;
|
||||
}
|
||||
}
|
@ -68,6 +68,7 @@ class CorrectDatabase extends Command
|
||||
'firefly-iii:delete-empty-journals',
|
||||
'firefly-iii:delete-empty-groups',
|
||||
'firefly-iii:fix-account-types',
|
||||
'firefly-iii:rename-meta-fields'
|
||||
];
|
||||
foreach ($commands as $command) {
|
||||
$this->line(sprintf('Now executing %s', $command));
|
||||
|
@ -23,7 +23,7 @@ namespace FireflyIII\Console\Commands\Upgrade;
|
||||
|
||||
use DB;
|
||||
use Exception;
|
||||
use FireflyIII\Factory\TransactionJournalFactory;
|
||||
use FireflyIII\Factory\TransactionGroupFactory;
|
||||
use FireflyIII\Models\Transaction;
|
||||
use FireflyIII\Models\TransactionJournal;
|
||||
use FireflyIII\Repositories\Journal\JournalRepositoryInterface;
|
||||
@ -54,13 +54,10 @@ class MigrateToGroups extends Command
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'firefly-iii:migrate-to-groups {--F|force : Force the migration, even if it fired before.}';
|
||||
|
||||
/** @var TransactionJournalFactory */
|
||||
private $journalFactory;
|
||||
|
||||
/** @var TransactionGroupFactory */
|
||||
private $groupFactory;
|
||||
/** @var JournalRepositoryInterface */
|
||||
private $journalRepository;
|
||||
|
||||
/** @var JournalDestroyService */
|
||||
private $service;
|
||||
|
||||
@ -72,9 +69,9 @@ class MigrateToGroups extends Command
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
$this->journalFactory = app(TransactionJournalFactory::class);
|
||||
$this->journalRepository = app(JournalRepositoryInterface::class);
|
||||
$this->service = app(JournalDestroyService::class);
|
||||
$this->groupFactory = app(TransactionGroupFactory::class);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -119,7 +116,7 @@ class MigrateToGroups extends Command
|
||||
private function findOpposingTransaction(TransactionJournal $journal, Transaction $transaction): ?Transaction
|
||||
{
|
||||
$set = $journal->transactions->filter(
|
||||
function (Transaction $subject) use ($transaction) {
|
||||
static function (Transaction $subject) use ($transaction) {
|
||||
return $transaction->amount * -1 === (float)$subject->amount && $transaction->identifier === $subject->identifier;
|
||||
}
|
||||
);
|
||||
@ -135,7 +132,7 @@ class MigrateToGroups extends Command
|
||||
private function getDestinationTransactions(TransactionJournal $journal): Collection
|
||||
{
|
||||
return $journal->transactions->filter(
|
||||
function (Transaction $transaction) {
|
||||
static function (Transaction $transaction) {
|
||||
return $transaction->amount > 0;
|
||||
}
|
||||
);
|
||||
@ -224,13 +221,10 @@ class MigrateToGroups extends Command
|
||||
Log::debug(sprintf('Will now try to convert journal #%d', $journal->id));
|
||||
|
||||
$this->journalRepository->setUser($journal->user);
|
||||
$this->journalFactory->setUser($journal->user);
|
||||
$this->groupFactory->setUser($journal->user);
|
||||
|
||||
$data = [
|
||||
// mandatory fields.
|
||||
'type' => strtolower($journal->transactionType->type),
|
||||
'date' => $journal->date,
|
||||
'user' => $journal->user_id,
|
||||
'group_title' => $journal->description,
|
||||
'transactions' => [],
|
||||
];
|
||||
@ -280,6 +274,9 @@ class MigrateToGroups extends Command
|
||||
}
|
||||
|
||||
$tArray = [
|
||||
'type' => strtolower($journal->transactionType->type),
|
||||
'date' => $journal->date,
|
||||
'user' => $journal->user_id,
|
||||
'currency_id' => $transaction->transaction_currency_id,
|
||||
'foreign_currency_id' => $transaction->foreign_currency_id,
|
||||
'amount' => $transaction->amount,
|
||||
@ -318,21 +315,18 @@ class MigrateToGroups extends Command
|
||||
$data['transactions'][] = $tArray;
|
||||
}
|
||||
Log::debug(sprintf('Now calling transaction journal factory (%d transactions in array)', count($data['transactions'])));
|
||||
$result = $this->journalFactory->create($data);
|
||||
$group = $this->groupFactory->create($data);
|
||||
Log::debug('Done calling transaction journal factory');
|
||||
|
||||
// delete the old transaction journal.
|
||||
$this->service->destroy($journal);
|
||||
|
||||
// first group ID
|
||||
$first = $result->first() ? $result->first()->transaction_group_id : 0;
|
||||
|
||||
// report on result:
|
||||
Log::debug(
|
||||
sprintf('Migrated journal #%d into group #%d with these journals: #%s', $journal->id, $first, implode(', #', $result->pluck('id')->toArray()))
|
||||
sprintf('Migrated journal #%d into group #%d with these journals: #%s', $journal->id, $group->id, implode(', #', $group->transactionJournals->pluck('id')->toArray()))
|
||||
);
|
||||
$this->line(
|
||||
sprintf('Migrated journal #%d into group #%d with these journals: #%s', $journal->id, $first, implode(', #', $result->pluck('id')->toArray()))
|
||||
sprintf('Migrated journal #%d into group #%d with these journals: #%s', $journal->id, $group->id, implode(', #', $group->transactionJournals->pluck('id')->toArray()))
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -27,11 +27,11 @@ namespace FireflyIII\Factory;
|
||||
|
||||
use FireflyIII\Exceptions\FireflyException;
|
||||
use FireflyIII\Models\Account;
|
||||
use FireflyIII\Models\AccountType;
|
||||
use FireflyIII\Models\Transaction;
|
||||
use FireflyIII\Models\TransactionCurrency;
|
||||
use FireflyIII\Models\TransactionJournal;
|
||||
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
|
||||
use FireflyIII\Services\Internal\Support\JournalServiceTrait;
|
||||
use FireflyIII\Support\NullArrayObject;
|
||||
use FireflyIII\User;
|
||||
use FireflyIII\Validation\AccountValidator;
|
||||
@ -44,8 +44,8 @@ use Log;
|
||||
*/
|
||||
class TransactionFactory
|
||||
{
|
||||
/** @var AccountRepositoryInterface */
|
||||
private $accountRepository;
|
||||
use JournalServiceTrait;
|
||||
|
||||
/** @var AccountValidator */
|
||||
private $accountValidator;
|
||||
/** @var TransactionJournal */
|
||||
@ -113,12 +113,16 @@ class TransactionFactory
|
||||
*/
|
||||
public function createPair(NullArrayObject $data, TransactionCurrency $currency, ?TransactionCurrency $foreignCurrency): Collection
|
||||
{
|
||||
Log::debug('Going to create a pair of transactions.');
|
||||
Log::debug(sprintf('Source info: ID #%d, name "%s"', $data['source_id'], $data['source_name']));
|
||||
Log::debug(sprintf('Destination info: ID #%d, name "%s"', $data['destination_id'], $data['destination_name']));
|
||||
// validate source and destination using a new Validator.
|
||||
$this->validateAccounts($data);
|
||||
|
||||
// create or get source and destination accounts:
|
||||
$sourceAccount = $this->getAccount('source', (int)$data['source_id'], $data['source_name']);
|
||||
$destinationAccount = $this->getAccount('destination', (int)$data['destination_id'], $data['destination_name']);
|
||||
$type = $this->journal->transactionType->type;
|
||||
$sourceAccount = $this->getAccount($type, 'source', (int)$data['source_id'], $data['source_name']);
|
||||
$destinationAccount = $this->getAccount($type, 'destination', (int)$data['destination_id'], $data['destination_name']);
|
||||
|
||||
$amount = $this->getAmount($data['amount']);
|
||||
$foreignAmount = $this->getForeignAmount($data['foreign_amount']);
|
||||
@ -129,7 +133,7 @@ class TransactionFactory
|
||||
$two->reconciled = $data['reconciled'] ?? false;
|
||||
|
||||
// add foreign currency info to $one and $two if necessary.
|
||||
if (null !== $foreignCurrency) {
|
||||
if (null !== $foreignCurrency && null !== $foreignAmount) {
|
||||
$one->foreign_currency_id = $foreignCurrency->id;
|
||||
$two->foreign_currency_id = $foreignCurrency->id;
|
||||
$one->foreign_amount = app('steam')->negative($foreignAmount);
|
||||
@ -144,134 +148,6 @@ class TransactionFactory
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $direction
|
||||
* @param int|null $accountId
|
||||
* @param string|null $accountName
|
||||
*
|
||||
* @return Account
|
||||
* @throws FireflyException
|
||||
*/
|
||||
public function getAccount(string $direction, ?int $accountId, ?string $accountName): Account
|
||||
{
|
||||
// some debug logging:
|
||||
Log::debug(sprintf('Now in getAccount(%s, %d, %s)', $direction, $accountId, $accountName));
|
||||
|
||||
// final result:
|
||||
$result = null;
|
||||
|
||||
// expected type of source account, in order of preference
|
||||
/** @var array $array */
|
||||
$array = config('firefly.expected_source_types');
|
||||
$expectedTypes = $array[$direction];
|
||||
unset($array);
|
||||
|
||||
// and now try to find it, based on the type of transaction.
|
||||
$transactionType = $this->journal->transactionType->type;
|
||||
$message = 'Based on the fact that the transaction is a %s, the %s account should be in: %s';
|
||||
Log::debug(sprintf($message, $transactionType, $direction, implode(', ', $expectedTypes[$transactionType])));
|
||||
|
||||
// first attempt, find by ID.
|
||||
if (null !== $accountId) {
|
||||
$search = $this->accountRepository->findNull($accountId);
|
||||
if (null !== $search && in_array($search->accountType->type, $expectedTypes[$transactionType], true)) {
|
||||
Log::debug(
|
||||
sprintf('Found "account_id" object for %s: #%d, "%s" of type %s', $direction, $search->id, $search->name, $search->accountType->type)
|
||||
);
|
||||
$result = $search;
|
||||
}
|
||||
}
|
||||
|
||||
// second attempt, find by name.
|
||||
if (null === $result && null !== $accountName) {
|
||||
Log::debug('Found nothing by account ID.');
|
||||
// find by preferred type.
|
||||
$source = $this->accountRepository->findByName($accountName, [$expectedTypes[$transactionType][0]]);
|
||||
// or any expected type.
|
||||
$source = $source ?? $this->accountRepository->findByName($accountName, $expectedTypes[$transactionType]);
|
||||
|
||||
if (null !== $source) {
|
||||
Log::debug(sprintf('Found "account_name" object for %s: #%d, %s', $direction, $source->id, $source->name));
|
||||
|
||||
$result = $source;
|
||||
}
|
||||
}
|
||||
|
||||
// return cash account.
|
||||
if (null === $result && null === $accountName
|
||||
&& in_array(AccountType::CASH, $expectedTypes[$transactionType], true)) {
|
||||
$result = $this->accountRepository->getCashAccount();
|
||||
}
|
||||
|
||||
// return new account.
|
||||
if (null === $result) {
|
||||
$accountName = $accountName ?? '(no name)';
|
||||
// final attempt, create it.
|
||||
$preferredType = $expectedTypes[$transactionType][0];
|
||||
if (AccountType::ASSET === $preferredType) {
|
||||
throw new FireflyException(sprintf('TransactionFactory: Cannot create asset account with ID #%d or name "%s".', $accountId, $accountName));
|
||||
}
|
||||
|
||||
$result = $this->accountRepository->store(
|
||||
[
|
||||
'account_type_id' => null,
|
||||
'accountType' => $preferredType,
|
||||
'name' => $accountName,
|
||||
'active' => true,
|
||||
'iban' => null,
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $amount
|
||||
*
|
||||
* @return string
|
||||
* @throws FireflyException
|
||||
*/
|
||||
public function getAmount(string $amount): string
|
||||
{
|
||||
if ('' === $amount) {
|
||||
throw new FireflyException(sprintf('The amount cannot be an empty string: "%s"', $amount));
|
||||
}
|
||||
if (0 === bccomp('0', $amount)) {
|
||||
throw new FireflyException(sprintf('The amount seems to be zero: "%s"', $amount));
|
||||
}
|
||||
|
||||
return $amount;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string|null $amount
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getForeignAmount(?string $amount): ?string
|
||||
{
|
||||
$result = null;
|
||||
if (null === $amount) {
|
||||
Log::debug('No foreign amount info in array. Return NULL');
|
||||
|
||||
return null;
|
||||
}
|
||||
if ('' === $amount) {
|
||||
Log::debug('Foreign amount is empty string, return NULL.');
|
||||
|
||||
return null;
|
||||
}
|
||||
if (0 === bccomp('0', $amount)) {
|
||||
Log::debug('Foreign amount is 0.0, return NULL.');
|
||||
|
||||
return null;
|
||||
}
|
||||
Log::debug(sprintf('Foreign amount is %s', $amount));
|
||||
|
||||
return $amount;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param TransactionJournal $journal
|
||||
@ -298,6 +174,7 @@ class TransactionFactory
|
||||
private function validateAccounts(NullArrayObject $data): void
|
||||
{
|
||||
$transactionType = $data['type'] ?? 'invalid';
|
||||
$this->accountValidator->setUser($this->journal->user);
|
||||
$this->accountValidator->setTransactionType($transactionType);
|
||||
|
||||
// validate source account.
|
||||
@ -309,6 +186,7 @@ class TransactionFactory
|
||||
if (false === $validSource) {
|
||||
throw new FireflyException($this->accountValidator->sourceError);
|
||||
}
|
||||
Log::debug('Source seems valid.');
|
||||
// validate destination account
|
||||
$destinationId = isset($data['destination_id']) ? (int)$data['destination_id'] : null;
|
||||
$destinationName = $data['destination_name'] ?? null;
|
||||
|
@ -27,7 +27,6 @@ namespace FireflyIII\Factory;
|
||||
use Carbon\Carbon;
|
||||
use Exception;
|
||||
use FireflyIII\Exceptions\FireflyException;
|
||||
use FireflyIII\Models\Note;
|
||||
use FireflyIII\Models\TransactionCurrency;
|
||||
use FireflyIII\Models\TransactionJournal;
|
||||
use FireflyIII\Models\TransactionType;
|
||||
@ -37,6 +36,7 @@ use FireflyIII\Repositories\Category\CategoryRepositoryInterface;
|
||||
use FireflyIII\Repositories\Currency\CurrencyRepositoryInterface;
|
||||
use FireflyIII\Repositories\PiggyBank\PiggyBankRepositoryInterface;
|
||||
use FireflyIII\Repositories\TransactionType\TransactionTypeRepositoryInterface;
|
||||
use FireflyIII\Services\Internal\Support\JournalServiceTrait;
|
||||
use FireflyIII\Support\NullArrayObject;
|
||||
use FireflyIII\User;
|
||||
use Illuminate\Support\Collection;
|
||||
@ -47,12 +47,10 @@ use Log;
|
||||
*/
|
||||
class TransactionJournalFactory
|
||||
{
|
||||
use JournalServiceTrait;
|
||||
|
||||
/** @var BillRepositoryInterface */
|
||||
private $billRepository;
|
||||
/** @var BudgetRepositoryInterface */
|
||||
private $budgetRepository;
|
||||
/** @var CategoryRepositoryInterface */
|
||||
private $categoryRepository;
|
||||
/** @var CurrencyRepositoryInterface */
|
||||
private $currencyRepository;
|
||||
/** @var array */
|
||||
@ -61,8 +59,6 @@ class TransactionJournalFactory
|
||||
private $piggyEventFactory;
|
||||
/** @var PiggyBankRepositoryInterface */
|
||||
private $piggyRepository;
|
||||
/** @var TagFactory */
|
||||
private $tagFactory;
|
||||
/** @var TransactionFactory */
|
||||
private $transactionFactory;
|
||||
/** @var TransactionTypeRepositoryInterface */
|
||||
@ -88,7 +84,7 @@ class TransactionJournalFactory
|
||||
'due_date', 'payment_date', 'invoice_date',
|
||||
|
||||
// others
|
||||
'recurrence_id', 'internal_reference', 'bunq_payment_id',
|
||||
'recurrence_id', 'internal_reference', 'bunq_payment_id',
|
||||
'import_hash', 'import_hash_v2', 'external_id', 'original_source'];
|
||||
|
||||
|
||||
@ -151,6 +147,7 @@ class TransactionJournalFactory
|
||||
{
|
||||
$this->user = $user;
|
||||
$this->currencyRepository->setUser($this->user);
|
||||
$this->tagFactory->setUser($user);
|
||||
$this->transactionFactory->setUser($this->user);
|
||||
$this->billRepository->setUser($this->user);
|
||||
$this->budgetRepository->setUser($this->user);
|
||||
@ -184,31 +181,6 @@ class TransactionJournalFactory
|
||||
Log::debug('Create no piggy event');
|
||||
}
|
||||
|
||||
/**
|
||||
* Link tags to journal.
|
||||
*
|
||||
* @param TransactionJournal $journal
|
||||
* @param array $tags
|
||||
* @SuppressWarnings(PHPMD.CyclomaticComplexity)
|
||||
*/
|
||||
public function storeTags(TransactionJournal $journal, ?array $tags): void
|
||||
{
|
||||
$this->tagFactory->setUser($journal->user);
|
||||
$set = [];
|
||||
if (!\is_array($tags)) {
|
||||
return;
|
||||
}
|
||||
foreach ($tags as $string) {
|
||||
if ('' !== $string) {
|
||||
$tag = $this->tagFactory->findOrCreate($string);
|
||||
if (null !== $tag) {
|
||||
$set[] = $tag->id;
|
||||
}
|
||||
}
|
||||
}
|
||||
$journal->tags()->sync($set);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param TransactionJournal $journal
|
||||
* @param NullArrayObject $data
|
||||
@ -229,27 +201,6 @@ class TransactionJournalFactory
|
||||
$factory->updateOrCreate($set);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param TransactionJournal $journal
|
||||
* @param string $notes
|
||||
*/
|
||||
protected function storeNote(TransactionJournal $journal, ?string $notes): void
|
||||
{
|
||||
$notes = (string)$notes;
|
||||
if ('' !== $notes) {
|
||||
$note = $journal->notes()->first();
|
||||
if (null === $note) {
|
||||
$note = new Note;
|
||||
$note->noteable()->associate($journal);
|
||||
}
|
||||
$note->text = $notes;
|
||||
$note->save();
|
||||
Log::debug(sprintf('Stored notes for journal #%d', $journal->id));
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param NullArrayObject $row
|
||||
*
|
||||
@ -263,8 +214,9 @@ class TransactionJournalFactory
|
||||
/** Get basic fields */
|
||||
$type = $this->typeRepository->findTransactionType(null, $row['type']);
|
||||
$carbon = $row['date'] ?? new Carbon;
|
||||
$order = $row['order'] ?? 0;
|
||||
$currency = $this->currencyRepository->findCurrency((int)$row['currency_id'], $row['currency_code']);
|
||||
$foreignCurrency = $this->findForeignCurrency($row);
|
||||
$foreignCurrency = $this->currencyRepository->findCurrencyNull($row['foreign_currency_id'], $row['foreign_currency_code']);
|
||||
$bill = $this->billRepository->findBill((int)$row['bill_id'], $row['bill_name']);
|
||||
$billId = TransactionType::WITHDRAWAL === $type->type && null !== $bill ? $bill->id : null;
|
||||
$description = app('steam')->cleanString((string)$row['description']);
|
||||
@ -281,7 +233,7 @@ class TransactionJournalFactory
|
||||
'transaction_currency_id' => $currency->id,
|
||||
'description' => '' === $description ? '(empty description)' : $description,
|
||||
'date' => $carbon->format('Y-m-d H:i:s'),
|
||||
'order' => 0,
|
||||
'order' => $order,
|
||||
'tag_count' => 0,
|
||||
'completed' => 0,
|
||||
]
|
||||
@ -318,7 +270,7 @@ class TransactionJournalFactory
|
||||
$this->storeCategory($journal, $row);
|
||||
|
||||
/** Set notes */
|
||||
$this->storeNote($journal, $row['notes']);
|
||||
$this->storeNotes($journal, $row['notes']);
|
||||
|
||||
/** Set piggy bank */
|
||||
$this->storePiggyEvent($journal, $row);
|
||||
@ -332,22 +284,6 @@ class TransactionJournalFactory
|
||||
return $journal;
|
||||
}
|
||||
|
||||
/**
|
||||
* This is a separate function because "findCurrency" will default to EUR and that may not be what we want.
|
||||
*
|
||||
* @param NullArrayObject $transaction
|
||||
*
|
||||
* @return TransactionCurrency|null
|
||||
*/
|
||||
private function findForeignCurrency(NullArrayObject $transaction): ?TransactionCurrency
|
||||
{
|
||||
if (null === $transaction['foreign_currency'] && null === $transaction['foreign_currency_id'] && null === $transaction['foreign_currency_code']) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $this->currencyRepository->findCurrency((int)$transaction['foreign_currency_id'], $transaction['foreign_currency_code']);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param NullArrayObject $row
|
||||
*
|
||||
@ -355,7 +291,7 @@ class TransactionJournalFactory
|
||||
*/
|
||||
private function hashArray(NullArrayObject $row): string
|
||||
{
|
||||
$row['import_hash_v2'] = null;
|
||||
$row['import_hash_v2'] = null;
|
||||
$row['original_source'] = null;
|
||||
$json = json_encode($row);
|
||||
if (false === $json) {
|
||||
@ -367,36 +303,6 @@ class TransactionJournalFactory
|
||||
return $hash;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param TransactionJournal $journal
|
||||
* @param NullArrayObject $data
|
||||
*/
|
||||
private function storeBudget(TransactionJournal $journal, NullArrayObject $data): void
|
||||
{
|
||||
if (TransactionType::WITHDRAWAL !== $journal->transactionType->type) {
|
||||
return;
|
||||
}
|
||||
$budget = $this->budgetRepository->findBudget($data['budget'], $data['budget_id'], $data['budget_name']);
|
||||
if (null !== $budget) {
|
||||
Log::debug(sprintf('Link budget #%d to journal #%d', $budget->id, $journal->id));
|
||||
$journal->budgets()->sync([$budget->id]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param TransactionJournal $journal
|
||||
* @param NullArrayObject $data
|
||||
*/
|
||||
private function storeCategory(TransactionJournal $journal, NullArrayObject $data): void
|
||||
{
|
||||
$category = $this->categoryRepository->findCategory($data['category'], $data['category_id'], $data['category_name']);
|
||||
if (null !== $category) {
|
||||
Log::debug(sprintf('Link category #%d to journal #%d', $category->id, $journal->id));
|
||||
$journal->categories()->sync([$category->id]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param TransactionJournal $journal
|
||||
* @param NullArrayObject $transaction
|
||||
|
@ -53,10 +53,12 @@ class TransactionJournalMetaFactory
|
||||
*/
|
||||
public function updateOrCreate(array $data): ?TransactionJournalMeta
|
||||
{
|
||||
Log::debug('In updateOrCreate()');
|
||||
$value = $data['data'];
|
||||
/** @var TransactionJournalMeta $entry */
|
||||
$entry = $data['journal']->transactionJournalMeta()->where('name', $data['name'])->first();
|
||||
if (null === $value && null !== $entry) {
|
||||
Log::debug('Value is empty, delete meta value.');
|
||||
try {
|
||||
$entry->delete();
|
||||
} catch (Exception $e) { // @codeCoverageIgnore
|
||||
@ -67,11 +69,14 @@ class TransactionJournalMetaFactory
|
||||
}
|
||||
|
||||
if ($data['data'] instanceof Carbon) {
|
||||
Log::debug('Is a carbon object.');
|
||||
$value = $data['data']->toW3cString();
|
||||
}
|
||||
if ('' === (string)$value) {
|
||||
Log::debug('Is an empty string.');
|
||||
// don't store blank strings.
|
||||
if (null !== $entry) {
|
||||
Log::debug('Will not store empty strings, delete meta value');
|
||||
try {
|
||||
$entry->delete();
|
||||
} catch (Exception $e) { // @codeCoverageIgnore
|
||||
@ -83,11 +88,13 @@ class TransactionJournalMetaFactory
|
||||
}
|
||||
|
||||
if (null === $entry) {
|
||||
Log::debug('Will create new object.');
|
||||
Log::debug(sprintf('Going to create new meta-data entry to store "%s".', $data['name']));
|
||||
$entry = new TransactionJournalMeta();
|
||||
$entry->transactionJournal()->associate($data['journal']);
|
||||
$entry->name = $data['name'];
|
||||
}
|
||||
Log::debug('Will update value and return.');
|
||||
$entry->data = $value;
|
||||
$entry->save();
|
||||
|
||||
|
@ -44,7 +44,7 @@ class UpdatedGroupEventHandler
|
||||
public function processRules(UpdatedTransactionGroup $updatedJournalEvent): bool
|
||||
{
|
||||
// get all the user's rule groups, with the rules, order by 'order'.
|
||||
$journals = $updatedJournalEvent->transactionGroup;
|
||||
$journals = $updatedJournalEvent->transactionGroup->transactionJournals;
|
||||
|
||||
/** @var RuleGroupRepositoryInterface $ruleGroupRepos */
|
||||
$ruleGroupRepos = app(RuleGroupRepositoryInterface::class);
|
||||
|
@ -99,6 +99,7 @@ class GroupCollector implements GroupCollectorInterface
|
||||
'transaction_types.type as transaction_type_type',
|
||||
'transaction_journals.description',
|
||||
'transaction_journals.date',
|
||||
'transaction_journals.order',
|
||||
|
||||
# source info (always present)
|
||||
'source.id as source_transaction_id',
|
||||
|
@ -82,7 +82,7 @@ class InstallController extends Controller
|
||||
'firefly-iii:migrate-to-groups' => [],
|
||||
'firefly-iii:back-to-journals' => [],
|
||||
|
||||
// there are 12 verify commands.
|
||||
// there are 13 verify commands.
|
||||
'firefly-iii:fix-piggies' => [],
|
||||
'firefly-iii:create-link-types' => [],
|
||||
'firefly-iii:create-access-tokens' => [],
|
||||
@ -95,8 +95,10 @@ class InstallController extends Controller
|
||||
'firefly-iii:delete-empty-journals' => [],
|
||||
'firefly-iii:delete-empty-groups' => [],
|
||||
'firefly-iii:fix-account-types' => [],
|
||||
'firefly-iii:rename-meta-fields' => [],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Show index.
|
||||
*
|
||||
|
@ -170,6 +170,9 @@ class Request extends FormRequest
|
||||
if (null === $string) {
|
||||
return null;
|
||||
}
|
||||
if ('' === $string) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (int)$string;
|
||||
}
|
||||
|
@ -134,9 +134,8 @@ class TransactionJournal extends Model
|
||||
|
||||
/** @var array Fields that can be filled */
|
||||
protected $fillable
|
||||
= ['user_id', 'transaction_type_id', 'bill_id', 'interest_date', 'book_date', 'process_date',
|
||||
'transaction_currency_id', 'description', 'completed',
|
||||
'date', 'rent_date', 'encrypted', 'tag_count',];
|
||||
= ['user_id', 'transaction_type_id', 'bill_id', 'tag_count','transaction_currency_id', 'description', 'completed', 'order',
|
||||
'date'];
|
||||
/** @var array Hidden from view */
|
||||
protected $hidden = ['encrypted'];
|
||||
|
||||
|
@ -97,8 +97,18 @@ class JournalServiceProvider extends ServiceProvider
|
||||
|
||||
private function registerGroupRepository()
|
||||
{
|
||||
// password verifier thing
|
||||
$this->app->bind(TransactionGroupRepositoryInterface::class, TransactionGroupRepository::class);
|
||||
$this->app->bind(
|
||||
TransactionGroupRepositoryInterface::class,
|
||||
function (Application $app) {
|
||||
/** @var TransactionGroupRepositoryInterface $repository */
|
||||
$repository = app(TransactionGroupRepository::class);
|
||||
if ($app->auth->check()) {
|
||||
$repository->setUser(auth()->user());
|
||||
}
|
||||
|
||||
return $repository;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -220,25 +220,16 @@ class BudgetRepository implements BudgetRepositoryInterface
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Budget|null $budget
|
||||
* @param int|null $budgetId
|
||||
* @param string|null $budgetName
|
||||
*
|
||||
* @return Budget|null
|
||||
*/
|
||||
public function findBudget(?Budget $budget, ?int $budgetId, ?string $budgetName): ?Budget
|
||||
public function findBudget(?int $budgetId, ?string $budgetName): ?Budget
|
||||
{
|
||||
Log::debug('Now in findBudget()');
|
||||
$result = null;
|
||||
if (null !== $budget) {
|
||||
Log::debug(sprintf('Parameters contain budget #%d, will return this.', $budget->id));
|
||||
$result = $budget;
|
||||
}
|
||||
|
||||
if (null === $result) {
|
||||
Log::debug(sprintf('Searching for budget with ID #%d...', $budgetId));
|
||||
$result = $this->findNull((int)$budgetId);
|
||||
}
|
||||
Log::debug(sprintf('Searching for budget with ID #%d...', $budgetId));
|
||||
$result = $this->findNull((int)$budgetId);
|
||||
if (null === $result) {
|
||||
Log::debug(sprintf('Searching for budget with name %s...', $budgetName));
|
||||
$result = $this->findByName((string)$budgetName);
|
||||
|
@ -82,13 +82,12 @@ interface BudgetRepositoryInterface
|
||||
public function destroyBudgetLimit(BudgetLimit $budgetLimit): void;
|
||||
|
||||
/**
|
||||
* @param Budget|null $budget
|
||||
* @param int|null $budgetId
|
||||
* @param string|null $budgetName
|
||||
*
|
||||
* @return Budget|null
|
||||
*/
|
||||
public function findBudget(?Budget $budget, ?int $budgetId, ?string $budgetName): ?Budget;
|
||||
public function findBudget( ?int $budgetId, ?string $budgetName): ?Budget;
|
||||
|
||||
/**
|
||||
* Find budget by name.
|
||||
|
@ -253,25 +253,16 @@ class CategoryRepository implements CategoryRepositoryInterface
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Category|null $category
|
||||
* @param int|null $categoryId
|
||||
* @param string|null $categoryName
|
||||
* @param int|null $categoryId
|
||||
* @param string|null $categoryName
|
||||
*
|
||||
* @return Category|null
|
||||
*/
|
||||
public function findCategory(?Category $category, ?int $categoryId, ?string $categoryName): ?Category
|
||||
public function findCategory(?int $categoryId, ?string $categoryName): ?Category
|
||||
{
|
||||
Log::debug('Now in findCategory()');
|
||||
$result = null;
|
||||
if (null !== $category) {
|
||||
Log::debug(sprintf('Parameters contain category #%d, will return this.', $category->id));
|
||||
$result = $category;
|
||||
}
|
||||
|
||||
if (null === $result) {
|
||||
Log::debug(sprintf('Searching for category with ID #%d...', $categoryId));
|
||||
$result = $this->findNull((int)$categoryId);
|
||||
}
|
||||
Log::debug(sprintf('Searching for category with ID #%d...', $categoryId));
|
||||
$result = $this->findNull((int)$categoryId);
|
||||
if (null === $result) {
|
||||
Log::debug(sprintf('Searching for category with name %s...', $categoryName));
|
||||
$result = $this->findByName((string)$categoryName);
|
||||
|
@ -34,13 +34,12 @@ interface CategoryRepositoryInterface
|
||||
{
|
||||
|
||||
/**
|
||||
* @param Category|null $category
|
||||
* @param int|null $categoryId
|
||||
* @param string|null $categoryName
|
||||
*
|
||||
* @return Category|null
|
||||
*/
|
||||
public function findCategory(?Category $category, ?int $categoryId, ?string $categoryName): ?Category;
|
||||
public function findCategory( ?int $categoryId, ?string $categoryName): ?Category;
|
||||
|
||||
/**
|
||||
* @param Category $category
|
||||
|
@ -265,16 +265,13 @@ class CurrencyRepository implements CurrencyRepositoryInterface
|
||||
*/
|
||||
public function findCurrency(?int $currencyId, ?string $currencyCode): TransactionCurrency
|
||||
{
|
||||
Log::debug('Now in findCurrency()');
|
||||
$result = $this->find((int)$currencyId);
|
||||
if (null === $result) {
|
||||
Log::debug(sprintf('Searching for currency with code %s...', $currencyCode));
|
||||
$result = $this->findByCode((string)$currencyCode);
|
||||
}
|
||||
$result = $this->findCurrencyNull($currencyId, $currencyCode);
|
||||
|
||||
if (null === $result) {
|
||||
Log::debug('Grabbing default currency for this user...');
|
||||
$result = app('amount')->getDefaultCurrencyByUser($this->user);
|
||||
}
|
||||
|
||||
if (null === $result) {
|
||||
Log::debug('Grabbing EUR as fallback.');
|
||||
$result = $this->findByCode('EUR');
|
||||
@ -288,6 +285,30 @@ class CurrencyRepository implements CurrencyRepositoryInterface
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find by object, ID or code. Returns NULL if nothing found.
|
||||
*
|
||||
* @param int|null $currencyId
|
||||
* @param string|null $currencyCode
|
||||
*
|
||||
* @return TransactionCurrency|null
|
||||
*/
|
||||
public function findCurrencyNull(?int $currencyId, ?string $currencyCode): ?TransactionCurrency
|
||||
{
|
||||
Log::debug('Now in findCurrencyNull()');
|
||||
$result = $this->find((int)$currencyId);
|
||||
if (null === $result) {
|
||||
Log::debug(sprintf('Searching for currency with code %s...', $currencyCode));
|
||||
$result = $this->findByCode((string)$currencyCode);
|
||||
}
|
||||
if (null !== $result && false === $result->enabled) {
|
||||
Log::debug(sprintf('Also enabled currency %s', $result->code));
|
||||
$this->enable($result);
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find by ID, return NULL if not found.
|
||||
* Used in Import Currency!
|
||||
|
@ -135,13 +135,23 @@ interface CurrencyRepositoryInterface
|
||||
/**
|
||||
* Find by object, ID or code. Returns user default or system default.
|
||||
*
|
||||
* @param int|null $currencyId
|
||||
* @param string|null $currencyCode
|
||||
* @param int|null $currencyId
|
||||
* @param string|null $currencyCode
|
||||
*
|
||||
* @return TransactionCurrency|null
|
||||
*/
|
||||
public function findCurrency(?int $currencyId, ?string $currencyCode): TransactionCurrency;
|
||||
|
||||
/**
|
||||
* Find by object, ID or code. Returns NULL if nothing found.
|
||||
*
|
||||
* @param int|null $currencyId
|
||||
* @param string|null $currencyCode
|
||||
*
|
||||
* @return TransactionCurrency|null
|
||||
*/
|
||||
public function findCurrencyNull(?int $currencyId, ?string $currencyCode): ?TransactionCurrency;
|
||||
|
||||
/**
|
||||
* Find by ID, return NULL if not found.
|
||||
*
|
||||
|
@ -774,40 +774,6 @@ class JournalRepository implements JournalRepositoryInterface
|
||||
$this->user = $user;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $data
|
||||
*
|
||||
* @return TransactionGroup
|
||||
*
|
||||
* @throws FireflyException
|
||||
*/
|
||||
public function store(array $data): TransactionGroup
|
||||
{
|
||||
/** @var TransactionGroupFactory $factory */
|
||||
$factory = app(TransactionGroupFactory::class);
|
||||
$factory->setUser($this->user);
|
||||
|
||||
return $factory->create($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param TransactionGroup $journal
|
||||
* @param array $data
|
||||
*
|
||||
* @return TransactionGroup
|
||||
*
|
||||
* @throws FireflyException
|
||||
* @throws FireflyException
|
||||
*/
|
||||
public function update(TransactionGroup $journal, array $data): TransactionGroup
|
||||
{
|
||||
/** @var JournalUpdateService $service */
|
||||
$service = app(JournalUpdateService::class);
|
||||
$journal = $service->update($journal, $data);
|
||||
|
||||
return $journal;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update budget for a journal.
|
||||
*
|
||||
|
@ -319,21 +319,7 @@ interface JournalRepositoryInterface
|
||||
*/
|
||||
public function setUser(User $user);
|
||||
|
||||
/**
|
||||
* @param array $data
|
||||
*
|
||||
* @throws FireflyException
|
||||
* @return TransactionJournal
|
||||
*/
|
||||
public function store(array $data): TransactionGroup;
|
||||
|
||||
/**
|
||||
* @param TransactionGroup $transactionGroup
|
||||
* @param array $data
|
||||
*
|
||||
* @return TransactionGroup
|
||||
*/
|
||||
public function update(TransactionGroup $transactionGroup, array $data): TransactionGroup;
|
||||
|
||||
/**
|
||||
* Update budget for a journal.
|
||||
|
@ -27,8 +27,12 @@ namespace FireflyIII\Repositories\TransactionGroup;
|
||||
use Carbon\Carbon;
|
||||
use DB;
|
||||
use Exception;
|
||||
use FireflyIII\Exceptions\FireflyException;
|
||||
use FireflyIII\Factory\TransactionGroupFactory;
|
||||
use FireflyIII\Models\Note;
|
||||
use FireflyIII\Models\TransactionGroup;
|
||||
use FireflyIII\Models\TransactionJournal;
|
||||
use FireflyIII\Services\Internal\Update\GroupUpdateService;
|
||||
use FireflyIII\Support\NullArrayObject;
|
||||
|
||||
/**
|
||||
@ -36,6 +40,8 @@ use FireflyIII\Support\NullArrayObject;
|
||||
*/
|
||||
class TransactionGroupRepository implements TransactionGroupRepositoryInterface
|
||||
{
|
||||
private $user;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*/
|
||||
@ -61,6 +67,7 @@ class TransactionGroupRepository implements TransactionGroupRepositoryInterface
|
||||
::table('journal_meta')
|
||||
->where('transaction_journal_id', $journalId)
|
||||
->whereIn('name', $fields)
|
||||
->whereNull('deleted_at')
|
||||
->get(['name', 'data']);
|
||||
$return = [];
|
||||
|
||||
@ -85,6 +92,7 @@ class TransactionGroupRepository implements TransactionGroupRepositoryInterface
|
||||
::table('journal_meta')
|
||||
->where('transaction_journal_id', $journalId)
|
||||
->whereIn('name', $fields)
|
||||
->whereNull('deleted_at')
|
||||
->get(['name', 'data']);
|
||||
$return = [];
|
||||
|
||||
@ -133,4 +141,45 @@ class TransactionGroupRepository implements TransactionGroupRepositoryInterface
|
||||
|
||||
return $result->pluck('tag')->toArray();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $user
|
||||
*/
|
||||
public function setUser($user): void
|
||||
{
|
||||
$this->user = $user;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $data
|
||||
*
|
||||
* @return TransactionGroup
|
||||
*
|
||||
* @throws FireflyException
|
||||
*/
|
||||
public function store(array $data): TransactionGroup
|
||||
{
|
||||
/** @var TransactionGroupFactory $factory */
|
||||
$factory = app(TransactionGroupFactory::class);
|
||||
$factory->setUser($this->user);
|
||||
|
||||
return $factory->create($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param TransactionGroup $transactionGroup
|
||||
* @param array $data
|
||||
*
|
||||
* @return TransactionGroup
|
||||
*
|
||||
* @throws FireflyException
|
||||
*/
|
||||
public function update(TransactionGroup $transactionGroup, array $data): TransactionGroup
|
||||
{
|
||||
/** @var GroupUpdateService $service */
|
||||
$service = app(GroupUpdateService::class);
|
||||
$updatedGroup = $service->update($transactionGroup, $data);
|
||||
|
||||
return $updatedGroup;
|
||||
}
|
||||
}
|
@ -23,7 +23,10 @@ declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\Repositories\TransactionGroup;
|
||||
|
||||
use FireflyIII\Exceptions\FireflyException;
|
||||
use FireflyIII\Models\TransactionGroup;
|
||||
use FireflyIII\Support\NullArrayObject;
|
||||
use FireflyIII\User;
|
||||
|
||||
/**
|
||||
* Interface TransactionGroupRepositoryInterface
|
||||
@ -67,4 +70,32 @@ interface TransactionGroupRepositoryInterface
|
||||
* @return array
|
||||
*/
|
||||
public function getTags(int $journalId): array;
|
||||
|
||||
/**
|
||||
* Set the user.
|
||||
*
|
||||
* @param User $user
|
||||
*/
|
||||
public function setUser(User $user): void;
|
||||
|
||||
/**
|
||||
* Create a new transaction group.
|
||||
*
|
||||
* @param array $data
|
||||
*
|
||||
* @return TransactionGroup
|
||||
* @throws FireflyException
|
||||
*/
|
||||
public function store(array $data): TransactionGroup;
|
||||
|
||||
/**
|
||||
* Update an existing transaction group.
|
||||
*
|
||||
* @param TransactionGroup $transactionGroup
|
||||
* @param array $data
|
||||
*
|
||||
* @return TransactionGroup
|
||||
*/
|
||||
public function update(TransactionGroup $transactionGroup, array $data): TransactionGroup;
|
||||
|
||||
}
|
@ -25,6 +25,7 @@ namespace FireflyIII\Repositories\TransactionType;
|
||||
|
||||
use FireflyIII\Models\TransactionType;
|
||||
use Log;
|
||||
|
||||
/**
|
||||
* Class TransactionTypeRepository
|
||||
*/
|
||||
@ -57,7 +58,8 @@ class TransactionTypeRepository implements TransactionTypeRepositoryInterface
|
||||
|
||||
return $type;
|
||||
}
|
||||
$search = $this->findByType($typeString);
|
||||
$typeString = $typeString ?? TransactionType::WITHDRAWAL;
|
||||
$search = $this->findByType($typeString);
|
||||
if (null === $search) {
|
||||
$search = $this->findByType(TransactionType::WITHDRAWAL);
|
||||
}
|
||||
|
@ -24,16 +24,17 @@ declare(strict_types=1);
|
||||
namespace FireflyIII\Services\Internal\Support;
|
||||
|
||||
use Exception;
|
||||
use FireflyIII\Factory\BillFactory;
|
||||
use FireflyIII\Factory\BudgetFactory;
|
||||
use FireflyIII\Factory\CategoryFactory;
|
||||
use FireflyIII\Exceptions\FireflyException;
|
||||
use FireflyIII\Factory\TagFactory;
|
||||
use FireflyIII\Factory\TransactionJournalMetaFactory;
|
||||
use FireflyIII\Models\Bill;
|
||||
use FireflyIII\Models\Budget;
|
||||
use FireflyIII\Models\Category;
|
||||
use FireflyIII\Models\Account;
|
||||
use FireflyIII\Models\AccountType;
|
||||
use FireflyIII\Models\Note;
|
||||
use FireflyIII\Models\TransactionJournal;
|
||||
use FireflyIII\Models\TransactionType;
|
||||
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
|
||||
use FireflyIII\Repositories\Budget\BudgetRepositoryInterface;
|
||||
use FireflyIII\Repositories\Category\CategoryRepositoryInterface;
|
||||
use FireflyIII\Support\NullArrayObject;
|
||||
use Log;
|
||||
|
||||
/**
|
||||
@ -42,26 +43,229 @@ use Log;
|
||||
*/
|
||||
trait JournalServiceTrait
|
||||
{
|
||||
/** @var AccountRepositoryInterface */
|
||||
private $accountRepository;
|
||||
/** @var BudgetRepositoryInterface */
|
||||
private $budgetRepository;
|
||||
/** @var CategoryRepositoryInterface */
|
||||
private $categoryRepository;
|
||||
/** @var TagFactory */
|
||||
private $tagFactory;
|
||||
|
||||
|
||||
/**
|
||||
* @param string|null $amount
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function getForeignAmount(?string $amount): ?string
|
||||
{
|
||||
$result = null;
|
||||
if (null === $amount) {
|
||||
Log::debug('No foreign amount info in array. Return NULL');
|
||||
|
||||
return null;
|
||||
}
|
||||
if ('' === $amount) {
|
||||
Log::debug('Foreign amount is empty string, return NULL.');
|
||||
|
||||
return null;
|
||||
}
|
||||
if (0 === bccomp('0', $amount)) {
|
||||
Log::debug('Foreign amount is 0.0, return NULL.');
|
||||
|
||||
return null;
|
||||
}
|
||||
Log::debug(sprintf('Foreign amount is %s', $amount));
|
||||
|
||||
return $amount;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $transactionType
|
||||
* @param string $direction
|
||||
* @param int|null $accountId
|
||||
* @param string|null $accountName
|
||||
*
|
||||
* @return Account
|
||||
* @throws FireflyException
|
||||
*/
|
||||
protected function getAccount(string $transactionType, string $direction, ?int $accountId, ?string $accountName): Account
|
||||
{
|
||||
// some debug logging:
|
||||
Log::debug(sprintf('Now in getAccount(%s, %d, %s)', $direction, $accountId, $accountName));
|
||||
|
||||
// final result:
|
||||
$result = null;
|
||||
|
||||
// expected type of source account, in order of preference
|
||||
/** @var array $array */
|
||||
$array = config('firefly.expected_source_types');
|
||||
$expectedTypes = $array[$direction];
|
||||
unset($array);
|
||||
|
||||
// and now try to find it, based on the type of transaction.
|
||||
$message = 'Based on the fact that the transaction is a %s, the %s account should be in: %s';
|
||||
Log::debug(sprintf($message, $transactionType, $direction, implode(', ', $expectedTypes[$transactionType])));
|
||||
|
||||
// first attempt, find by ID.
|
||||
if (null !== $accountId) {
|
||||
$search = $this->accountRepository->findNull($accountId);
|
||||
if (null !== $search && in_array($search->accountType->type, $expectedTypes[$transactionType], true)) {
|
||||
Log::debug(
|
||||
sprintf('Found "account_id" object for %s: #%d, "%s" of type %s', $direction, $search->id, $search->name, $search->accountType->type)
|
||||
);
|
||||
$result = $search;
|
||||
}
|
||||
}
|
||||
|
||||
// second attempt, find by name.
|
||||
if (null === $result && null !== $accountName) {
|
||||
Log::debug('Found nothing by account ID.');
|
||||
// find by preferred type.
|
||||
$source = $this->accountRepository->findByName($accountName, [$expectedTypes[$transactionType][0]]);
|
||||
// or any expected type.
|
||||
$source = $source ?? $this->accountRepository->findByName($accountName, $expectedTypes[$transactionType]);
|
||||
|
||||
if (null !== $source) {
|
||||
Log::debug(sprintf('Found "account_name" object for %s: #%d, %s', $direction, $source->id, $source->name));
|
||||
|
||||
$result = $source;
|
||||
}
|
||||
}
|
||||
|
||||
// return cash account.
|
||||
if (null === $result && null === $accountName
|
||||
&& in_array(AccountType::CASH, $expectedTypes[$transactionType], true)) {
|
||||
$result = $this->accountRepository->getCashAccount();
|
||||
}
|
||||
|
||||
// return new account.
|
||||
if (null === $result) {
|
||||
$accountName = $accountName ?? '(no name)';
|
||||
// final attempt, create it.
|
||||
$preferredType = $expectedTypes[$transactionType][0];
|
||||
if (AccountType::ASSET === $preferredType) {
|
||||
throw new FireflyException(sprintf('TransactionFactory: Cannot create asset account with ID #%d or name "%s".', $accountId, $accountName));
|
||||
}
|
||||
|
||||
$result = $this->accountRepository->store(
|
||||
[
|
||||
'account_type_id' => null,
|
||||
'accountType' => $preferredType,
|
||||
'name' => $accountName,
|
||||
'active' => true,
|
||||
'iban' => null,
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $amount
|
||||
*
|
||||
* @return string
|
||||
* @throws FireflyException
|
||||
*/
|
||||
protected function getAmount(string $amount): string
|
||||
{
|
||||
if ('' === $amount) {
|
||||
throw new FireflyException(sprintf('The amount cannot be an empty string: "%s"', $amount));
|
||||
}
|
||||
if (0 === bccomp('0', $amount)) {
|
||||
throw new FireflyException(sprintf('The amount seems to be zero: "%s"', $amount));
|
||||
}
|
||||
|
||||
return $amount;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param TransactionJournal $journal
|
||||
* @param NullArrayObject $data
|
||||
*/
|
||||
protected function storeBudget(TransactionJournal $journal, NullArrayObject $data): void
|
||||
{
|
||||
if (TransactionType::WITHDRAWAL !== $journal->transactionType->type) {
|
||||
$journal->budgets()->sync([]);
|
||||
|
||||
return;
|
||||
}
|
||||
$budget = $this->budgetRepository->findBudget($data['budget_id'], $data['budget_name']);
|
||||
if (null !== $budget) {
|
||||
Log::debug(sprintf('Link budget #%d to journal #%d', $budget->id, $journal->id));
|
||||
$journal->budgets()->sync([$budget->id]);
|
||||
|
||||
return;
|
||||
}
|
||||
// if the budget is NULL, sync empty.
|
||||
$journal->budgets()->sync([]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param TransactionJournal $journal
|
||||
* @param NullArrayObject $data
|
||||
*/
|
||||
protected function storeCategory(TransactionJournal $journal, NullArrayObject $data): void
|
||||
{
|
||||
$category = $this->categoryRepository->findCategory($data['category_id'], $data['category_name']);
|
||||
if (null !== $category) {
|
||||
Log::debug(sprintf('Link category #%d to journal #%d', $category->id, $journal->id));
|
||||
$journal->categories()->sync([$category->id]);
|
||||
|
||||
return;
|
||||
}
|
||||
// if the category is NULL, sync empty.
|
||||
$journal->categories()->sync([]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param TransactionJournal $journal
|
||||
* @param string $notes
|
||||
*/
|
||||
protected function storeNotes(TransactionJournal $journal, ?string $notes): void
|
||||
{
|
||||
$notes = (string)$notes;
|
||||
$note = $journal->notes()->first();
|
||||
if ('' !== $notes) {
|
||||
if (null === $note) {
|
||||
$note = new Note;
|
||||
$note->noteable()->associate($journal);
|
||||
}
|
||||
$note->text = $notes;
|
||||
$note->save();
|
||||
Log::debug(sprintf('Stored notes for journal #%d', $journal->id));
|
||||
|
||||
return;
|
||||
}
|
||||
if ('' === $notes && null !== $note) {
|
||||
// try to delete existing notes.
|
||||
try {
|
||||
$note->delete();
|
||||
} catch (Exception $e) {
|
||||
Log::debug(sprintf('Could not delete journal notes: %s', $e->getMessage()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Link tags to journal.
|
||||
*
|
||||
* @param TransactionJournal $journal
|
||||
* @param array $data
|
||||
* @param array $tags
|
||||
* @SuppressWarnings(PHPMD.CyclomaticComplexity)
|
||||
*/
|
||||
public function connectTags(TransactionJournal $journal, array $data): void
|
||||
protected function storeTags(TransactionJournal $journal, ?array $tags): void
|
||||
{
|
||||
/** @var TagFactory $factory */
|
||||
$factory = app(TagFactory::class);
|
||||
$factory->setUser($journal->user);
|
||||
$this->tagFactory->setUser($journal->user);
|
||||
$set = [];
|
||||
if (!\is_array($data['tags'])) {
|
||||
return; // @codeCoverageIgnore
|
||||
if (!is_array($tags)) {
|
||||
return;
|
||||
}
|
||||
foreach ($data['tags'] as $string) {
|
||||
foreach ($tags as $string) {
|
||||
if ('' !== $string) {
|
||||
$tag = $factory->findOrCreate($string);
|
||||
$tag = $this->tagFactory->findOrCreate($string);
|
||||
if (null !== $tag) {
|
||||
$set[] = $tag->id;
|
||||
}
|
||||
@ -71,117 +275,147 @@ trait JournalServiceTrait
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param int|null $budgetId
|
||||
* @param null|string $budgetName
|
||||
*
|
||||
* @return Budget|null
|
||||
*/
|
||||
protected function findBudget(?int $budgetId, ?string $budgetName): ?Budget
|
||||
{
|
||||
/** @var BudgetFactory $factory */
|
||||
$factory = app(BudgetFactory::class);
|
||||
$factory->setUser($this->user);
|
||||
|
||||
return $factory->find($budgetId, $budgetName);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int|null $categoryId
|
||||
* @param null|string $categoryName
|
||||
*
|
||||
* @return Category|null
|
||||
*/
|
||||
protected function findCategory(?int $categoryId, ?string $categoryName): ?Category
|
||||
{
|
||||
Log::debug(sprintf('Going to find or create category #%d, with name "%s"', $categoryId, $categoryName));
|
||||
/** @var CategoryFactory $factory */
|
||||
$factory = app(CategoryFactory::class);
|
||||
$factory->setUser($this->user);
|
||||
|
||||
return $factory->findOrCreate($categoryId, $categoryName);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param TransactionJournal $journal
|
||||
* @param Budget|null $budget
|
||||
*/
|
||||
protected function setBudget(TransactionJournal $journal, ?Budget $budget): void
|
||||
{
|
||||
if (null === $budget) {
|
||||
$journal->budgets()->sync([]);
|
||||
|
||||
return;
|
||||
}
|
||||
$journal->budgets()->sync([$budget->id]);
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param TransactionJournal $journal
|
||||
* @param Category|null $category
|
||||
*/
|
||||
protected function setCategory(TransactionJournal $journal, ?Category $category): void
|
||||
{
|
||||
if (null === $category) {
|
||||
$journal->categories()->sync([]);
|
||||
|
||||
return;
|
||||
}
|
||||
$journal->categories()->sync([$category->id]);
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param TransactionJournal $journal
|
||||
* @param array $data
|
||||
* @param string $field
|
||||
*/
|
||||
protected function storeMeta(TransactionJournal $journal, array $data, string $field): void
|
||||
{
|
||||
$set = [
|
||||
'journal' => $journal,
|
||||
'name' => $field,
|
||||
'data' => (string)($data[$field] ?? ''),
|
||||
];
|
||||
|
||||
Log::debug(sprintf('Going to store meta-field "%s", with value "%s".', $set['name'], $set['data']));
|
||||
|
||||
/** @var TransactionJournalMetaFactory $factory */
|
||||
$factory = app(TransactionJournalMetaFactory::class);
|
||||
$factory->updateOrCreate($set);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param TransactionJournal $journal
|
||||
* @param string $notes
|
||||
*/
|
||||
protected function storeNote(TransactionJournal $journal, ?string $notes): void
|
||||
{
|
||||
$notes = (string)$notes;
|
||||
if ('' !== $notes) {
|
||||
$note = $journal->notes()->first();
|
||||
if (null === $note) {
|
||||
$note = new Note;
|
||||
$note->noteable()->associate($journal);
|
||||
}
|
||||
$note->text = $notes;
|
||||
$note->save();
|
||||
|
||||
return;
|
||||
}
|
||||
$note = $journal->notes()->first();
|
||||
if (null !== $note) {
|
||||
try {
|
||||
$note->delete();
|
||||
} catch (Exception $e) {
|
||||
Log::debug(sprintf('Journal service trait could not delete note: %s', $e->getMessage()));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
//
|
||||
// /**
|
||||
// * Link tags to journal.
|
||||
// *
|
||||
// * @param TransactionJournal $journal
|
||||
// * @param array $data
|
||||
// * @SuppressWarnings(PHPMD.CyclomaticComplexity)
|
||||
// */
|
||||
// public function connectTags(TransactionJournal $journal, array $data): void
|
||||
// {
|
||||
// /** @var TagFactory $factory */
|
||||
// $factory = app(TagFactory::class);
|
||||
// $factory->setUser($journal->user);
|
||||
// $set = [];
|
||||
// if (!\is_array($data['tags'])) {
|
||||
// return; // @codeCoverageIgnore
|
||||
// }
|
||||
// foreach ($data['tags'] as $string) {
|
||||
// if ('' !== $string) {
|
||||
// $tag = $factory->findOrCreate($string);
|
||||
// if (null !== $tag) {
|
||||
// $set[] = $tag->id;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// $journal->tags()->sync($set);
|
||||
// }
|
||||
//
|
||||
//
|
||||
// /**
|
||||
// * @param int|null $budgetId
|
||||
// * @param null|string $budgetName
|
||||
// *
|
||||
// * @return Budget|null
|
||||
// */
|
||||
// protected function findBudget(?int $budgetId, ?string $budgetName): ?Budget
|
||||
// {
|
||||
// /** @var BudgetFactory $factory */
|
||||
// $factory = app(BudgetFactory::class);
|
||||
// $factory->setUser($this->user);
|
||||
//
|
||||
// return $factory->find($budgetId, $budgetName);
|
||||
// }
|
||||
//
|
||||
// /**
|
||||
// * @param int|null $categoryId
|
||||
// * @param null|string $categoryName
|
||||
// *
|
||||
// * @return Category|null
|
||||
// */
|
||||
// protected function findCategory(?int $categoryId, ?string $categoryName): ?Category
|
||||
// {
|
||||
// Log::debug(sprintf('Going to find or create category #%d, with name "%s"', $categoryId, $categoryName));
|
||||
// /** @var CategoryFactory $factory */
|
||||
// $factory = app(CategoryFactory::class);
|
||||
// $factory->setUser($this->user);
|
||||
//
|
||||
// return $factory->findOrCreate($categoryId, $categoryName);
|
||||
// }
|
||||
//
|
||||
//
|
||||
// /**
|
||||
// * @param TransactionJournal $journal
|
||||
// * @param Budget|null $budget
|
||||
// */
|
||||
// protected function setBudget(TransactionJournal $journal, ?Budget $budget): void
|
||||
// {
|
||||
// if (null === $budget) {
|
||||
// $journal->budgets()->sync([]);
|
||||
//
|
||||
// return;
|
||||
// }
|
||||
// $journal->budgets()->sync([$budget->id]);
|
||||
//
|
||||
// }
|
||||
//
|
||||
//
|
||||
// /**
|
||||
// * @param TransactionJournal $journal
|
||||
// * @param Category|null $category
|
||||
// */
|
||||
// protected function setCategory(TransactionJournal $journal, ?Category $category): void
|
||||
// {
|
||||
// if (null === $category) {
|
||||
// $journal->categories()->sync([]);
|
||||
//
|
||||
// return;
|
||||
// }
|
||||
// $journal->categories()->sync([$category->id]);
|
||||
//
|
||||
// }
|
||||
//
|
||||
//
|
||||
// /**
|
||||
// * @param TransactionJournal $journal
|
||||
// * @param array $data
|
||||
// * @param string $field
|
||||
// */
|
||||
// protected function storeMeta(TransactionJournal $journal, array $data, string $field): void
|
||||
// {
|
||||
// $set = [
|
||||
// 'journal' => $journal,
|
||||
// 'name' => $field,
|
||||
// 'data' => (string)($data[$field] ?? ''),
|
||||
// ];
|
||||
//
|
||||
// Log::debug(sprintf('Going to store meta-field "%s", with value "%s".', $set['name'], $set['data']));
|
||||
//
|
||||
// /** @var TransactionJournalMetaFactory $factory */
|
||||
// $factory = app(TransactionJournalMetaFactory::class);
|
||||
// $factory->updateOrCreate($set);
|
||||
// }
|
||||
//
|
||||
// /**
|
||||
// * @param TransactionJournal $journal
|
||||
// * @param string $notes
|
||||
// */
|
||||
// protected function storeNote(TransactionJournal $journal, ?string $notes): void
|
||||
// {
|
||||
// $notes = (string)$notes;
|
||||
// if ('' !== $notes) {
|
||||
// $note = $journal->notes()->first();
|
||||
// if (null === $note) {
|
||||
// $note = new Note;
|
||||
// $note->noteable()->associate($journal);
|
||||
// }
|
||||
// $note->text = $notes;
|
||||
// $note->save();
|
||||
//
|
||||
// return;
|
||||
// }
|
||||
// $note = $journal->notes()->first();
|
||||
// if (null !== $note) {
|
||||
// try {
|
||||
// $note->delete();
|
||||
// } catch (Exception $e) {
|
||||
// Log::debug(sprintf('Journal service trait could not delete note: %s', $e->getMessage()));
|
||||
// }
|
||||
// }
|
||||
//
|
||||
//
|
||||
// }
|
||||
}
|
||||
|
84
app/Services/Internal/Update/GroupUpdateService.php
Normal file
84
app/Services/Internal/Update/GroupUpdateService.php
Normal file
@ -0,0 +1,84 @@
|
||||
<?php
|
||||
/**
|
||||
* GroupUpdateService.php
|
||||
* Copyright (c) 2019 thegrumpydictator@gmail.com
|
||||
*
|
||||
* This file is part of Firefly III.
|
||||
*
|
||||
* 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
|
||||
* along with Firefly III. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\Services\Internal\Update;
|
||||
|
||||
use FireflyIII\Models\TransactionGroup;
|
||||
use FireflyIII\Models\TransactionJournal;
|
||||
use Log;
|
||||
|
||||
/**
|
||||
* Class GroupUpdateService
|
||||
*/
|
||||
class GroupUpdateService
|
||||
{
|
||||
/**
|
||||
* Update a transaction group.
|
||||
*
|
||||
* @param TransactionGroup $transactionGroup
|
||||
* @param array $data
|
||||
*
|
||||
* @return TransactionGroup
|
||||
*/
|
||||
public function update(TransactionGroup $transactionGroup, array $data): TransactionGroup
|
||||
{
|
||||
Log::debug('Now in group update service');
|
||||
$transactions = $data['transactions'] ?? [];
|
||||
// update group name.
|
||||
if (array_key_exists('group_title', $data)) {
|
||||
Log::debug(sprintf('Update transaction group #%d title.', $transactionGroup->id));
|
||||
$transactionGroup->title = $data['group_title'];
|
||||
$transactionGroup->save();
|
||||
}
|
||||
if (1 === count($transactions) && 1 === $transactionGroup->transactionJournals()->count()) {
|
||||
/** @var TransactionJournal $first */
|
||||
$first = $transactionGroup->transactionJournals()->first();
|
||||
Log::debug(sprintf('Will now update journal #%d (only journal in group #%d)', $first->id, $transactionGroup->id));
|
||||
$this->updateTransactionJournal($transactionGroup, $first, reset($transactions));
|
||||
$transactionGroup->refresh();
|
||||
|
||||
return $transactionGroup;
|
||||
}
|
||||
die('cannot update split');
|
||||
|
||||
app('preferences')->mark();
|
||||
}
|
||||
|
||||
/**
|
||||
* Update single journal.
|
||||
*
|
||||
* @param TransactionGroup $transactionGroup
|
||||
* @param TransactionJournal $journal
|
||||
* @param array $data
|
||||
*/
|
||||
private function updateTransactionJournal(TransactionGroup $transactionGroup, TransactionJournal $journal, array $data): void
|
||||
{
|
||||
/** @var JournalUpdateService $updateService */
|
||||
$updateService = app(JournalUpdateService::class);
|
||||
$updateService->setTransactionGroup($transactionGroup);
|
||||
$updateService->setTransactionJournal($journal);
|
||||
$updateService->setData($data);
|
||||
$updateService->update();
|
||||
}
|
||||
|
||||
}
|
@ -23,12 +23,25 @@ declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\Services\Internal\Update;
|
||||
|
||||
use FireflyIII\Factory\TransactionFactory;
|
||||
use Carbon\Carbon;
|
||||
use Exception;
|
||||
use FireflyIII\Exceptions\FireflyException;
|
||||
use FireflyIII\Factory\TagFactory;
|
||||
use FireflyIII\Factory\TransactionJournalMetaFactory;
|
||||
use FireflyIII\Factory\TransactionTypeFactory;
|
||||
use FireflyIII\Models\Account;
|
||||
use FireflyIII\Models\Transaction;
|
||||
use FireflyIII\Models\TransactionGroup;
|
||||
use FireflyIII\Models\TransactionJournal;
|
||||
use FireflyIII\Models\TransactionType;
|
||||
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
|
||||
use FireflyIII\Repositories\Bill\BillRepositoryInterface;
|
||||
use FireflyIII\Repositories\Budget\BudgetRepositoryInterface;
|
||||
use FireflyIII\Repositories\Category\CategoryRepositoryInterface;
|
||||
use FireflyIII\Repositories\Currency\CurrencyRepositoryInterface;
|
||||
use FireflyIII\Services\Internal\Support\JournalServiceTrait;
|
||||
use Illuminate\Support\Collection;
|
||||
use FireflyIII\Support\NullArrayObject;
|
||||
use FireflyIII\Validation\AccountValidator;
|
||||
use Log;
|
||||
|
||||
/**
|
||||
@ -40,163 +53,618 @@ class JournalUpdateService
|
||||
{
|
||||
use JournalServiceTrait;
|
||||
|
||||
/** @var BillRepositoryInterface */
|
||||
private $billRepository;
|
||||
/** @var CurrencyRepositoryInterface */
|
||||
private $currencyRepository;
|
||||
/** @var array The data to update the journal with. */
|
||||
private $data;
|
||||
/** @var Account The destination account. */
|
||||
private $destinationAccount;
|
||||
/** @var Transaction */
|
||||
private $destinationTransaction;
|
||||
/** @var array All meta values that are dates. */
|
||||
private $metaDate;
|
||||
/** @var array All meta values that are strings. */
|
||||
private $metaString;
|
||||
/** @var Account Source account of the journal */
|
||||
private $sourceAccount;
|
||||
/** @var Transaction Source transaction of the journal. */
|
||||
private $sourceTransaction;
|
||||
/** @var TransactionGroup The parent group. */
|
||||
private $transactionGroup;
|
||||
/** @var TransactionJournal The journal to update. */
|
||||
private $transactionJournal;
|
||||
/** @var Account If new account info is submitted, this array will hold the valid destination. */
|
||||
private $validDestination;
|
||||
/** @var Account If new account info is submitted, this array will hold the valid source. */
|
||||
private $validSource;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
* JournalUpdateService constructor.
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
if ('testing' === config('app.env')) {
|
||||
Log::warning(sprintf('%s should not be instantiated in the TEST environment!', \get_class($this)));
|
||||
$this->billRepository = app(BillRepositoryInterface::class);
|
||||
$this->categoryRepository = app(CategoryRepositoryInterface::class);
|
||||
$this->budgetRepository = app(BudgetRepositoryInterface::class);
|
||||
$this->tagFactory = app(TagFactory::class);
|
||||
$this->accountRepository = app(AccountRepositoryInterface::class);
|
||||
$this->currencyRepository = app(CurrencyRepositoryInterface::class);
|
||||
$this->metaString = ['sepa_cc', 'sepa_ct_op', 'sepa_ct_id', 'sepa_db', 'sepa_country', 'sepa_ep', 'sepa_ci', 'sepa_batch_id', 'recurrence_id',
|
||||
'internal_reference', 'bunq_payment_id', 'external_id',];
|
||||
$this->metaDate = ['interest_date', 'book_date', 'process_date', 'due_date', 'payment_date', 'invoice_date',];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $data
|
||||
*/
|
||||
public function setData(array $data): void
|
||||
{
|
||||
$this->data = $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param TransactionGroup $transactionGroup
|
||||
*/
|
||||
public function setTransactionGroup(TransactionGroup $transactionGroup): void
|
||||
{
|
||||
$this->transactionGroup = $transactionGroup;
|
||||
$this->billRepository->setUser($transactionGroup->user);
|
||||
$this->categoryRepository->setUser($transactionGroup->user);
|
||||
$this->budgetRepository->setUser($transactionGroup->user);
|
||||
$this->tagFactory->setUser($transactionGroup->user);
|
||||
$this->accountRepository->setUser($transactionGroup->user);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param TransactionJournal $transactionJournal
|
||||
*/
|
||||
public function setTransactionJournal(TransactionJournal $transactionJournal): void
|
||||
{
|
||||
$this->transactionJournal = $transactionJournal;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public function update(): void
|
||||
{
|
||||
Log::debug(sprintf('Now in JournalUpdateService for journal #%d.', $this->transactionJournal->id));
|
||||
|
||||
// can we update account data using the new type?
|
||||
if ($this->hasValidAccounts()) {
|
||||
Log::info('-- account info is valid, now update.');
|
||||
// update accounts:
|
||||
$this->updateAccounts();
|
||||
|
||||
// then also update transaction journal type ID:
|
||||
$this->updateType();
|
||||
$this->transactionJournal->refresh();
|
||||
}
|
||||
|
||||
// find and update bill, if possible.
|
||||
$this->updateBill();
|
||||
|
||||
// update journal fields.
|
||||
$this->updateField('description');
|
||||
$this->updateField('date');
|
||||
$this->updateField('order');
|
||||
|
||||
$this->transactionJournal->save();
|
||||
$this->transactionJournal->refresh();
|
||||
|
||||
// update category
|
||||
if ($this->hasFields(['category_id', 'category_name'])) {
|
||||
Log::debug('Will update category.');
|
||||
|
||||
$this->storeCategory($this->transactionJournal, new NullArrayObject($this->data));
|
||||
}
|
||||
// update budget
|
||||
if ($this->hasFields(['budget_id', 'budget_name'])) {
|
||||
Log::debug('Will update budget.');
|
||||
$this->storeBudget($this->transactionJournal, new NullArrayObject($this->data));
|
||||
}
|
||||
// update tags
|
||||
if ($this->hasFields(['tags'])) {
|
||||
Log::debug('Will update tags.');
|
||||
$tags = $this->data['tags'] ?? null;
|
||||
$this->storeTags($this->transactionJournal, $tags);
|
||||
}
|
||||
|
||||
// update notes.
|
||||
if ($this->hasFields(['notes'])) {
|
||||
$notes = '' === (string)$this->data['notes'] ? null : $this->data['notes'];
|
||||
$this->storeNotes($this->transactionJournal, $notes);
|
||||
}
|
||||
// update meta fields.
|
||||
// first string
|
||||
if ($this->hasFields($this->metaString)) {
|
||||
Log::debug('Meta string fields are present.');
|
||||
$this->updateMetaFields();
|
||||
}
|
||||
|
||||
// then date fields.
|
||||
if ($this->hasFields($this->metaDate)) {
|
||||
Log::debug('Meta date fields are present.');
|
||||
$this->updateMetaDateFields();
|
||||
}
|
||||
|
||||
|
||||
// update transactions.
|
||||
if ($this->hasFields(['currency_id', 'currency_code'])) {
|
||||
$this->updateCurrency();
|
||||
}
|
||||
if ($this->hasFields(['amount'])) {
|
||||
$this->updateAmount();
|
||||
}
|
||||
|
||||
// amount, foreign currency.
|
||||
if ($this->hasFields(['foreign_currency_id', 'foreign_currency_code', 'foreign_amount'])) {
|
||||
$this->updateForeignAmount();
|
||||
}
|
||||
|
||||
// TODO update hash
|
||||
|
||||
app('preferences')->mark();
|
||||
|
||||
$this->transactionJournal->refresh();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get destination transaction.
|
||||
*
|
||||
* @return Transaction
|
||||
*/
|
||||
private function getDestinationTransaction(): Transaction
|
||||
{
|
||||
if (null === $this->destinationTransaction) {
|
||||
$this->destinationTransaction = $this->transactionJournal->transactions()->where('amount', '>', 0)->first();
|
||||
}
|
||||
|
||||
return $this->destinationTransaction;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method returns the current or expected type of the journal (in case of a change) based on the data in the array.
|
||||
*
|
||||
* If the array contains key 'type' and the value is correct, this is returned. Otherwise, the original type is returned.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private function getExpectedType(): string
|
||||
{
|
||||
Log::debug('Now in getExpectedType()');
|
||||
if ($this->hasFields(['type'])) {
|
||||
return ucfirst('opening-balance' === $this->data['type'] ? 'opening balance' : $this->data['type']);
|
||||
}
|
||||
|
||||
return $this->transactionJournal->transactionType->type;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Account
|
||||
*/
|
||||
private function getOriginalDestinationAccount(): Account
|
||||
{
|
||||
if (null === $this->destinationAccount) {
|
||||
$destination = $this->getSourceTransaction();
|
||||
$this->destinationAccount = $destination->account;
|
||||
}
|
||||
|
||||
return $this->destinationAccount;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Account
|
||||
*/
|
||||
private function getOriginalSourceAccount(): Account
|
||||
{
|
||||
if (null === $this->sourceAccount) {
|
||||
$source = $this->getSourceTransaction();
|
||||
$this->sourceAccount = $source->account;
|
||||
}
|
||||
|
||||
return $this->sourceAccount;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Transaction
|
||||
*/
|
||||
private function getSourceTransaction(): Transaction
|
||||
{
|
||||
if (null === $this->sourceTransaction) {
|
||||
$this->sourceTransaction = $this->transactionJournal->transactions()->with(['account'])->where('amount', '<', 0)->first();
|
||||
}
|
||||
|
||||
return $this->sourceTransaction;
|
||||
}
|
||||
|
||||
/**
|
||||
* Does a validation and returns the destination account. This method will break if the dest isn't really valid.
|
||||
*
|
||||
* @return Account
|
||||
*/
|
||||
private function getValidDestinationAccount(): Account
|
||||
{
|
||||
Log::debug('Now in getValidDestinationAccount().');
|
||||
|
||||
if (!$this->hasFields(['destination_id', 'destination_name'])) {
|
||||
return $this->getOriginalDestinationAccount();
|
||||
}
|
||||
|
||||
$destId = $this->data['destination_id'] ?? null;
|
||||
$destName = $this->data['destination_name'] ?? null;
|
||||
|
||||
// make new account validator.
|
||||
$expectedType = $this->getExpectedType();
|
||||
Log::debug(sprintf('Expected type (new or unchanged) is %s', $expectedType));
|
||||
try {
|
||||
$result = $this->getAccount($expectedType, 'destination', $destId, $destName);
|
||||
} catch (FireflyException $e) {
|
||||
Log::error(sprintf('getValidDestinationAccount() threw unexpected error: %s', $e->getMessage()));
|
||||
$result = $this->getOriginalDestinationAccount();
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Does a validation and returns the source account. This method will break if the source isn't really valid.
|
||||
*
|
||||
* @return Account
|
||||
*/
|
||||
private function getValidSourceAccount(): Account
|
||||
{
|
||||
Log::debug('Now in getValidSourceAccount().');
|
||||
$sourceId = $this->data['source_id'] ?? null;
|
||||
$sourceName = $this->data['source_name'] ?? null;
|
||||
|
||||
if (!$this->hasFields(['source_id', 'source_name'])) {
|
||||
return $this->getOriginalSourceAccount();
|
||||
}
|
||||
|
||||
$expectedType = $this->getExpectedType();
|
||||
try {
|
||||
$result = $this->getAccount($expectedType, 'source', $sourceId, $sourceName);
|
||||
} catch (FireflyException $e) {
|
||||
Log::error(sprintf('Cant get the valid source account: %s', $e->getMessage()));
|
||||
|
||||
$result = $this->getOriginalSourceAccount();
|
||||
}
|
||||
|
||||
Log::debug(sprintf('getValidSourceAccount() will return #%d ("%s")', $result->id, $result->name));
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $fields
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private function hasFields(array $fields): bool
|
||||
{
|
||||
foreach ($fields as $field) {
|
||||
if (array_key_exists($field, $this->data)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
private function hasValidAccounts(): bool
|
||||
{
|
||||
return $this->hasValidSourceAccount() && $this->hasValidDestinationAccount();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
private function hasValidDestinationAccount(): bool
|
||||
{
|
||||
Log::debug('Now in hasValidDestinationAccount().');
|
||||
$destId = $this->data['destination_id'] ?? null;
|
||||
$destName = $this->data['destination_name'] ?? null;
|
||||
|
||||
if (!$this->hasFields(['destination_id', 'destination_name'])) {
|
||||
$destination = $this->getOriginalDestinationAccount();
|
||||
$destId = $destination->id;
|
||||
$destName = $destination->name;
|
||||
}
|
||||
|
||||
// make new account validator.
|
||||
$expectedType = $this->getExpectedType();
|
||||
Log::debug(sprintf('Expected type (new or unchanged) is %s', $expectedType));
|
||||
|
||||
// make a new validator.
|
||||
/** @var AccountValidator $validator */
|
||||
$validator = app(AccountValidator::class);
|
||||
$validator->setTransactionType($expectedType);
|
||||
$validator->setUser($this->transactionJournal->user);
|
||||
$validator->source = $this->getValidSourceAccount();
|
||||
|
||||
|
||||
$result = $validator->validateDestination($destId, $destName);
|
||||
Log::debug(sprintf('hasValidDestinationAccount(%d, "%s") will return %s', $destId, $destName, var_export($result, true)));
|
||||
|
||||
// validate submitted info:
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
private function hasValidSourceAccount(): bool
|
||||
{
|
||||
Log::debug('Now in hasValidSourceAccount().');
|
||||
$sourceId = $this->data['source_id'] ?? null;
|
||||
$sourceName = $this->data['source_name'] ?? null;
|
||||
|
||||
if (!$this->hasFields(['source_id', 'source_name'])) {
|
||||
$sourceAccount = $this->getOriginalSourceAccount();
|
||||
$sourceId = $sourceAccount->id;
|
||||
$sourceName = $sourceAccount->name;
|
||||
}
|
||||
|
||||
// make new account validator.
|
||||
$expectedType = $this->getExpectedType();
|
||||
Log::debug(sprintf('Expected type (new or unchanged) is %s', $expectedType));
|
||||
|
||||
// make a new validator.
|
||||
/** @var AccountValidator $validator */
|
||||
$validator = app(AccountValidator::class);
|
||||
$validator->setTransactionType($expectedType);
|
||||
$validator->setUser($this->transactionJournal->user);
|
||||
|
||||
$result = $validator->validateSource($sourceId, $sourceName);
|
||||
Log::debug(sprintf('hasValidSourceAccount(%d, "%s") will return %s', $sourceId, $sourceName, var_export($result, true)));
|
||||
|
||||
// validate submitted info:
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Will update the source and destination accounts of this journal. Assumes they are valid.
|
||||
*/
|
||||
private function updateAccounts(): void
|
||||
{
|
||||
$source = $this->getValidSourceAccount();
|
||||
$destination = $this->getValidDestinationAccount();
|
||||
|
||||
// cowardly refuse to update if both accounts are the same.
|
||||
if ($source->id === $destination->id) {
|
||||
Log::error(sprintf('Source + dest accounts are equal (%d, "%s")', $source->id, $source->name));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$sourceTransaction = $this->getSourceTransaction();
|
||||
$sourceTransaction->account()->associate($source);
|
||||
$sourceTransaction->save();
|
||||
|
||||
$destinationTransaction = $this->getDestinationTransaction();
|
||||
$destinationTransaction->account()->associate($destination);
|
||||
$destinationTransaction->save();
|
||||
|
||||
Log::debug(sprintf('Will set source to #%d ("%s")', $source->id, $source->name));
|
||||
Log::debug(sprintf('Will set dest to #%d ("%s")', $destination->id, $destination->name));
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private function updateAmount(): void
|
||||
{
|
||||
$value = $this->data['amount'] ?? '';
|
||||
try {
|
||||
$amount = $this->getAmount($value);
|
||||
} catch (FireflyException $e) {
|
||||
Log::debug(sprintf('getAmount("%s") returns error: %s', $value, $e->getMessage()));
|
||||
|
||||
return;
|
||||
}
|
||||
Log::debug(sprintf('Updated amount to %s', $amount));
|
||||
$sourceTransaction = $this->getSourceTransaction();
|
||||
$sourceTransaction->amount = app('steam')->negative($value);
|
||||
$sourceTransaction->save();
|
||||
|
||||
$destinationTransaction = $this->getDestinationTransaction();
|
||||
$destinationTransaction->amount = app('steam')->positive($value);
|
||||
$destinationTransaction->save();
|
||||
}
|
||||
|
||||
/**
|
||||
* Update journal bill information.
|
||||
*/
|
||||
private function updateBill(): void
|
||||
{
|
||||
$type = $this->transactionJournal->transactionType->type;
|
||||
if ((
|
||||
array_key_exists('bill_id', $this->data)
|
||||
|| array_key_exists('bill_name', $this->data)
|
||||
)
|
||||
&& TransactionType::WITHDRAWAL === $type
|
||||
) {
|
||||
$billId = (int)($this->data['bill_id'] ?? 0);
|
||||
$billName = (string)($this->data['bill_name'] ?? '');
|
||||
$bill = $this->billRepository->findBill($billId, $billName);
|
||||
$this->transactionJournal->bill_id = null === $bill ? null : $bill->id;
|
||||
Log::debug('Updated bill ID');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private function updateCurrency(): void
|
||||
{
|
||||
$currencyId = $this->data['currency_id'] ?? null;
|
||||
$currencyCode = $this->data['currency_code'] ?? null;
|
||||
$currency = $this->currencyRepository->findCurrency($currencyId, $currencyCode);
|
||||
if (null !== $currency) {
|
||||
// update currency everywhere.
|
||||
$this->transactionJournal->transaction_currency_id = $currency->id;
|
||||
$this->transactionJournal->save();
|
||||
|
||||
$source = $this->getSourceTransaction();
|
||||
$source->transaction_currency_id = $currency->id;
|
||||
$source->save();
|
||||
|
||||
$dest = $this->getDestinationTransaction();
|
||||
$dest->transaction_currency_id = $currency->id;
|
||||
$dest->save();
|
||||
Log::debug(sprintf('Updated currency to #%d (%s)', $currency->id, $currency->code));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update journal generic field. Cannot be set to NULL.
|
||||
*
|
||||
* @param $fieldName
|
||||
*/
|
||||
private function updateField($fieldName): void
|
||||
{
|
||||
if (array_key_exists($fieldName, $this->data) && '' !== (string)$this->data[$fieldName]) {
|
||||
$this->transactionJournal->$fieldName = $this->data[$fieldName];
|
||||
Log::debug(sprintf('Updated %s', $fieldName));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param TransactionJournal $journal
|
||||
* @param array $data
|
||||
*
|
||||
* @return TransactionJournal
|
||||
* @throws \FireflyIII\Exceptions\FireflyException
|
||||
* @SuppressWarnings(PHPMD.ExcessiveMethodLength)
|
||||
* @SuppressWarnings(PHPMD.CyclomaticComplexity)
|
||||
*/
|
||||
public function update(TransactionJournal $journal, array $data): TransactionJournal
|
||||
private function updateForeignAmount(): void
|
||||
{
|
||||
// update journal:
|
||||
$journal->description = $data['description'];
|
||||
$journal->date = $data['date'];
|
||||
$journal->save();
|
||||
$amount = $this->data['foreign_amount'] ?? null;
|
||||
$foreignAmount = $this->getForeignAmount($amount);
|
||||
$source = $this->getSourceTransaction();
|
||||
$dest = $this->getDestinationTransaction();
|
||||
$foreignCurrency = $source->foreignCurrency;
|
||||
|
||||
// update transactions:
|
||||
/** @var TransactionUpdateService $service */
|
||||
$service = app(TransactionUpdateService::class);
|
||||
$service->setUser($journal->user);
|
||||
// find currency in data array
|
||||
$newForeignId = $this->data['foreign_currency_id'] ?? null;
|
||||
$newForeignCode = $this->data['foreign_currency_code'] ?? null;
|
||||
$foreignCurrency = $this->currencyRepository->findCurrencyNull($newForeignId, $newForeignCode) ?? $foreignCurrency;
|
||||
|
||||
// create transactions:
|
||||
/** @var TransactionFactory $factory */
|
||||
$factory = app(TransactionFactory::class);
|
||||
$factory->setUser($journal->user);
|
||||
// not the same as normal currency
|
||||
if (null !== $foreignCurrency && $foreignCurrency->id === $this->transactionJournal->transaction_currency_id) {
|
||||
Log::error(sprintf('Foreign currency is equal to normal currency (%s)', $foreignCurrency->code));
|
||||
|
||||
Log::debug(sprintf('Found %d rows in array (should result in %d transactions', \count($data['transactions']), \count($data['transactions']) * 2));
|
||||
|
||||
/**
|
||||
* @var int $identifier
|
||||
* @var array $trData
|
||||
*/
|
||||
foreach ($data['transactions'] as $identifier => $trData) {
|
||||
// exists transaction(s) with this identifier? update!
|
||||
/** @var Collection $existing */
|
||||
$existing = $journal->transactions()->where('identifier', $identifier)->get();
|
||||
Log::debug(sprintf('Found %d transactions with identifier %d', $existing->count(), $identifier));
|
||||
if ($existing->count() > 0) {
|
||||
$existing->each(
|
||||
function (Transaction $transaction) use ($service, $trData) {
|
||||
Log::debug(sprintf('Update transaction #%d (identifier %d)', $transaction->id, $trData['identifier']));
|
||||
$service->update($transaction, $trData);
|
||||
}
|
||||
);
|
||||
continue;
|
||||
}
|
||||
Log::debug('Found none, so create a pair.');
|
||||
// otherwise, create!
|
||||
$factory->createPair($journal, $trData);
|
||||
return;
|
||||
}
|
||||
// could be that journal has more transactions than submitted (remove split)
|
||||
$transactions = $journal->transactions()->where('amount', '>', 0)->get();
|
||||
Log::debug(sprintf('Journal #%d has %d transactions', $journal->id, $transactions->count()));
|
||||
/** @var Transaction $transaction */
|
||||
foreach ($transactions as $transaction) {
|
||||
Log::debug(sprintf('Now at transaction %d with identifier %d', $transaction->id, $transaction->identifier));
|
||||
if (!isset($data['transactions'][$transaction->identifier])) {
|
||||
Log::debug('No such entry in array, delete this set of transactions.');
|
||||
$journal->transactions()->where('identifier', $transaction->identifier)->delete();
|
||||
}
|
||||
|
||||
// add foreign currency info to source and destination if possible.
|
||||
if (null !== $foreignCurrency && null !== $foreignAmount) {
|
||||
$source->foreign_currency_id = $foreignCurrency->id;
|
||||
$source->foreign_amount = app('steam')->negative($foreignAmount);
|
||||
$source->save();
|
||||
|
||||
|
||||
$dest->foreign_currency_id = $foreignCurrency->id;
|
||||
$dest->foreign_amount = app('steam')->positive($foreignAmount);
|
||||
$dest->save();
|
||||
|
||||
Log::debug(sprintf('Update foreign info to %s (#%d) %s', $foreignCurrency->code, $foreignCurrency->id, $foreignAmount));
|
||||
|
||||
return;
|
||||
}
|
||||
Log::debug(sprintf('New count is %d, transactions array held %d items', $journal->transactions()->count(), \count($data['transactions'])));
|
||||
if ('0' === $amount) {
|
||||
$source->foreign_currency_id = null;
|
||||
$source->foreign_amount = null;
|
||||
$source->save();
|
||||
|
||||
// connect bill:
|
||||
$this->connectBill($journal, $data);
|
||||
|
||||
// connect tags:
|
||||
$this->connectTags($journal, $data);
|
||||
|
||||
// remove category from journal:
|
||||
$journal->categories()->sync([]);
|
||||
|
||||
// remove budgets from journal:
|
||||
$journal->budgets()->sync([]);
|
||||
|
||||
// update or create custom fields:
|
||||
// store date meta fields (if present):
|
||||
$this->storeMeta($journal, $data, 'interest_date');
|
||||
$this->storeMeta($journal, $data, 'book_date');
|
||||
$this->storeMeta($journal, $data, 'process_date');
|
||||
$this->storeMeta($journal, $data, 'due_date');
|
||||
$this->storeMeta($journal, $data, 'payment_date');
|
||||
$this->storeMeta($journal, $data, 'invoice_date');
|
||||
$this->storeMeta($journal, $data, 'internal_reference');
|
||||
|
||||
// store note:
|
||||
$this->storeNote($journal, $data['notes']);
|
||||
|
||||
|
||||
return $journal;
|
||||
$dest->foreign_currency_id = null;
|
||||
$dest->foreign_amount = null;
|
||||
$dest->save();
|
||||
Log::debug(sprintf('Foreign amount is "%s" so remove foreign amount info.', $amount));
|
||||
}
|
||||
Log::info('Not enough info to update foreign currency info.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Update budget for a journal.
|
||||
*
|
||||
* @param TransactionJournal $journal
|
||||
* @param int $budgetId
|
||||
*
|
||||
* @return TransactionJournal
|
||||
*/
|
||||
public function updateBudget(TransactionJournal $journal, int $budgetId): TransactionJournal
|
||||
private function updateMetaDateFields(): void
|
||||
{
|
||||
/** @var TransactionUpdateService $service */
|
||||
$service = app(TransactionUpdateService::class);
|
||||
$service->setUser($journal->user);
|
||||
if (TransactionType::WITHDRAWAL === $journal->transactionType->type) {
|
||||
/** @var Transaction $transaction */
|
||||
foreach ($journal->transactions as $transaction) {
|
||||
$service->updateBudget($transaction, $budgetId);
|
||||
/** @var TransactionJournalMetaFactory $factory */
|
||||
$factory = app(TransactionJournalMetaFactory::class);
|
||||
|
||||
foreach ($this->metaDate as $field) {
|
||||
if ($this->hasFields([$field])) {
|
||||
try {
|
||||
$value = '' === $this->data[$field] ? null : new Carbon($this->data[$field]);
|
||||
} catch (Exception $e) {
|
||||
Log::debug(sprintf('%s is not a valid date value: %s', $this->data[$field], $e->getMessage()));
|
||||
|
||||
return;
|
||||
}
|
||||
Log::debug(sprintf('Field "%s" is present ("%s"), try to update it.', $field, $value));
|
||||
$set = [
|
||||
'journal' => $this->transactionJournal,
|
||||
'name' => $field,
|
||||
'data' => $value,
|
||||
];
|
||||
$factory->updateOrCreate($set);
|
||||
}
|
||||
|
||||
return $journal;
|
||||
}
|
||||
// clear budget.
|
||||
/** @var Transaction $transaction */
|
||||
foreach ($journal->transactions as $transaction) {
|
||||
$transaction->budgets()->sync([]);
|
||||
}
|
||||
// remove budgets from journal:
|
||||
$journal->budgets()->sync([]);
|
||||
|
||||
return $journal;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update category for a journal.
|
||||
*
|
||||
* @param TransactionJournal $journal
|
||||
* @param string $category
|
||||
*
|
||||
* @return TransactionJournal
|
||||
*/
|
||||
public function updateCategory(TransactionJournal $journal, string $category): TransactionJournal
|
||||
private function updateMetaFields(): void
|
||||
{
|
||||
/** @var TransactionUpdateService $service */
|
||||
$service = app(TransactionUpdateService::class);
|
||||
$service->setUser($journal->user);
|
||||
/** @var TransactionJournalMetaFactory $factory */
|
||||
$factory = app(TransactionJournalMetaFactory::class);
|
||||
|
||||
/** @var Transaction $transaction */
|
||||
foreach ($journal->transactions as $transaction) {
|
||||
$service->updateCategory($transaction, $category);
|
||||
foreach ($this->metaString as $field) {
|
||||
if ($this->hasFields([$field])) {
|
||||
$value = '' === $this->data[$field] ? null : $this->data[$field];
|
||||
Log::debug(sprintf('Field "%s" is present ("%s"), try to update it.', $field, $value));
|
||||
$set = [
|
||||
'journal' => $this->transactionJournal,
|
||||
'name' => $field,
|
||||
'data' => $value,
|
||||
];
|
||||
$factory->updateOrCreate($set);
|
||||
}
|
||||
}
|
||||
// make journal empty:
|
||||
$journal->categories()->sync([]);
|
||||
|
||||
return $journal;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates journal transaction type.
|
||||
*/
|
||||
private function updateType(): void
|
||||
{
|
||||
Log::debug('Now in updateType()');
|
||||
if ($this->hasFields(['type'])) {
|
||||
$type = 'opening-balance' === $this->data['type'] ? 'opening balance' : $this->data['type'];
|
||||
Log::debug(
|
||||
sprintf(
|
||||
'Trying to change journal #%d from a %s to a %s.',
|
||||
$this->transactionJournal->id, $this->transactionJournal->transactionType->type, $type
|
||||
)
|
||||
);
|
||||
|
||||
/** @var TransactionTypeFactory $typeFactory */
|
||||
$typeFactory = app(TransactionTypeFactory::class);
|
||||
$result = $typeFactory->find($this->data['type']);
|
||||
if (null !== $result) {
|
||||
Log::debug('Changed transaction type!');
|
||||
$this->transactionJournal->transaction_type_id = $result->id;
|
||||
$this->transactionJournal->save();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
Log::debug('No type field present.');
|
||||
}
|
||||
}
|
||||
|
53
app/Support/Binder/TransactionGroup.php
Normal file
53
app/Support/Binder/TransactionGroup.php
Normal file
@ -0,0 +1,53 @@
|
||||
<?php
|
||||
/**
|
||||
* TransactionGroup.php
|
||||
* Copyright (c) 2019 thegrumpydictator@gmail.com
|
||||
*
|
||||
* This file is part of Firefly III.
|
||||
*
|
||||
* 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
|
||||
* along with Firefly III. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\Support\Binder;
|
||||
|
||||
use FireflyIII\Models\TransactionJournal;
|
||||
use Illuminate\Routing\Route;
|
||||
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
|
||||
|
||||
/**
|
||||
* Class TransactionGroup.
|
||||
*/
|
||||
class TransactionGroup implements BinderInterface
|
||||
{
|
||||
/**
|
||||
* @param string $value
|
||||
* @param Route $route
|
||||
*
|
||||
* @return TransactionGroup
|
||||
* @throws NotFoundHttpException
|
||||
*/
|
||||
public static function routeBinder(string $value, Route $route): TransactionGroup
|
||||
{
|
||||
if (auth()->check()) {
|
||||
$group = auth()->user()->transactionGroups()
|
||||
->find($value);
|
||||
if (null !== $group) {
|
||||
return $group;
|
||||
}
|
||||
}
|
||||
|
||||
throw new NotFoundHttpException;
|
||||
}
|
||||
}
|
@ -195,8 +195,8 @@ class ImportTransaction
|
||||
return;
|
||||
}
|
||||
|
||||
$modifiers = ['rabo-debit-credit', 'ing-debit-credit'];
|
||||
if (\in_array($role, $modifiers, true)) {
|
||||
$modifiers = ['generic-debit-credit'];
|
||||
if (in_array($role, $modifiers, true)) {
|
||||
$this->modifiers[$role] = $columnValue->getValue();
|
||||
|
||||
return;
|
||||
|
@ -119,6 +119,7 @@ class TransactionGroupTransformer extends AbstractTransformer
|
||||
'transaction_journal_id' => $row['transaction_journal_id'],
|
||||
'type' => strtolower($type),
|
||||
'date' => $row['date']->toAtomString(),
|
||||
'order' => $row['order'],
|
||||
|
||||
'currency_id' => $row['currency_id'],
|
||||
'currency_code' => $row['currency_code'],
|
||||
@ -163,7 +164,7 @@ class TransactionGroupTransformer extends AbstractTransformer
|
||||
'original_source' => $metaFieldData['original_source'],
|
||||
'recurrence_id' => $metaFieldData['recurrence_id'],
|
||||
'bunq_payment_id' => $metaFieldData['bunq_payment_id'],
|
||||
'import_hash_v2' => $metaFieldData['import_hash_v2'],
|
||||
'import_hash_v2' => $metaFieldData['import_hash_v2'],
|
||||
|
||||
'sepa_cc' => $metaFieldData['sepa_cc'],
|
||||
'sepa_ct_op' => $metaFieldData['sepa_ct_op'],
|
||||
|
@ -27,6 +27,7 @@ use FireflyIII\Models\Account;
|
||||
use FireflyIII\Models\AccountType;
|
||||
use FireflyIII\Models\TransactionType;
|
||||
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
|
||||
use FireflyIII\User;
|
||||
use Log;
|
||||
|
||||
/**
|
||||
@ -50,6 +51,8 @@ class AccountValidator
|
||||
private $combinations;
|
||||
/** @var string */
|
||||
private $transactionType;
|
||||
/** @var User */
|
||||
private $user;
|
||||
|
||||
/**
|
||||
* AccountValidator constructor.
|
||||
@ -69,9 +72,19 @@ class AccountValidator
|
||||
*/
|
||||
public function setTransactionType(string $transactionType): void
|
||||
{
|
||||
Log::debug(sprintf('Transaction type for validator is now %s', ucfirst($transactionType)));
|
||||
$this->transactionType = ucfirst($transactionType);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param User $user
|
||||
*/
|
||||
public function setUser(User $user): void
|
||||
{
|
||||
$this->user = $user;
|
||||
$this->accountRepository->setUser($user);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int|null $destinationId
|
||||
* @param $destinationName
|
||||
@ -83,7 +96,7 @@ class AccountValidator
|
||||
|
||||
Log::debug(sprintf('Now in AccountValidator::validateDestination(%d, "%s")', $destinationId, $destinationName));
|
||||
if (null === $this->source) {
|
||||
Log::error('Source is NULL');
|
||||
Log::error('Source is NULL, always FALSE.');
|
||||
$this->destError = 'No source account validation has taken place yet. Please do this first or overrule the object.';
|
||||
|
||||
return false;
|
||||
@ -121,9 +134,10 @@ class AccountValidator
|
||||
*/
|
||||
public function validateSource(?int $accountId, ?string $accountName): bool
|
||||
{
|
||||
Log::debug(sprintf('Now in AccountValidator::validateSource(%d, "%s")', $accountId, $accountName));
|
||||
switch ($this->transactionType) {
|
||||
default:
|
||||
$result = false;
|
||||
$result = false;
|
||||
$this->sourceError = sprintf('Cannot handle type "%s"', $this->transactionType);
|
||||
Log::error(sprintf('AccountValidator::validateSource cannot handle "%s", so it will always return false.', $this->transactionType));
|
||||
break;
|
||||
@ -241,7 +255,6 @@ class AccountValidator
|
||||
// if the account can be created anyway we don't need to search.
|
||||
if (null === $result && true === $this->canCreateTypes($validTypes)) {
|
||||
Log::debug('Can create some of these types, so return true.');
|
||||
$this->createDestinationAccount($accountName);
|
||||
$result = true;
|
||||
}
|
||||
|
||||
@ -249,15 +262,18 @@ class AccountValidator
|
||||
// otherwise try to find the account:
|
||||
$search = $this->findExistingAccount($validTypes, (int)$accountId, (string)$accountName);
|
||||
if (null === $search) {
|
||||
Log::debug('findExistingAccount() returned NULL, so the result is false.');
|
||||
$this->destError = (string)trans('validation.deposit_dest_bad_data', ['id' => $accountId, 'name' => $accountName]);
|
||||
$result = false;
|
||||
}
|
||||
if (null !== $search) {
|
||||
Log::debug(sprintf('findExistingAccount() returned #%d ("%s"), so the result is true.', $search->id, $search->name));
|
||||
$this->destination = $search;
|
||||
$result = true;
|
||||
}
|
||||
}
|
||||
$result = $result ?? false;
|
||||
Log::debug(sprintf('validateDepositDestination(%d, "%s") will return %s', $accountId, $accountName, var_export($result, true)));
|
||||
|
||||
return $result;
|
||||
}
|
||||
@ -270,6 +286,7 @@ class AccountValidator
|
||||
*/
|
||||
private function validateDepositSource(?int $accountId, ?string $accountName): bool
|
||||
{
|
||||
Log::debug(sprintf('Now in validateDepositSource(%d, "%s")', $accountId, $accountName));
|
||||
$result = null;
|
||||
// source can be any of the following types.
|
||||
$validTypes = array_keys($this->combinations[$this->transactionType]);
|
||||
@ -281,10 +298,25 @@ class AccountValidator
|
||||
$result = false;
|
||||
}
|
||||
|
||||
// if the user submits an ID only but that ID is not of the correct type,
|
||||
// return false.
|
||||
if (null !== $accountId && null === $accountName) {
|
||||
$search = $this->accountRepository->findNull($accountId);
|
||||
if (null !== $search && !in_array($search->accountType->type, $validTypes, true)) {
|
||||
Log::debug(sprintf('User submitted only an ID (#%d), which is a "%s", so this is not a valid source.', $accountId, $search->accountType->type));
|
||||
$result = false;
|
||||
}
|
||||
}
|
||||
|
||||
// if the account can be created anyway we don't need to search.
|
||||
if (null === $result && true === $this->canCreateTypes($validTypes)) {
|
||||
// set the source to be a (dummy) revenue account.
|
||||
$result = true;
|
||||
|
||||
// set the source to be a (dummy) revenue account.
|
||||
$account = new Account;
|
||||
$accountType = AccountType::whereType(AccountType::REVENUE)->first();
|
||||
$account->accountType = $accountType;
|
||||
$this->source = $account;
|
||||
}
|
||||
$result = $result ?? false;
|
||||
|
||||
@ -332,6 +364,7 @@ class AccountValidator
|
||||
*/
|
||||
private function validateTransferSource(?int $accountId, ?string $accountName): bool
|
||||
{
|
||||
Log::debug(sprintf('Now in validateTransferSource(%d, "%s")', $accountId, $accountName));
|
||||
// source can be any of the following types.
|
||||
$validTypes = array_keys($this->combinations[$this->transactionType]);
|
||||
if (null === $accountId && null === $accountName && false === $this->canCreateTypes($validTypes)) {
|
||||
@ -362,6 +395,7 @@ class AccountValidator
|
||||
*/
|
||||
private function validateWithdrawalDestination(?int $accountId, ?string $accountName): bool
|
||||
{
|
||||
Log::debug(sprintf('Now in validateWithdrawalDestination(%d, "%s")', $accountId, $accountName));
|
||||
// source can be any of the following types.
|
||||
$validTypes = $this->combinations[$this->transactionType][$this->source->accountType->type] ?? [];
|
||||
if (null === $accountId && null === $accountName && false === $this->canCreateTypes($validTypes)) {
|
||||
@ -377,6 +411,7 @@ class AccountValidator
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// don't expect to end up here:
|
||||
return false;
|
||||
}
|
||||
@ -389,6 +424,7 @@ class AccountValidator
|
||||
*/
|
||||
private function validateWithdrawalSource(?int $accountId, ?string $accountName): bool
|
||||
{
|
||||
Log::debug(sprintf('Now in validateWithdrawalSource(%d, "%s")', $accountId, $accountName));
|
||||
// source can be any of the following types.
|
||||
$validTypes = array_keys($this->combinations[$this->transactionType]);
|
||||
if (null === $accountId && null === $accountName && false === $this->canCreateTypes($validTypes)) {
|
||||
|
@ -24,6 +24,7 @@ declare(strict_types=1);
|
||||
namespace FireflyIII\Validation;
|
||||
|
||||
use FireflyIII\Models\Transaction;
|
||||
use FireflyIII\Models\TransactionGroup;
|
||||
use Illuminate\Validation\Validator;
|
||||
|
||||
/**
|
||||
@ -31,6 +32,16 @@ use Illuminate\Validation\Validator;
|
||||
*/
|
||||
trait TransactionValidation
|
||||
{
|
||||
/**
|
||||
* If type is set, source + destination info is mandatory.
|
||||
*
|
||||
* @param Validator $validator
|
||||
*/
|
||||
protected function validateAccountPresence(Validator $validator): void
|
||||
{
|
||||
// TODO
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Validates the given account information. Switches on given transaction type.
|
||||
@ -98,7 +109,9 @@ trait TransactionValidation
|
||||
|
||||
// no valid descriptions?
|
||||
if (0 === $validDescriptions) {
|
||||
$validator->errors()->add('description', (string)trans('validation.filled', ['attribute' => (string)trans('validation.attributes.description')]));
|
||||
$validator->errors()->add(
|
||||
'transactions.0.description', (string)trans('validation.filled', ['attribute' => (string)trans('validation.attributes.description')])
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -148,7 +161,7 @@ trait TransactionValidation
|
||||
$transactions = $data['transactions'] ?? [];
|
||||
// need at least one transaction
|
||||
if (0 === \count($transactions)) {
|
||||
$validator->errors()->add('description', (string)trans('validation.at_least_one_transaction'));
|
||||
$validator->errors()->add('transactions.0.description', (string)trans('validation.at_least_one_transaction'));
|
||||
}
|
||||
}
|
||||
|
||||
@ -233,6 +246,47 @@ trait TransactionValidation
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* All types of splits must be equal.
|
||||
*
|
||||
* @param Validator $validator
|
||||
*/
|
||||
public function validateTransactionTypesForUpdate(Validator $validator): void
|
||||
{
|
||||
$data = $validator->getData();
|
||||
$transactions = $data['transactions'] ?? [];
|
||||
$types = [];
|
||||
foreach ($transactions as $index => $transaction) {
|
||||
$types[] = $transaction['type'] ?? 'invalid';
|
||||
}
|
||||
$unique = array_unique($types);
|
||||
if (count($unique) > 1) {
|
||||
$validator->errors()->add('transactions.0.type', (string)trans('validation.transaction_types_equal'));
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Validator $validator
|
||||
* @param TransactionGroup $transactionGroup
|
||||
*/
|
||||
private function validateJournalIds(Validator $validator, TransactionGroup $transactionGroup): void
|
||||
{
|
||||
$data = $validator->getData();
|
||||
$transactions = $data['transactions'] ?? [];
|
||||
if (count($transactions) < 2) {
|
||||
return;
|
||||
}
|
||||
foreach ($transactions as $index => $transaction) {
|
||||
$journalId = (int)($transaction['transaction_journal_id'] ?? 0);
|
||||
$count = $transactionGroup->transactionJournals()->where('id', $journalId)->count();
|
||||
if (0 === $journalId || 0 === $count) {
|
||||
$validator->errors()->add(sprintf('transactions.%d.source_name', $index), (string)trans('validation.need_id_in_edit'));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// /**
|
||||
// * Throws an error when this asset account is invalid.
|
||||
// *
|
||||
|
@ -24,10 +24,44 @@
|
||||
declare(strict_types=1);
|
||||
|
||||
use FireflyIII\Export\Exporter\CsvExporter;
|
||||
use FireflyIII\Models\Account;
|
||||
use FireflyIII\Models\AccountType;
|
||||
use FireflyIII\Models\Attachment;
|
||||
use FireflyIII\Models\AvailableBudget;
|
||||
use FireflyIII\Models\Bill;
|
||||
use FireflyIII\Models\Budget;
|
||||
use FireflyIII\Models\BudgetLimit;
|
||||
use FireflyIII\Models\Category;
|
||||
use FireflyIII\Models\ExportJob;
|
||||
use FireflyIII\Models\ImportJob;
|
||||
use FireflyIII\Models\LinkType;
|
||||
use FireflyIII\Models\PiggyBank;
|
||||
use FireflyIII\Models\Preference;
|
||||
use FireflyIII\Models\Recurrence;
|
||||
use FireflyIII\Models\Rule;
|
||||
use FireflyIII\Models\RuleGroup;
|
||||
use FireflyIII\Models\Tag;
|
||||
use FireflyIII\Models\Transaction;
|
||||
use FireflyIII\Models\TransactionCurrency;
|
||||
use FireflyIII\Models\TransactionGroup;
|
||||
use FireflyIII\Models\TransactionJournal;
|
||||
use FireflyIII\Models\TransactionJournalLink;
|
||||
use FireflyIII\Models\TransactionType as TransactionTypeModel;
|
||||
use FireflyIII\Services\Currency\FixerIOv2;
|
||||
use FireflyIII\Services\Currency\RatesApiIOv1;
|
||||
use FireflyIII\Support\Binder\AccountList;
|
||||
use FireflyIII\Support\Binder\BudgetList;
|
||||
use FireflyIII\Support\Binder\CategoryList;
|
||||
use FireflyIII\Support\Binder\CLIToken;
|
||||
use FireflyIII\Support\Binder\ConfigurationName;
|
||||
use FireflyIII\Support\Binder\CurrencyCode;
|
||||
use FireflyIII\Support\Binder\Date;
|
||||
use FireflyIII\Support\Binder\ImportProvider;
|
||||
use FireflyIII\Support\Binder\JournalList;
|
||||
use FireflyIII\Support\Binder\SimpleJournalList;
|
||||
use FireflyIII\Support\Binder\TagList;
|
||||
use FireflyIII\Support\Binder\TagOrId;
|
||||
use FireflyIII\Support\Binder\UnfinishedJournal;
|
||||
use FireflyIII\TransactionRules\Actions\AddTag;
|
||||
use FireflyIII\TransactionRules\Actions\AppendDescription;
|
||||
use FireflyIII\TransactionRules\Actions\AppendNotes;
|
||||
@ -82,6 +116,7 @@ use FireflyIII\TransactionRules\Triggers\ToAccountIs;
|
||||
use FireflyIII\TransactionRules\Triggers\ToAccountStarts;
|
||||
use FireflyIII\TransactionRules\Triggers\TransactionType;
|
||||
use FireflyIII\TransactionRules\Triggers\UserAction;
|
||||
use FireflyIII\User;
|
||||
|
||||
/*
|
||||
* DO NOT EDIT THIS FILE. IT IS AUTO GENERATED.
|
||||
@ -320,54 +355,55 @@ return [
|
||||
],
|
||||
'bindables' => [
|
||||
// models
|
||||
'account' => \FireflyIII\Models\Account::class,
|
||||
'attachment' => \FireflyIII\Models\Attachment::class,
|
||||
'availableBudget' => \FireflyIII\Models\AvailableBudget::class,
|
||||
'bill' => \FireflyIII\Models\Bill::class,
|
||||
'budget' => \FireflyIII\Models\Budget::class,
|
||||
'budgetLimit' => \FireflyIII\Models\BudgetLimit::class,
|
||||
'category' => \FireflyIII\Models\Category::class,
|
||||
'linkType' => \FireflyIII\Models\LinkType::class,
|
||||
'transactionType' => \FireflyIII\Models\TransactionType::class,
|
||||
'journalLink' => \FireflyIII\Models\TransactionJournalLink::class,
|
||||
'currency' => \FireflyIII\Models\TransactionCurrency::class,
|
||||
'piggyBank' => \FireflyIII\Models\PiggyBank::class,
|
||||
'preference' => \FireflyIII\Models\Preference::class,
|
||||
'tj' => \FireflyIII\Models\TransactionJournal::class,
|
||||
'tag' => \FireflyIII\Models\Tag::class,
|
||||
'recurrence' => \FireflyIII\Models\Recurrence::class,
|
||||
'rule' => \FireflyIII\Models\Rule::class,
|
||||
'ruleGroup' => \FireflyIII\Models\RuleGroup::class,
|
||||
'exportJob' => \FireflyIII\Models\ExportJob::class,
|
||||
'importJob' => \FireflyIII\Models\ImportJob::class,
|
||||
'transaction' => \FireflyIII\Models\Transaction::class,
|
||||
'user' => \FireflyIII\User::class,
|
||||
'account' => Account::class,
|
||||
'attachment' => Attachment::class,
|
||||
'availableBudget' => AvailableBudget::class,
|
||||
'bill' => Bill::class,
|
||||
'budget' => Budget::class,
|
||||
'budgetLimit' => BudgetLimit::class,
|
||||
'category' => Category::class,
|
||||
'linkType' => LinkType::class,
|
||||
'transactionType' => TransactionTypeModel::class,
|
||||
'journalLink' => TransactionJournalLink::class,
|
||||
'currency' => TransactionCurrency::class,
|
||||
'piggyBank' => PiggyBank::class,
|
||||
'preference' => Preference::class,
|
||||
'tj' => TransactionJournal::class,
|
||||
'tag' => Tag::class,
|
||||
'recurrence' => Recurrence::class,
|
||||
'rule' => Rule::class,
|
||||
'ruleGroup' => RuleGroup::class,
|
||||
'exportJob' => ExportJob::class,
|
||||
'importJob' => ImportJob::class,
|
||||
'transaction' => Transaction::class,
|
||||
'transactionGroup' => TransactionGroup::class,
|
||||
'user' => User::class,
|
||||
|
||||
// strings
|
||||
'import_provider' => \FireflyIII\Support\Binder\ImportProvider::class,
|
||||
'currency_code' => \FireflyIII\Support\Binder\CurrencyCode::class,
|
||||
'import_provider' => ImportProvider::class,
|
||||
'currency_code' => CurrencyCode::class,
|
||||
|
||||
// dates
|
||||
'start_date' => \FireflyIII\Support\Binder\Date::class,
|
||||
'end_date' => \FireflyIII\Support\Binder\Date::class,
|
||||
'date' => \FireflyIII\Support\Binder\Date::class,
|
||||
'start_date' => Date::class,
|
||||
'end_date' => Date::class,
|
||||
'date' => Date::class,
|
||||
|
||||
// lists
|
||||
'accountList' => \FireflyIII\Support\Binder\AccountList::class,
|
||||
'expenseList' => \FireflyIII\Support\Binder\AccountList::class,
|
||||
'budgetList' => \FireflyIII\Support\Binder\BudgetList::class,
|
||||
'journalList' => \FireflyIII\Support\Binder\JournalList::class,
|
||||
'categoryList' => \FireflyIII\Support\Binder\CategoryList::class,
|
||||
'tagList' => \FireflyIII\Support\Binder\TagList::class,
|
||||
'simpleJournalList' => \FireflyIII\Support\Binder\SimpleJournalList::class,
|
||||
'accountList' => AccountList::class,
|
||||
'expenseList' => AccountList::class,
|
||||
'budgetList' => BudgetList::class,
|
||||
'journalList' => JournalList::class,
|
||||
'categoryList' => CategoryList::class,
|
||||
'tagList' => TagList::class,
|
||||
'simpleJournalList' => SimpleJournalList::class,
|
||||
|
||||
// others
|
||||
'fromCurrencyCode' => \FireflyIII\Support\Binder\CurrencyCode::class,
|
||||
'toCurrencyCode' => \FireflyIII\Support\Binder\CurrencyCode::class,
|
||||
'unfinishedJournal' => \FireflyIII\Support\Binder\UnfinishedJournal::class,
|
||||
'cliToken' => \FireflyIII\Support\Binder\CLIToken::class,
|
||||
'tagOrId' => \FireflyIII\Support\Binder\TagOrId::class,
|
||||
'configName' => \FireflyIII\Support\Binder\ConfigurationName::class,
|
||||
'fromCurrencyCode' => CurrencyCode::class,
|
||||
'toCurrencyCode' => CurrencyCode::class,
|
||||
'unfinishedJournal' => UnfinishedJournal::class,
|
||||
'cliToken' => CLIToken::class,
|
||||
'tagOrId' => TagOrId::class,
|
||||
'configName' => ConfigurationName::class,
|
||||
|
||||
|
||||
],
|
||||
|
@ -182,4 +182,5 @@ return [
|
||||
'transfer_source_bad_data' => 'Could not find a valid source account when searching for ID ":id" or name ":name".',
|
||||
'transfer_dest_need_data' => 'Need to get a valid destination account ID and/or valid destination account name to continue.',
|
||||
'transfer_dest_bad_data' => 'Could not find a valid destination account when searching for ID ":id" or name ":name".',
|
||||
'need_id_in_edit' => 'When updating a transaction with splits, each split must have a valid transaction journal id (field "transaction_journal_id").',
|
||||
];
|
||||
|
@ -43,7 +43,7 @@ declare(strict_types=1);
|
||||
|
||||
Route::group(
|
||||
['middleware' => ['auth:api', 'bindings'], 'namespace' => 'FireflyIII\Api\V1\Controllers', 'prefix' => 'about', 'as' => 'api.v1.about.'],
|
||||
function () {
|
||||
static function () {
|
||||
|
||||
// Accounts API routes:
|
||||
Route::get('', ['uses' => 'AboutController@about', 'as' => 'index']);
|
||||
@ -54,7 +54,7 @@ Route::group(
|
||||
|
||||
Route::group(
|
||||
['middleware' => ['auth:api', 'bindings'], 'namespace' => 'FireflyIII\Api\V1\Controllers', 'prefix' => 'accounts', 'as' => 'api.v1.accounts.'],
|
||||
function () {
|
||||
static function () {
|
||||
|
||||
// Accounts API routes:
|
||||
Route::get('', ['uses' => 'AccountController@index', 'as' => 'index']);
|
||||
@ -71,7 +71,7 @@ Route::group(
|
||||
|
||||
Route::group(
|
||||
['middleware' => ['auth:api', 'bindings'], 'namespace' => 'FireflyIII\Api\V1\Controllers', 'prefix' => 'attachments', 'as' => 'api.v1.attachments.'],
|
||||
function () {
|
||||
static function () {
|
||||
|
||||
// Attachment API routes:
|
||||
Route::get('', ['uses' => 'AttachmentController@index', 'as' => 'index']);
|
||||
@ -87,7 +87,7 @@ Route::group(
|
||||
Route::group(
|
||||
['middleware' => ['auth:api', 'bindings'], 'namespace' => 'FireflyIII\Api\V1\Controllers', 'prefix' => 'available_budgets',
|
||||
'as' => 'api.v1.available_budgets.'],
|
||||
function () {
|
||||
static function () {
|
||||
|
||||
// Available Budget API routes:
|
||||
Route::get('', ['uses' => 'AvailableBudgetController@index', 'as' => 'index']);
|
||||
@ -99,7 +99,7 @@ Route::group(
|
||||
);
|
||||
|
||||
Route::group(
|
||||
['middleware' => ['auth:api', 'bindings'], 'namespace' => 'FireflyIII\Api\V1\Controllers', 'prefix' => 'bills', 'as' => 'api.v1.bills.'], function () {
|
||||
['middleware' => ['auth:api', 'bindings'], 'namespace' => 'FireflyIII\Api\V1\Controllers', 'prefix' => 'bills', 'as' => 'api.v1.bills.'], static function () {
|
||||
|
||||
// Bills API routes:
|
||||
Route::get('', ['uses' => 'BillController@index', 'as' => 'index']);
|
||||
@ -117,7 +117,7 @@ Route::group(
|
||||
|
||||
Route::group(
|
||||
['middleware' => ['auth:api', 'bindings'], 'namespace' => 'FireflyIII\Api\V1\Controllers', 'prefix' => 'budgets/limits', 'as' => 'api.v1.budget_limits.'],
|
||||
function () {
|
||||
static function () {
|
||||
|
||||
// Budget Limit API routes:
|
||||
Route::get('', ['uses' => 'BudgetLimitController@index', 'as' => 'index']);
|
||||
@ -131,7 +131,7 @@ Route::group(
|
||||
|
||||
Route::group(
|
||||
['middleware' => ['auth:api', 'bindings'], 'namespace' => 'FireflyIII\Api\V1\Controllers', 'prefix' => 'budgets', 'as' => 'api.v1.budgets.'],
|
||||
function () {
|
||||
static function () {
|
||||
|
||||
// Budget API routes:
|
||||
Route::get('', ['uses' => 'BudgetController@index', 'as' => 'index']);
|
||||
@ -147,7 +147,7 @@ Route::group(
|
||||
|
||||
Route::group(
|
||||
['middleware' => ['auth:api', 'bindings'], 'namespace' => 'FireflyIII\Api\V1\Controllers', 'prefix' => 'categories', 'as' => 'api.v1.categories.'],
|
||||
function () {
|
||||
static function () {
|
||||
|
||||
// Category API routes:
|
||||
Route::get('', ['uses' => 'CategoryController@index', 'as' => 'index']);
|
||||
@ -167,7 +167,7 @@ Route::group(
|
||||
Route::group(
|
||||
['middleware' => ['auth:api', 'bindings'], 'namespace' => 'FireflyIII\Api\V1\Controllers\Chart', 'prefix' => 'chart/account',
|
||||
'as' => 'api.v1.chart.account.'],
|
||||
function () {
|
||||
static function () {
|
||||
Route::get('overview', ['uses' => 'AccountController@overview', 'as' => 'overview']);
|
||||
Route::get('expense', ['uses' => 'AccountController@expenseOverview', 'as' => 'expense']);
|
||||
Route::get('revenue', ['uses' => 'AccountController@revenueOverview', 'as' => 'revenue']);
|
||||
@ -179,7 +179,7 @@ Route::group(
|
||||
Route::group(
|
||||
['middleware' => ['auth:api', 'bindings'], 'namespace' => 'FireflyIII\Api\V1\Controllers\Chart', 'prefix' => 'chart/ab',
|
||||
'as' => 'api.v1.chart.ab.'],
|
||||
function () {
|
||||
static function () {
|
||||
|
||||
// Overview API routes:
|
||||
Route::get('overview/{availableBudget}', ['uses' => 'AvailableBudgetController@overview', 'as' => 'overview']);
|
||||
@ -190,7 +190,7 @@ Route::group(
|
||||
Route::group(
|
||||
['middleware' => ['auth:api', 'bindings'], 'namespace' => 'FireflyIII\Api\V1\Controllers\Chart', 'prefix' => 'chart/category',
|
||||
'as' => 'api.v1.chart.category.'],
|
||||
function () {
|
||||
static function () {
|
||||
|
||||
// Overview API routes:
|
||||
Route::get('overview', ['uses' => 'CategoryController@overview', 'as' => 'overview']);
|
||||
@ -203,7 +203,7 @@ Route::group(
|
||||
|
||||
Route::group(
|
||||
['middleware' => ['auth:api', 'bindings'], 'namespace' => 'FireflyIII\Api\V1\Controllers', 'prefix' => 'configuration', 'as' => 'api.v1.configuration.'],
|
||||
function () {
|
||||
static function () {
|
||||
|
||||
// Configuration API routes:
|
||||
Route::get('', ['uses' => 'ConfigurationController@index', 'as' => 'index']);
|
||||
@ -213,7 +213,7 @@ Route::group(
|
||||
|
||||
Route::group(
|
||||
['middleware' => ['auth:api', 'bindings'], 'namespace' => 'FireflyIII\Api\V1\Controllers', 'prefix' => 'cer', 'as' => 'api.v1.cer.'],
|
||||
function () {
|
||||
static function () {
|
||||
|
||||
// Currency Exchange Rate API routes:
|
||||
Route::get('', ['uses' => 'CurrencyExchangeRateController@index', 'as' => 'index']);
|
||||
@ -222,7 +222,7 @@ Route::group(
|
||||
|
||||
Route::group(
|
||||
['middleware' => ['auth:api', 'bindings'], 'namespace' => 'FireflyIII\Api\V1\Controllers', 'prefix' => 'import', 'as' => 'api.v1.import.'],
|
||||
function () {
|
||||
static function () {
|
||||
|
||||
// Transaction Links API routes:
|
||||
Route::get('list', ['uses' => 'ImportController@listAll', 'as' => 'list']);
|
||||
@ -232,7 +232,7 @@ Route::group(
|
||||
);
|
||||
Route::group(
|
||||
['middleware' => ['auth:api', 'bindings'], 'namespace' => 'FireflyIII\Api\V1\Controllers', 'prefix' => 'link_types', 'as' => 'api.v1.link_types.'],
|
||||
function () {
|
||||
static function () {
|
||||
|
||||
// Link Type API routes:
|
||||
Route::get('', ['uses' => 'LinkTypeController@index', 'as' => 'index']);
|
||||
@ -247,7 +247,7 @@ Route::group(
|
||||
Route::group(
|
||||
['middleware' => ['auth:api', 'bindings'], 'namespace' => 'FireflyIII\Api\V1\Controllers', 'prefix' => 'transaction_links',
|
||||
'as' => 'api.v1.transaction_links.'],
|
||||
function () {
|
||||
static function () {
|
||||
|
||||
// Transaction Links API routes:
|
||||
Route::get('', ['uses' => 'TransactionLinkController@index', 'as' => 'index']);
|
||||
@ -261,7 +261,7 @@ Route::group(
|
||||
|
||||
Route::group(
|
||||
['middleware' => ['auth:api', 'bindings'], 'namespace' => 'FireflyIII\Api\V1\Controllers', 'prefix' => 'piggy_banks', 'as' => 'api.v1.piggy_banks.'],
|
||||
function () {
|
||||
static function () {
|
||||
|
||||
// Piggy Bank API routes:
|
||||
Route::get('', ['uses' => 'PiggyBankController@index', 'as' => 'index']);
|
||||
@ -275,7 +275,7 @@ Route::group(
|
||||
|
||||
Route::group(
|
||||
['middleware' => ['auth:api', 'bindings'], 'namespace' => 'FireflyIII\Api\V1\Controllers', 'prefix' => 'preferences', 'as' => 'api.v1.preferences.'],
|
||||
function () {
|
||||
static function () {
|
||||
|
||||
// Preference API routes:
|
||||
Route::get('', ['uses' => 'PreferenceController@index', 'as' => 'index']);
|
||||
@ -286,7 +286,7 @@ Route::group(
|
||||
|
||||
Route::group(
|
||||
['middleware' => ['auth:api', 'bindings'], 'namespace' => 'FireflyIII\Api\V1\Controllers', 'prefix' => 'recurrences', 'as' => 'api.v1.recurrences.'],
|
||||
function () {
|
||||
static function () {
|
||||
|
||||
// Recurrence API routes:
|
||||
Route::get('', ['uses' => 'RecurrenceController@index', 'as' => 'index']);
|
||||
@ -301,7 +301,7 @@ Route::group(
|
||||
|
||||
Route::group(
|
||||
['middleware' => ['auth:api', 'bindings'], 'namespace' => 'FireflyIII\Api\V1\Controllers', 'prefix' => 'rules', 'as' => 'api.v1.rules.'],
|
||||
function () {
|
||||
static function () {
|
||||
|
||||
// Rules API routes:
|
||||
Route::get('', ['uses' => 'RuleController@index', 'as' => 'index']);
|
||||
@ -316,7 +316,7 @@ Route::group(
|
||||
|
||||
Route::group(
|
||||
['middleware' => ['auth:api', 'bindings'], 'namespace' => 'FireflyIII\Api\V1\Controllers', 'prefix' => 'rule_groups', 'as' => 'api.v1.rule_groups.'],
|
||||
function () {
|
||||
static function () {
|
||||
|
||||
// Rules API routes:
|
||||
Route::get('', ['uses' => 'RuleGroupController@index', 'as' => 'index']);
|
||||
@ -333,7 +333,7 @@ Route::group(
|
||||
Route::group(
|
||||
['middleware' => ['auth:api', 'bindings'], 'namespace' => 'FireflyIII\Api\V1\Controllers', 'prefix' => 'summary',
|
||||
'as' => 'api.v1.summary.'],
|
||||
function () {
|
||||
static function () {
|
||||
|
||||
// Overview API routes:
|
||||
Route::get('basic', ['uses' => 'SummaryController@basic', 'as' => 'basic']);
|
||||
@ -343,7 +343,7 @@ Route::group(
|
||||
|
||||
Route::group(
|
||||
['middleware' => ['auth:api', 'bindings'], 'namespace' => 'FireflyIII\Api\V1\Controllers', 'prefix' => 'currencies', 'as' => 'api.v1.currencies.'],
|
||||
function () {
|
||||
static function () {
|
||||
|
||||
// Transaction currency API routes:
|
||||
Route::get('', ['uses' => 'CurrencyController@index', 'as' => 'index']);
|
||||
@ -369,7 +369,7 @@ Route::group(
|
||||
|
||||
Route::group(
|
||||
['middleware' => ['auth:api', 'bindings'], 'namespace' => 'FireflyIII\Api\V1\Controllers', 'prefix' => 'tags', 'as' => 'api.v1.tags.'],
|
||||
function () {
|
||||
static function () {
|
||||
// Tag API routes:
|
||||
Route::get('', ['uses' => 'TagController@index', 'as' => 'index']);
|
||||
Route::post('', ['uses' => 'TagController@store', 'as' => 'store']);
|
||||
@ -382,7 +382,7 @@ Route::group(
|
||||
|
||||
Route::group(
|
||||
['middleware' => ['auth:api', 'bindings'], 'namespace' => 'FireflyIII\Api\V1\Controllers', 'prefix' => 'tag-cloud', 'as' => 'api.v1.tag-cloud.'],
|
||||
function () {
|
||||
static function () {
|
||||
// Tag cloud API routes (to prevent collisions)
|
||||
Route::get('', ['uses' => 'TagController@cloud', 'as' => 'cloud']);
|
||||
}
|
||||
@ -391,7 +391,7 @@ Route::group(
|
||||
|
||||
Route::group(
|
||||
['middleware' => ['auth:api', 'bindings'], 'namespace' => 'FireflyIII\Api\V1\Controllers', 'prefix' => 'transactions', 'as' => 'api.v1.transactions.'],
|
||||
function () {
|
||||
static function () {
|
||||
|
||||
// Transaction API routes:
|
||||
Route::get('', ['uses' => 'TransactionController@index', 'as' => 'index']);
|
||||
@ -409,7 +409,7 @@ Route::group(
|
||||
Route::group(
|
||||
['middleware' => ['auth:api', 'bindings', \FireflyIII\Http\Middleware\IsAdmin::class], 'namespace' => 'FireflyIII\Api\V1\Controllers', 'prefix' => 'users',
|
||||
'as' => 'api.v1.users.'],
|
||||
function () {
|
||||
static function () {
|
||||
|
||||
// Users API routes:
|
||||
Route::get('', ['uses' => 'UserController@index', 'as' => 'index']);
|
||||
|
Loading…
Reference in New Issue
Block a user