Files
firefly-iii/app/Api/V1/Requests/Models/Transaction/StoreRequest.php

290 lines
15 KiB
PHP
Raw Normal View History

<?php
2018-05-11 10:08:34 +02:00
/**
* TransactionStoreRequest.php
* Copyright (c) 2019 james@firefly-iii.org
*
* This file is part of Firefly III (https://github.com/firefly-iii).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
2018-05-11 10:08:34 +02:00
declare(strict_types=1);
2021-03-06 20:52:42 +01:00
namespace FireflyIII\Api\V1\Requests\Models\Transaction;
use FireflyIII\Rules\BelongsUser;
2018-12-03 15:57:15 +01:00
use FireflyIII\Rules\IsBoolean;
2019-02-08 07:14:45 +01:00
use FireflyIII\Rules\IsDateOrTime;
2024-01-02 14:33:17 +01:00
use FireflyIII\Rules\IsValidPositiveAmount;
use FireflyIII\Support\NullArrayObject;
2021-02-17 06:17:48 +01:00
use FireflyIII\Support\Request\AppendsLocationData;
2020-11-08 13:36:13 +01:00
use FireflyIII\Support\Request\ChecksLogin;
2020-07-18 08:34:00 +02:00
use FireflyIII\Support\Request\ConvertsDataTypes;
2020-03-21 06:00:36 +01:00
use FireflyIII\Validation\CurrencyValidation;
use FireflyIII\Validation\GroupValidation;
2018-07-05 18:02:02 +02:00
use FireflyIII\Validation\TransactionValidation;
use Illuminate\Foundation\Http\FormRequest;
2023-05-29 13:56:55 +02:00
use Illuminate\Validation\Validator;
/**
2021-03-06 20:52:42 +01:00
* Class StoreRequest
*/
2021-03-06 20:52:42 +01:00
class StoreRequest extends FormRequest
{
2022-10-30 14:23:00 +01:00
use AppendsLocationData;
2023-11-04 14:18:49 +01:00
use ChecksLogin;
use ConvertsDataTypes;
use CurrencyValidation;
use GroupValidation;
use TransactionValidation;
/**
2018-07-05 18:02:02 +02:00
* Get all data. Is pretty complex because of all the ??-statements.
*/
public function getAll(): array
{
2023-10-29 06:33:43 +01:00
app('log')->debug('get all data in TransactionStoreRequest');
2021-05-24 08:06:56 +02:00
return [
2022-05-02 19:35:35 +02:00
'group_title' => $this->convertString('group_title'),
'error_if_duplicate_hash' => $this->boolean('error_if_duplicate_hash'),
'apply_rules' => $this->boolean('apply_rules', true),
2021-05-02 14:52:25 +02:00
'fire_webhooks' => $this->boolean('fire_webhooks', true),
'transactions' => $this->getTransactionData(),
];
2022-10-30 11:43:17 +01:00
// TODO include location and ability to process it.
}
/**
* The rules that the incoming request must be matched against.
*/
public function rules(): array
{
2023-10-29 06:33:43 +01:00
app('log')->debug('Collect rules of TransactionStoreRequest');
$validProtocols = config('firefly.valid_url_protocols');
2023-12-20 19:35:52 +01:00
2020-03-15 08:16:16 +01:00
return [
// basic fields for group:
'group_title' => 'between:1,1000|nullable',
'error_if_duplicate_hash' => [new IsBoolean()],
'apply_rules' => [new IsBoolean()],
2018-12-21 09:01:21 +01:00
// 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|nullable',
'transactions.*.currency_code' => 'min:3|max:51|exists:transaction_currencies,code|nullable',
'transactions.*.foreign_currency_id' => 'numeric|exists:transaction_currencies,id|nullable',
'transactions.*.foreign_currency_code' => 'min:3|max:51|exists:transaction_currencies,code|nullable',
// amount
2024-01-02 14:33:17 +01:00
'transactions.*.amount' => ['required', new IsValidPositiveAmount()],
'transactions.*.foreign_amount' => ['nullable', new IsValidPositiveAmount()],
// description
'transactions.*.description' => 'nullable|between:1,1000',
// source of transaction
'transactions.*.source_id' => ['numeric', 'nullable', new BelongsUser()],
'transactions.*.source_name' => 'between:1,255|nullable',
'transactions.*.source_iban' => 'between:1,255|nullable|iban',
'transactions.*.source_number' => 'between:1,255|nullable',
'transactions.*.source_bic' => 'between:1,255|nullable|bic',
// destination of transaction
'transactions.*.destination_id' => ['numeric', 'nullable', new BelongsUser()],
'transactions.*.destination_name' => 'between:1,255|nullable',
'transactions.*.destination_iban' => 'between:1,255|nullable|iban',
'transactions.*.destination_number' => 'between:1,255|nullable',
'transactions.*.destination_bic' => 'between:1,255|nullable|bic',
// 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(), 'nullable'],
'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()],
'transactions.*.piggy_bank_id' => ['numeric', 'nullable', 'mustExist:piggy_banks,id', new BelongsUser()],
'transactions.*.piggy_bank_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',
2023-12-31 17:40:57 +01:00
'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',
'transactions.*.external_url' => sprintf('min:1|max:255|nullable|url:%s', $validProtocols),
// 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',
];
}
/**
* Configure the validator instance.
*/
public function withValidator(Validator $validator): void
{
$validator->after(
2023-12-21 05:07:26 +01:00
function (Validator $validator): void {
2020-10-24 16:49:05 +02:00
// must be valid array.
$this->validateTransactionArray($validator);
// must submit at least one transaction.
2023-10-29 06:33:43 +01:00
app('log')->debug('Now going to validateOneTransaction');
2018-07-05 18:02:02 +02:00
$this->validateOneTransaction($validator);
2023-10-29 06:33:43 +01:00
app('log')->debug('Now done with validateOneTransaction');
// all journals must have a description
2018-07-05 18:02:02 +02:00
$this->validateDescriptions($validator);
2019-04-06 11:08:46 +02:00
// all transaction types must be equal:
$this->validateTransactionTypes($validator);
// validate foreign currency info
2018-07-05 18:02:02 +02:00
$this->validateForeignCurrencyInformation($validator);
// validate all account info
$this->validateAccountInformation($validator);
2019-04-06 11:08:46 +02:00
// validate source/destination is equal, depending on the transaction journal type.
$this->validateEqualAccounts($validator);
// the group must have a description if > 1 journal.
$this->validateGroupDescription($validator);
}
);
}
2023-12-20 19:35:52 +01:00
/**
* Get transaction data.
*/
private function getTransactionData(): array
{
$return = [];
/**
* @var array $transaction
*/
foreach ($this->get('transactions') as $transaction) {
$object = new NullArrayObject($transaction);
$return[] = [
'type' => $this->clearString($object['type']),
'date' => $this->dateFromValue($object['date']),
'order' => $this->integerFromValue((string)$object['order']),
2023-12-20 19:35:52 +01:00
'currency_id' => $this->integerFromValue((string)$object['currency_id']),
'currency_code' => $this->clearString((string)$object['currency_code']),
// foreign currency info:
'foreign_currency_id' => $this->integerFromValue((string)$object['foreign_currency_id']),
'foreign_currency_code' => $this->clearString((string)$object['foreign_currency_code']),
// amount and foreign amount. Cannot be 0.
'amount' => $this->clearString((string)$object['amount']),
'foreign_amount' => $this->clearString((string)$object['foreign_amount']),
// description.
'description' => $this->clearString($object['description']),
// source of transaction. If everything is null, assume cash account.
'source_id' => $this->integerFromValue((string)$object['source_id']),
'source_name' => $this->clearString((string)$object['source_name']),
'source_iban' => $this->clearString((string)$object['source_iban']),
'source_number' => $this->clearString((string)$object['source_number']),
'source_bic' => $this->clearString((string)$object['source_bic']),
// destination of transaction. If everything is null, assume cash account.
'destination_id' => $this->integerFromValue((string)$object['destination_id']),
'destination_name' => $this->clearString((string)$object['destination_name']),
'destination_iban' => $this->clearString((string)$object['destination_iban']),
'destination_number' => $this->clearString((string)$object['destination_number']),
'destination_bic' => $this->clearString((string)$object['destination_bic']),
// budget info
'budget_id' => $this->integerFromValue((string)$object['budget_id']),
'budget_name' => $this->clearString((string)$object['budget_name']),
// category info
'category_id' => $this->integerFromValue((string)$object['category_id']),
'category_name' => $this->clearString((string)$object['category_name']),
// journal bill reference. Optional. Will only work for withdrawals
'bill_id' => $this->integerFromValue((string)$object['bill_id']),
'bill_name' => $this->clearString((string)$object['bill_name']),
// piggy bank reference. Optional. Will only work for transfers
'piggy_bank_id' => $this->integerFromValue((string)$object['piggy_bank_id']),
'piggy_bank_name' => $this->clearString((string)$object['piggy_bank_name']),
// some other interesting properties
'reconciled' => $this->convertBoolean((string)$object['reconciled']),
'notes' => $this->clearStringKeepNewlines((string)$object['notes']),
'tags' => $this->arrayFromValue($object['tags']),
// all custom fields:
'internal_reference' => $this->clearString((string)$object['internal_reference']),
'external_id' => $this->clearString((string)$object['external_id']),
'original_source' => sprintf('ff3-v%s|api-v%s', config('firefly.version'), config('firefly.api_version')),
'recurrence_id' => $this->integerFromValue($object['recurrence_id']),
'bunq_payment_id' => $this->clearString((string)$object['bunq_payment_id']),
'external_url' => $this->clearString((string)$object['external_url']),
'sepa_cc' => $this->clearString((string)$object['sepa_cc']),
'sepa_ct_op' => $this->clearString((string)$object['sepa_ct_op']),
'sepa_ct_id' => $this->clearString((string)$object['sepa_ct_id']),
'sepa_db' => $this->clearString((string)$object['sepa_db']),
'sepa_country' => $this->clearString((string)$object['sepa_country']),
'sepa_ep' => $this->clearString((string)$object['sepa_ep']),
'sepa_ci' => $this->clearString((string)$object['sepa_ci']),
'sepa_batch_id' => $this->clearString((string)$object['sepa_batch_id']),
2023-12-20 19:35:52 +01:00
// custom date fields. Must be Carbon objects. Presence is optional.
'interest_date' => $this->dateFromValue($object['interest_date']),
'book_date' => $this->dateFromValue($object['book_date']),
'process_date' => $this->dateFromValue($object['process_date']),
'due_date' => $this->dateFromValue($object['due_date']),
'payment_date' => $this->dateFromValue($object['payment_date']),
'invoice_date' => $this->dateFromValue($object['invoice_date']),
2023-12-20 19:35:52 +01:00
];
}
return $return;
}
2018-03-05 19:35:58 +01:00
}