mirror of
https://github.com/firefly-iii/firefly-iii.git
synced 2025-10-12 15:35:15 +00:00
Merge branch 'release/v6.1.5'
This commit is contained in:
13
.ci/php-cs-fixer/composer.lock
generated
13
.ci/php-cs-fixer/composer.lock
generated
@@ -226,21 +226,22 @@
|
||||
},
|
||||
{
|
||||
"name": "friendsofphp/php-cs-fixer",
|
||||
"version": "v3.45.0",
|
||||
"version": "v3.46.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer.git",
|
||||
"reference": "c0daa33cb2533cd73f48dde1c70c2afa3e7953b5"
|
||||
"reference": "be6831c9af1740470d2a773119b9273f8ac1c3d2"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/PHP-CS-Fixer/PHP-CS-Fixer/zipball/c0daa33cb2533cd73f48dde1c70c2afa3e7953b5",
|
||||
"reference": "c0daa33cb2533cd73f48dde1c70c2afa3e7953b5",
|
||||
"url": "https://api.github.com/repos/PHP-CS-Fixer/PHP-CS-Fixer/zipball/be6831c9af1740470d2a773119b9273f8ac1c3d2",
|
||||
"reference": "be6831c9af1740470d2a773119b9273f8ac1c3d2",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"composer/semver": "^3.4",
|
||||
"composer/xdebug-handler": "^3.0.3",
|
||||
"ext-filter": "*",
|
||||
"ext-json": "*",
|
||||
"ext-tokenizer": "*",
|
||||
"php": "^7.4 || ^8.0",
|
||||
@@ -304,7 +305,7 @@
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/issues",
|
||||
"source": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/tree/v3.45.0"
|
||||
"source": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/tree/v3.46.0"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@@ -312,7 +313,7 @@
|
||||
"type": "github"
|
||||
}
|
||||
],
|
||||
"time": "2023-12-30T02:07:07+00:00"
|
||||
"time": "2024-01-03T21:38:46+00:00"
|
||||
},
|
||||
{
|
||||
"name": "psr/container",
|
||||
|
@@ -78,7 +78,7 @@ PAPERTRAIL_HOST=
|
||||
PAPERTRAIL_PORT=
|
||||
|
||||
# Database credentials. Make sure the database exists. I recommend a dedicated user for Firefly III
|
||||
# For other database types, please see the FAQ: https://docs.firefly-iii.org/firefly-iii/faq/self-hosted/#i-want-to-use-sqlite
|
||||
# For other database types, please see the FAQ: https://docs.firefly-iii.org/references/faq/install/#i-want-to-use-sqlite
|
||||
# If you use Docker or similar, you can set these variables from a file by appending them with _FILE
|
||||
# Use "pgsql" for PostgreSQL
|
||||
# Use "mysql" for MySQL and MariaDB.
|
||||
@@ -150,7 +150,7 @@ COOKIE_SECURE=false
|
||||
COOKIE_SAMESITE=lax
|
||||
|
||||
# If you want Firefly III to email you, update these settings
|
||||
# For instructions, see: https://docs.firefly-iii.org/firefly-iii/advanced-installation/email/#email
|
||||
# For instructions, see: https://docs.firefly-iii.org/how-to/firefly-iii/advanced/notifications/#email
|
||||
# If you use Docker or similar, you can set these variables from a file by appending them with _FILE
|
||||
MAIL_MAILER=log
|
||||
MAIL_HOST=null
|
||||
@@ -214,7 +214,7 @@ VALID_URL_PROTOCOLS=
|
||||
# - 'web' (default, uses built in DB)
|
||||
# - 'remote_user_guard' for Authelia etc
|
||||
# Read more about these settings in the documentation.
|
||||
# https://docs.firefly-iii.org/firefly-iii/advanced-installation/authentication
|
||||
# https://docs.firefly-iii.org/how-to/firefly-iii/advanced/authentication/
|
||||
#
|
||||
# LDAP is no longer supported :(
|
||||
#
|
||||
@@ -269,7 +269,7 @@ ALLOW_WEBHOOKS=false
|
||||
# 1. Set this token to any 32-character value (this is important!).
|
||||
# 2. Use this token in the cron URL instead of a user's command line token that you can find in /profile
|
||||
#
|
||||
# For more info: https://docs.firefly-iii.org/firefly-iii/advanced-installation/cron/
|
||||
# For more info: https://docs.firefly-iii.org/how-to/firefly-iii/advanced/cron/
|
||||
#
|
||||
# You can set this variable from a file by appending it with _FILE
|
||||
#
|
||||
|
1
.gitignore
vendored
1
.gitignore
vendored
@@ -1,6 +1,7 @@
|
||||
/node_modules
|
||||
/storage/*.key
|
||||
/vendor
|
||||
public/hot
|
||||
npm-debug.log
|
||||
yarn-error.log
|
||||
.env
|
||||
|
@@ -45,6 +45,7 @@ use FireflyIII\Repositories\Tag\TagRepositoryInterface;
|
||||
use FireflyIII\Services\Internal\Destroy\AccountDestroyService;
|
||||
use FireflyIII\Services\Internal\Destroy\JournalDestroyService;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
|
||||
/**
|
||||
* Class DestroyController
|
||||
@@ -175,12 +176,14 @@ class DestroyController extends Controller
|
||||
$count = $account->transactions()->count();
|
||||
if (true === $this->unused && 0 === $count) {
|
||||
app('log')->info(sprintf('Deleted unused account #%d "%s"', $account->id, $account->name));
|
||||
Log::channel('audit')->info(sprintf('Deleted unused account #%d "%s"', $account->id, $account->name));
|
||||
$service->destroy($account, null);
|
||||
|
||||
continue;
|
||||
}
|
||||
if (false === $this->unused) {
|
||||
app('log')->info(sprintf('Deleting account #%d "%s"', $account->id, $account->name));
|
||||
Log::channel('audit')->info(sprintf('Deleted account #%d "%s"', $account->id, $account->name));
|
||||
$service->destroy($account, null);
|
||||
}
|
||||
}
|
||||
|
@@ -59,9 +59,7 @@ class MoveTransactionsRequest extends FormRequest
|
||||
|
||||
/**
|
||||
* Configure the validator instance with special rules for after the basic validation rules.
|
||||
*
|
||||
* @param validator $validator
|
||||
* TODO this is duplicate
|
||||
* TODO this is duplicate.
|
||||
*/
|
||||
public function withValidator(Validator $validator): void
|
||||
{
|
||||
@@ -81,12 +79,12 @@ class MoveTransactionsRequest extends FormRequest
|
||||
$data = $validator->getData();
|
||||
$repository = app(AccountRepositoryInterface::class);
|
||||
$repository->setUser(auth()->user());
|
||||
$original = $repository->find((int)$data['original_account']);
|
||||
$destination = $repository->find((int)$data['destination_account']);
|
||||
$original = $repository->find((int) $data['original_account']);
|
||||
$destination = $repository->find((int) $data['destination_account']);
|
||||
|
||||
// not the same type:
|
||||
if ($original->accountType->type !== $destination->accountType->type) {
|
||||
$validator->errors()->add('title', (string)trans('validation.same_account_type'));
|
||||
$validator->errors()->add('title', (string) trans('validation.same_account_type'));
|
||||
|
||||
return;
|
||||
}
|
||||
@@ -96,7 +94,7 @@ class MoveTransactionsRequest extends FormRequest
|
||||
|
||||
// check different scenario's.
|
||||
if (null === $originalCurrency xor null === $destinationCurrency) {
|
||||
$validator->errors()->add('title', (string)trans('validation.same_account_currency'));
|
||||
$validator->errors()->add('title', (string) trans('validation.same_account_currency'));
|
||||
|
||||
return;
|
||||
}
|
||||
@@ -105,7 +103,7 @@ class MoveTransactionsRequest extends FormRequest
|
||||
return;
|
||||
}
|
||||
if ($originalCurrency->code !== $destinationCurrency->code) {
|
||||
$validator->errors()->add('title', (string)trans('validation.same_account_currency'));
|
||||
$validator->errors()->add('title', (string) trans('validation.same_account_currency'));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -101,7 +101,7 @@ class StoreRequest extends FormRequest
|
||||
'type' => 'required|max:1024|min:1|'.sprintf('in:%s', $types),
|
||||
'iban' => ['iban', 'nullable', new UniqueIban(null, $type)],
|
||||
'bic' => 'bic|nullable',
|
||||
'account_number' => ['between:1,255', 'nullable', new UniqueAccountNumber(null, $type)],
|
||||
'account_number' => ['min:1', 'max:255', 'nullable', new UniqueAccountNumber(null, $type)],
|
||||
'opening_balance' => 'numeric|required_with:opening_balance_date|nullable',
|
||||
'opening_balance_date' => 'date|required_with:opening_balance|nullable',
|
||||
'virtual_balance' => 'numeric|nullable',
|
||||
@@ -117,7 +117,7 @@ class StoreRequest extends FormRequest
|
||||
'liability_amount' => ['required_with:liability_start_date', new IsValidPositiveAmount()],
|
||||
'liability_start_date' => 'required_with:liability_amount|date',
|
||||
'liability_direction' => 'nullable|required_if:type,liability|required_if:type,liabilities|in:credit,debit',
|
||||
'interest' => 'between:0,100|numeric',
|
||||
'interest' => 'min:0|max:100|numeric',
|
||||
'interest_period' => sprintf('nullable|in:%s', implode(',', config('firefly.interest_periods'))),
|
||||
'notes' => 'min:0|max:65536',
|
||||
];
|
||||
|
@@ -91,7 +91,7 @@ class UpdateRequest extends FormRequest
|
||||
'type' => sprintf('in:%s', $types),
|
||||
'iban' => ['iban', 'nullable', new UniqueIban($account, $this->convertString('type'))],
|
||||
'bic' => 'bic|nullable',
|
||||
'account_number' => ['between:1,255', 'nullable', new UniqueAccountNumber($account, $this->convertString('type'))],
|
||||
'account_number' => ['min:1', 'max:255', 'nullable', new UniqueAccountNumber($account, $this->convertString('type'))],
|
||||
'opening_balance' => 'numeric|required_with:opening_balance_date|nullable',
|
||||
'opening_balance_date' => 'date|required_with:opening_balance|nullable',
|
||||
'virtual_balance' => 'numeric|nullable',
|
||||
@@ -105,7 +105,7 @@ class UpdateRequest extends FormRequest
|
||||
'monthly_payment_date' => 'date|nullable|required_if:account_role,ccAsset|required_if:credit_card_type,monthlyFull',
|
||||
'liability_type' => 'required_if:type,liability|in:loan,debt,mortgage',
|
||||
'liability_direction' => 'required_if:type,liability|in:credit,debit',
|
||||
'interest' => 'required_if:type,liability|between:0,100|numeric',
|
||||
'interest' => 'required_if:type,liability|min:0|max:100|numeric',
|
||||
'interest_period' => 'required_if:type,liability|in:daily,monthly,yearly',
|
||||
'notes' => 'min:0|max:65536',
|
||||
];
|
||||
|
@@ -66,9 +66,9 @@ class StoreRequest extends FormRequest
|
||||
$model = $this->convertString('attachable_type');
|
||||
|
||||
return [
|
||||
'filename' => 'required|between:1,255',
|
||||
'title' => 'between:1,255',
|
||||
'notes' => 'between:1,65000',
|
||||
'filename' => 'required|min:1|max:255',
|
||||
'title' => ['min:1', 'max:255'],
|
||||
'notes' => 'min:1|max:32768',
|
||||
'attachable_type' => sprintf('required|in:%s', $models),
|
||||
'attachable_id' => ['required', 'numeric', new IsValidAttachmentModel($model)],
|
||||
];
|
||||
|
@@ -68,9 +68,9 @@ class UpdateRequest extends FormRequest
|
||||
$model = $this->convertString('attachable_type');
|
||||
|
||||
return [
|
||||
'filename' => 'between:1,255',
|
||||
'title' => 'between:1,255',
|
||||
'notes' => 'between:1,65000',
|
||||
'filename' => ['min:1', 'max:255'],
|
||||
'title' => ['min:1', 'max:255'],
|
||||
'notes' => 'min:1|max:32768',
|
||||
'attachable_type' => sprintf('in:%s', $models),
|
||||
'attachable_id' => ['numeric', new IsValidAttachmentModel($model)],
|
||||
];
|
||||
|
@@ -72,7 +72,7 @@ class StoreRequest extends FormRequest
|
||||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
'name' => 'between:1,255|uniqueObjectForUser:bills,name',
|
||||
'name' => 'min:1|max:255|uniqueObjectForUser:bills,name',
|
||||
'amount_min' => ['required', new IsValidPositiveAmount()],
|
||||
'amount_max' => ['required', new IsValidPositiveAmount()],
|
||||
'currency_id' => 'numeric|exists:transaction_currencies,id',
|
||||
@@ -81,9 +81,9 @@ class StoreRequest extends FormRequest
|
||||
'end_date' => 'date|after:date',
|
||||
'extension_date' => 'date|after:date',
|
||||
'repeat_freq' => 'in:weekly,monthly,quarterly,half-year,yearly|required',
|
||||
'skip' => 'between:0,31',
|
||||
'skip' => 'min:0|max:31|numeric',
|
||||
'active' => [new IsBoolean()],
|
||||
'notes' => 'between:1,65536',
|
||||
'notes' => 'min:1|max:32768',
|
||||
];
|
||||
}
|
||||
|
||||
|
@@ -75,7 +75,7 @@ class UpdateRequest extends FormRequest
|
||||
$bill = $this->route()->parameter('bill');
|
||||
|
||||
return [
|
||||
'name' => sprintf('between:1,255|uniqueObjectForUser:bills,name,%d', $bill->id),
|
||||
'name' => sprintf('min:1|max:255|uniqueObjectForUser:bills,name,%d', $bill->id),
|
||||
'amount_min' => ['nullable', new IsValidPositiveAmount()],
|
||||
'amount_max' => ['nullable', new IsValidPositiveAmount()],
|
||||
'currency_id' => 'numeric|exists:transaction_currencies,id',
|
||||
@@ -84,9 +84,9 @@ class UpdateRequest extends FormRequest
|
||||
'end_date' => 'date|after:date',
|
||||
'extension_date' => 'date|after:date',
|
||||
'repeat_freq' => 'in:weekly,monthly,quarterly,half-year,yearly',
|
||||
'skip' => 'between:0,31',
|
||||
'skip' => 'min:0|max:31|numeric',
|
||||
'active' => [new IsBoolean()],
|
||||
'notes' => 'between:1,65536',
|
||||
'notes' => 'min:1|max:32768',
|
||||
];
|
||||
}
|
||||
|
||||
|
@@ -68,11 +68,11 @@ class StoreRequest extends FormRequest
|
||||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
'name' => 'required|between:1,100|uniqueObjectForUser:budgets,name',
|
||||
'name' => 'required|min:1|max:255|uniqueObjectForUser:budgets,name',
|
||||
'active' => [new IsBoolean()],
|
||||
'currency_id' => 'exists:transaction_currencies,id',
|
||||
'currency_code' => 'exists:transaction_currencies,code',
|
||||
'notes' => 'nullable|between:1,65536',
|
||||
'notes' => 'nullable|min:1|max:32768',
|
||||
// auto budget info
|
||||
'auto_budget_type' => 'in:reset,rollover,adjusted,none',
|
||||
'auto_budget_amount' => ['required_if:auto_budget_type,reset', 'required_if:auto_budget_type,rollover', 'required_if:auto_budget_type,adjusted', new IsValidPositiveAmount()],
|
||||
|
@@ -81,9 +81,9 @@ class UpdateRequest extends FormRequest
|
||||
$budget = $this->route()->parameter('budget');
|
||||
|
||||
return [
|
||||
'name' => sprintf('between:1,100|uniqueObjectForUser:budgets,name,%d', $budget->id),
|
||||
'name' => sprintf('min:1|max:100|uniqueObjectForUser:budgets,name,%d', $budget->id),
|
||||
'active' => [new IsBoolean()],
|
||||
'notes' => 'nullable|between:1,65536',
|
||||
'notes' => 'nullable|min:1|max:32768',
|
||||
'auto_budget_type' => 'in:reset,rollover,adjusted,none',
|
||||
'auto_budget_currency_id' => 'exists:transaction_currencies,id',
|
||||
'auto_budget_currency_code' => 'exists:transaction_currencies,code',
|
||||
|
@@ -70,9 +70,7 @@ class UpdateRequest extends FormRequest
|
||||
|
||||
/**
|
||||
* Configure the validator instance with special rules for after the basic validation rules.
|
||||
*
|
||||
* @param Validator $validator
|
||||
* TODO duplicate code
|
||||
* TODO duplicate code.
|
||||
*/
|
||||
public function withValidator(Validator $validator): void
|
||||
{
|
||||
@@ -84,7 +82,7 @@ class UpdateRequest extends FormRequest
|
||||
$start = new Carbon($data['start']);
|
||||
$end = new Carbon($data['end']);
|
||||
if ($end->isBefore($start)) {
|
||||
$validator->errors()->add('end', (string)trans('validation.date_after'));
|
||||
$validator->errors()->add('end', (string) trans('validation.date_after'));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -52,7 +52,7 @@ class StoreRequest extends FormRequest
|
||||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
'name' => 'required|between:1,100|uniqueObjectForUser:categories,name',
|
||||
'name' => 'required|min:1|max:100|uniqueObjectForUser:categories,name',
|
||||
];
|
||||
}
|
||||
}
|
||||
|
@@ -58,7 +58,7 @@ class UpdateRequest extends FormRequest
|
||||
$category = $this->route()->parameter('category');
|
||||
|
||||
return [
|
||||
'name' => sprintf('between:1,100|uniqueObjectForUser:categories,name,%d', $category->id),
|
||||
'name' => sprintf('min:1|max:100|uniqueObjectForUser:categories,name,%d', $category->id),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
@@ -64,11 +64,11 @@ class StoreRequest extends FormRequest
|
||||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
'name' => 'required|between:1,255|uniquePiggyBankForUser',
|
||||
'name' => 'required|min:1|max:255|uniquePiggyBankForUser',
|
||||
'current_amount' => ['nullable', new IsValidPositiveAmount()],
|
||||
'account_id' => 'required|numeric|belongsToUser:accounts,id',
|
||||
'object_group_id' => 'numeric|belongsToUser:object_groups,id',
|
||||
'object_group_title' => 'between:1,255',
|
||||
'object_group_title' => ['min:1', 'max:255'],
|
||||
'target_amount' => ['required', new IsValidPositiveAmount()],
|
||||
'start_date' => 'date|nullable',
|
||||
'target_date' => 'date|nullable|after:start_date',
|
||||
|
@@ -69,7 +69,7 @@ class UpdateRequest extends FormRequest
|
||||
$piggyBank = $this->route()->parameter('piggyBank');
|
||||
|
||||
return [
|
||||
'name' => 'between:1,255|uniquePiggyBankForUser:'.$piggyBank->id,
|
||||
'name' => 'min:1|max:255|uniquePiggyBankForUser:'.$piggyBank->id,
|
||||
'current_amount' => ['nullable', new LessThanPiggyTarget(), new IsValidPositiveAmount()],
|
||||
'target_amount' => ['nullable', new IsValidPositiveAmount()],
|
||||
'start_date' => 'date|nullable',
|
||||
|
@@ -79,20 +79,20 @@ class StoreRequest extends FormRequest
|
||||
{
|
||||
return [
|
||||
'type' => 'required|in:withdrawal,transfer,deposit',
|
||||
'title' => 'required|between:1,255|uniqueObjectForUser:recurrences,title',
|
||||
'description' => 'between:1,65000',
|
||||
'title' => 'required|min:1|max:255|uniqueObjectForUser:recurrences,title',
|
||||
'description' => 'min:1|max:32768',
|
||||
'first_date' => 'required|date',
|
||||
'apply_rules' => [new IsBoolean()],
|
||||
'active' => [new IsBoolean()],
|
||||
'repeat_until' => 'nullable|date',
|
||||
'nr_of_repetitions' => 'nullable|numeric|between:1,31',
|
||||
'nr_of_repetitions' => 'nullable|numeric|min:1|max:31',
|
||||
|
||||
'repetitions.*.type' => 'required|in:daily,weekly,ndom,monthly,yearly',
|
||||
'repetitions.*.moment' => 'between:0,10',
|
||||
'repetitions.*.skip' => 'nullable|numeric|between:0,31',
|
||||
'repetitions.*.moment' => 'min:0|max:10',
|
||||
'repetitions.*.skip' => 'nullable|numeric|min:0|max:31',
|
||||
'repetitions.*.weekend' => 'numeric|min:1|max:4',
|
||||
|
||||
'transactions.*.description' => 'required|between:1,255',
|
||||
'transactions.*.description' => 'required|min:1|max:255',
|
||||
'transactions.*.amount' => ['required', new IsValidPositiveAmount()],
|
||||
'transactions.*.foreign_amount' => ['nullable', new IsValidPositiveAmount()],
|
||||
'transactions.*.currency_id' => 'nullable|numeric|exists:transaction_currencies,id',
|
||||
@@ -100,18 +100,18 @@ class StoreRequest extends FormRequest
|
||||
'transactions.*.foreign_currency_id' => 'nullable|numeric|exists:transaction_currencies,id',
|
||||
'transactions.*.foreign_currency_code' => 'nullable|min:3|max:51|exists:transaction_currencies,code',
|
||||
'transactions.*.source_id' => ['numeric', 'nullable', new BelongsUser()],
|
||||
'transactions.*.source_name' => 'between:1,255|nullable',
|
||||
'transactions.*.source_name' => 'min:1|max:255|nullable',
|
||||
'transactions.*.destination_id' => ['numeric', 'nullable', new BelongsUser()],
|
||||
'transactions.*.destination_name' => 'between:1,255|nullable',
|
||||
'transactions.*.destination_name' => 'min:1|max:255|nullable',
|
||||
|
||||
// new and updated fields:
|
||||
'transactions.*.budget_id' => ['nullable', 'mustExist:budgets,id', new BelongsUser()],
|
||||
'transactions.*.budget_name' => ['between:1,255', 'nullable', new BelongsUser()],
|
||||
'transactions.*.budget_name' => ['min:1', 'max:255', 'nullable', new BelongsUser()],
|
||||
'transactions.*.category_id' => ['nullable', 'mustExist:categories,id', new BelongsUser()],
|
||||
'transactions.*.category_name' => 'between:1,255|nullable',
|
||||
'transactions.*.category_name' => 'min:1|max:255|nullable',
|
||||
'transactions.*.piggy_bank_id' => ['nullable', 'numeric', 'mustExist:piggy_banks,id', new BelongsUser()],
|
||||
'transactions.*.piggy_bank_name' => ['between:1,255', 'nullable', new BelongsUser()],
|
||||
'transactions.*.tags' => 'nullable|between:1,255',
|
||||
'transactions.*.piggy_bank_name' => ['min:1', 'max:255', 'nullable', new BelongsUser()],
|
||||
'transactions.*.tags' => 'nullable|min:1|max:255',
|
||||
];
|
||||
}
|
||||
|
||||
|
@@ -86,20 +86,20 @@ class UpdateRequest extends FormRequest
|
||||
$recurrence = $this->route()->parameter('recurrence');
|
||||
|
||||
return [
|
||||
'title' => sprintf('between:1,255|uniqueObjectForUser:recurrences,title,%d', $recurrence->id),
|
||||
'description' => 'between:1,65000',
|
||||
'title' => sprintf('min:1|max:255|uniqueObjectForUser:recurrences,title,%d', $recurrence->id),
|
||||
'description' => 'min:1|max:32768',
|
||||
'first_date' => 'date',
|
||||
'apply_rules' => [new IsBoolean()],
|
||||
'active' => [new IsBoolean()],
|
||||
'repeat_until' => 'nullable|date',
|
||||
'nr_of_repetitions' => 'nullable|numeric|between:1,31',
|
||||
'nr_of_repetitions' => 'nullable|numeric|min:1|max:31',
|
||||
|
||||
'repetitions.*.type' => 'in:daily,weekly,ndom,monthly,yearly',
|
||||
'repetitions.*.moment' => 'between:0,10',
|
||||
'repetitions.*.skip' => 'nullable|numeric|between:0,31',
|
||||
'repetitions.*.moment' => 'min:0|max:10|numeric',
|
||||
'repetitions.*.skip' => 'nullable|numeric|min:0|max:31',
|
||||
'repetitions.*.weekend' => 'nullable|numeric|min:1|max:4',
|
||||
|
||||
'transactions.*.description' => 'between:1,255',
|
||||
'transactions.*.description' => ['min:1', 'max:255'],
|
||||
'transactions.*.amount' => [new IsValidPositiveAmount()],
|
||||
'transactions.*.foreign_amount' => ['nullable', new IsValidPositiveAmount()],
|
||||
'transactions.*.currency_id' => 'nullable|numeric|exists:transaction_currencies,id',
|
||||
@@ -107,18 +107,18 @@ class UpdateRequest extends FormRequest
|
||||
'transactions.*.foreign_currency_id' => 'nullable|numeric|exists:transaction_currencies,id',
|
||||
'transactions.*.foreign_currency_code' => 'nullable|min:3|max:51|exists:transaction_currencies,code',
|
||||
'transactions.*.source_id' => ['numeric', 'nullable', new BelongsUser()],
|
||||
'transactions.*.source_name' => 'between:1,255|nullable',
|
||||
'transactions.*.source_name' => 'min:1|max:255|nullable',
|
||||
'transactions.*.destination_id' => ['numeric', 'nullable', new BelongsUser()],
|
||||
'transactions.*.destination_name' => 'between:1,255|nullable',
|
||||
'transactions.*.destination_name' => 'min:1|max:255|nullable',
|
||||
|
||||
// new and updated fields:
|
||||
'transactions.*.budget_id' => ['nullable', 'mustExist:budgets,id', new BelongsUser()],
|
||||
'transactions.*.budget_name' => ['between:1,255', 'nullable', new BelongsUser()],
|
||||
'transactions.*.budget_name' => ['min:1', 'max:255', 'nullable', new BelongsUser()],
|
||||
'transactions.*.category_id' => ['nullable', 'mustExist:categories,id', new BelongsUser()],
|
||||
'transactions.*.category_name' => 'between:1,255|nullable',
|
||||
'transactions.*.category_name' => 'min:1|max:255|nullable',
|
||||
'transactions.*.piggy_bank_id' => ['nullable', 'numeric', 'mustExist:piggy_banks,id', new BelongsUser()],
|
||||
'transactions.*.piggy_bank_name' => ['between:1,255', 'nullable', new BelongsUser()],
|
||||
'transactions.*.tags' => 'nullable|between:1,255',
|
||||
'transactions.*.piggy_bank_name' => ['min:1', 'max:255', 'nullable', new BelongsUser()],
|
||||
'transactions.*.tags' => 'nullable|min:1|max:255',
|
||||
];
|
||||
}
|
||||
|
||||
|
@@ -76,10 +76,10 @@ class StoreRequest extends FormRequest
|
||||
$contextActions = implode(',', config('firefly.context-rule-actions'));
|
||||
|
||||
return [
|
||||
'title' => 'required|between:1,100|uniqueObjectForUser:rules,title',
|
||||
'description' => 'between:1,5000|nullable',
|
||||
'title' => 'required|min:1|max:100|uniqueObjectForUser:rules,title',
|
||||
'description' => 'min:1|max:32768|nullable',
|
||||
'rule_group_id' => 'belongsToUser:rule_groups|required_without:rule_group_title',
|
||||
'rule_group_title' => 'nullable|between:1,255|required_without:rule_group_id|belongsToUser:rule_groups,title',
|
||||
'rule_group_title' => 'nullable|min:1|max:255|required_without:rule_group_id|belongsToUser:rule_groups,title',
|
||||
'trigger' => 'required|in:store-journal,update-journal',
|
||||
'triggers.*.type' => 'required|in:'.implode(',', $validTriggers),
|
||||
'triggers.*.value' => 'required_if:actions.*.type,'.$contextTriggers.'|min:1|ruleTriggerValue|max:1024',
|
||||
|
@@ -85,10 +85,10 @@ class UpdateRequest extends FormRequest
|
||||
$contextActions = implode(',', config('firefly.context-rule-actions'));
|
||||
|
||||
return [
|
||||
'title' => sprintf('between:1,100|uniqueObjectForUser:rules,title,%d', $rule->id),
|
||||
'description' => 'between:1,5000|nullable',
|
||||
'title' => sprintf('min:1|max:100|uniqueObjectForUser:rules,title,%d', $rule->id),
|
||||
'description' => 'min:1|max:32768|nullable',
|
||||
'rule_group_id' => 'belongsToUser:rule_groups',
|
||||
'rule_group_title' => 'nullable|between:1,255|belongsToUser:rule_groups,title',
|
||||
'rule_group_title' => 'nullable|min:1|max:255|belongsToUser:rule_groups,title',
|
||||
'trigger' => 'in:store-journal,update-journal',
|
||||
'triggers.*.type' => 'required|in:'.implode(',', $validTriggers),
|
||||
'triggers.*.value' => 'required_if:actions.*.type,'.$contextTriggers.'|min:1|ruleTriggerValue|max:1024',
|
||||
@@ -101,7 +101,7 @@ class UpdateRequest extends FormRequest
|
||||
'strict' => [new IsBoolean()],
|
||||
'stop_processing' => [new IsBoolean()],
|
||||
'active' => [new IsBoolean()],
|
||||
'order' => 'numeric|between:1,1337',
|
||||
'order' => 'numeric|min:1|max:2048',
|
||||
];
|
||||
}
|
||||
|
||||
|
@@ -64,8 +64,8 @@ class StoreRequest extends FormRequest
|
||||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
'title' => 'required|between:1,100|uniqueObjectForUser:rule_groups,title',
|
||||
'description' => 'between:1,5000|nullable',
|
||||
'title' => 'required|min:1|max:100|uniqueObjectForUser:rule_groups,title',
|
||||
'description' => 'min:1|max:32768|nullable',
|
||||
'active' => [new IsBoolean()],
|
||||
];
|
||||
}
|
||||
|
@@ -62,8 +62,8 @@ class UpdateRequest extends FormRequest
|
||||
$ruleGroup = $this->route()->parameter('ruleGroup');
|
||||
|
||||
return [
|
||||
'title' => 'between:1,100|uniqueObjectForUser:rule_groups,title,'.$ruleGroup->id,
|
||||
'description' => 'between:1,5000|nullable',
|
||||
'title' => 'min:1|max:100|uniqueObjectForUser:rule_groups,title,'.$ruleGroup->id,
|
||||
'description' => 'min:1|max:32768|nullable',
|
||||
'active' => [new IsBoolean()],
|
||||
];
|
||||
}
|
||||
|
@@ -28,6 +28,7 @@ use FireflyIII\Rules\BelongsUser;
|
||||
use FireflyIII\Rules\IsBoolean;
|
||||
use FireflyIII\Rules\IsDateOrTime;
|
||||
use FireflyIII\Rules\IsValidPositiveAmount;
|
||||
use FireflyIII\Rules\IsValidZeroOrMoreAmount;
|
||||
use FireflyIII\Support\NullArrayObject;
|
||||
use FireflyIII\Support\Request\AppendsLocationData;
|
||||
use FireflyIII\Support\Request\ChecksLogin;
|
||||
@@ -77,7 +78,7 @@ class StoreRequest extends FormRequest
|
||||
|
||||
return [
|
||||
// basic fields for group:
|
||||
'group_title' => 'between:1,1000|nullable',
|
||||
'group_title' => 'min:1|max:1000|nullable',
|
||||
'error_if_duplicate_hash' => [new IsBoolean()],
|
||||
'apply_rules' => [new IsBoolean()],
|
||||
|
||||
@@ -94,40 +95,40 @@ class StoreRequest extends FormRequest
|
||||
|
||||
// amount
|
||||
'transactions.*.amount' => ['required', new IsValidPositiveAmount()],
|
||||
'transactions.*.foreign_amount' => ['nullable', new IsValidPositiveAmount()],
|
||||
'transactions.*.foreign_amount' => ['nullable', new IsValidZeroOrMoreAmount()],
|
||||
|
||||
// description
|
||||
'transactions.*.description' => 'nullable|between:1,1000',
|
||||
'transactions.*.description' => 'nullable|min:1|max: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',
|
||||
'transactions.*.source_name' => 'min:1|max:255|nullable',
|
||||
'transactions.*.source_iban' => 'min:1|max:255|nullable|iban',
|
||||
'transactions.*.source_number' => 'min:1|max:255|nullable',
|
||||
'transactions.*.source_bic' => 'min:1|max: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',
|
||||
'transactions.*.destination_name' => 'min:1|max:255|nullable',
|
||||
'transactions.*.destination_iban' => 'min:1|max:255|nullable|iban',
|
||||
'transactions.*.destination_number' => 'min:1|max:255|nullable',
|
||||
'transactions.*.destination_bic' => 'min:1|max: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.*.budget_name' => ['min:1', 'max:255', 'nullable', new BelongsUser()],
|
||||
'transactions.*.category_id' => ['mustExist:categories,id', new BelongsUser(), 'nullable'],
|
||||
'transactions.*.category_name' => 'between:1,255|nullable',
|
||||
'transactions.*.category_name' => 'min:1|max:255|nullable',
|
||||
'transactions.*.bill_id' => ['numeric', 'nullable', 'mustExist:bills,id', new BelongsUser()],
|
||||
'transactions.*.bill_name' => ['between:1,255', 'nullable', new BelongsUser()],
|
||||
'transactions.*.bill_name' => ['min:1', 'max: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()],
|
||||
'transactions.*.piggy_bank_name' => ['min:1', 'max:255', 'nullable', new BelongsUser()],
|
||||
|
||||
// other interesting fields
|
||||
'transactions.*.reconciled' => [new IsBoolean()],
|
||||
'transactions.*.notes' => 'min:1|max:50000|nullable',
|
||||
'transactions.*.tags' => 'between:0,255',
|
||||
'transactions.*.tags.*' => 'between:0,255',
|
||||
'transactions.*.notes' => 'min:1|max:32768|nullable',
|
||||
'transactions.*.tags' => 'min:0|max:255',
|
||||
'transactions.*.tags.*' => 'min:0|max:255',
|
||||
|
||||
// meta info fields
|
||||
'transactions.*.internal_reference' => 'min:1|max:255|nullable',
|
||||
|
@@ -99,7 +99,7 @@ class UpdateRequest extends FormRequest
|
||||
|
||||
return [
|
||||
// basic fields for group:
|
||||
'group_title' => 'between:1,1000|nullable',
|
||||
'group_title' => 'min:1|max:1000|nullable',
|
||||
'apply_rules' => [new IsBoolean()],
|
||||
|
||||
// transaction rules (in array for splits):
|
||||
@@ -121,29 +121,29 @@ class UpdateRequest extends FormRequest
|
||||
'transactions.*.foreign_amount' => ['nullable', new IsValidZeroOrMoreAmount()],
|
||||
|
||||
// description
|
||||
'transactions.*.description' => 'nullable|between:1,1000',
|
||||
'transactions.*.description' => 'nullable|min:1|max:1000',
|
||||
|
||||
// source of transaction
|
||||
'transactions.*.source_id' => ['numeric', 'nullable', new BelongsUser()],
|
||||
'transactions.*.source_name' => 'between:1,255|nullable',
|
||||
'transactions.*.source_name' => 'min:1|max:255|nullable',
|
||||
|
||||
// destination of transaction
|
||||
'transactions.*.destination_id' => ['numeric', 'nullable', new BelongsUser()],
|
||||
'transactions.*.destination_name' => 'between:1,255|nullable',
|
||||
'transactions.*.destination_name' => 'min:1|max:255|nullable',
|
||||
|
||||
// budget, category, bill and piggy
|
||||
'transactions.*.budget_id' => ['mustExist:budgets,id', new BelongsUser(), 'nullable'],
|
||||
'transactions.*.budget_name' => ['between:1,255', 'nullable', new BelongsUser()],
|
||||
'transactions.*.budget_name' => ['min:1', 'max:255', 'nullable', new BelongsUser()],
|
||||
'transactions.*.category_id' => ['mustExist:categories,id', new BelongsUser(), 'nullable'],
|
||||
'transactions.*.category_name' => 'between:1,255|nullable',
|
||||
'transactions.*.category_name' => 'min:1|max:255|nullable',
|
||||
'transactions.*.bill_id' => ['numeric', 'nullable', 'mustExist:bills,id', new BelongsUser()],
|
||||
'transactions.*.bill_name' => ['between:1,255', 'nullable', new BelongsUser()],
|
||||
'transactions.*.bill_name' => ['min:1', 'max:255', 'nullable', new BelongsUser()],
|
||||
|
||||
// other interesting fields
|
||||
'transactions.*.reconciled' => [new IsBoolean()],
|
||||
'transactions.*.notes' => 'min:1|max:50000|nullable',
|
||||
'transactions.*.tags' => 'between:0,255|nullable',
|
||||
'transactions.*.tags.*' => 'between:0,255',
|
||||
'transactions.*.notes' => 'min:1|max:32768|nullable',
|
||||
'transactions.*.tags' => 'min:0|max:255|nullable',
|
||||
'transactions.*.tags.*' => 'min:0|max:255',
|
||||
|
||||
// meta info fields
|
||||
'transactions.*.internal_reference' => 'min:1|max:255|nullable',
|
||||
|
@@ -66,10 +66,10 @@ class StoreRequest extends FormRequest
|
||||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
'name' => 'required|between:1,255|unique:transaction_currencies,name',
|
||||
'code' => 'required|between:3,51|unique:transaction_currencies,code',
|
||||
'symbol' => 'required|between:1,51|unique:transaction_currencies,symbol',
|
||||
'decimal_places' => 'between:0,20|numeric|min:0|max:12',
|
||||
'name' => 'required|min:1|max:255|unique:transaction_currencies,name',
|
||||
'code' => 'required|min:3|max:32|unique:transaction_currencies,code',
|
||||
'symbol' => 'required|min:1|max:32|unique:transaction_currencies,symbol',
|
||||
'decimal_places' => 'numeric|min:0|max:12',
|
||||
'enabled' => [new IsBoolean()],
|
||||
'default' => [new IsBoolean()],
|
||||
];
|
||||
|
@@ -64,10 +64,10 @@ class UpdateRequest extends FormRequest
|
||||
$currency = $this->route()->parameter('currency_code');
|
||||
|
||||
return [
|
||||
'name' => sprintf('between:1,255|unique:transaction_currencies,name,%d', $currency->id),
|
||||
'code' => sprintf('between:3,51|unique:transaction_currencies,code,%d', $currency->id),
|
||||
'symbol' => sprintf('between:1,51|unique:transaction_currencies,symbol,%d', $currency->id),
|
||||
'decimal_places' => 'between:0,20|numeric|min:0|max:12',
|
||||
'name' => sprintf('min:1|max:255|unique:transaction_currencies,name,%d', $currency->id),
|
||||
'code' => sprintf('min:3|max:32|unique:transaction_currencies,code,%d', $currency->id),
|
||||
'symbol' => sprintf('min:1|max:32|unique:transaction_currencies,symbol,%d', $currency->id),
|
||||
'decimal_places' => 'numeric|min:0|max:12',
|
||||
'enabled' => [new IsBoolean()],
|
||||
'default' => [new IsBoolean()],
|
||||
];
|
||||
|
@@ -63,7 +63,7 @@ class StoreRequest extends FormRequest
|
||||
'link_type_name' => 'exists:link_types,name|required_without:link_type_id',
|
||||
'inward_id' => 'required|belongsToUser:transaction_journals,id|different:outward_id',
|
||||
'outward_id' => 'required|belongsToUser:transaction_journals,id|different:inward_id',
|
||||
'notes' => 'between:0,65000',
|
||||
'notes' => 'min:1|max:32768|nullable',
|
||||
];
|
||||
}
|
||||
|
||||
|
@@ -63,7 +63,7 @@ class UpdateRequest extends FormRequest
|
||||
'link_type_name' => 'exists:link_types,name',
|
||||
'inward_id' => 'belongsToUser:transaction_journals,id|different:outward_id',
|
||||
'outward_id' => 'belongsToUser:transaction_journals,id|different:inward_id',
|
||||
'notes' => 'between:0,65000',
|
||||
'notes' => 'min:1|max:32768|nullable',
|
||||
];
|
||||
}
|
||||
|
||||
|
@@ -72,7 +72,7 @@ class CreateRequest extends FormRequest
|
||||
$validProtocols = config('firefly.valid_url_protocols');
|
||||
|
||||
return [
|
||||
'title' => 'required|between:1,512|uniqueObjectForUser:webhooks,title',
|
||||
'title' => 'required|min:1|max:255|uniqueObjectForUser:webhooks,title',
|
||||
'active' => [new IsBoolean()],
|
||||
'trigger' => sprintf('required|in:%s', $triggers),
|
||||
'response' => sprintf('required|in:%s', $responses),
|
||||
|
@@ -85,7 +85,7 @@ class UpdateRequest extends FormRequest
|
||||
$webhook = $this->route()->parameter('webhook');
|
||||
|
||||
return [
|
||||
'title' => sprintf('between:1,512|uniqueObjectForUser:webhooks,title,%d', $webhook->id),
|
||||
'title' => sprintf('min:1|max:255|uniqueObjectForUser:webhooks,title,%d', $webhook->id),
|
||||
'active' => [new IsBoolean()],
|
||||
'trigger' => sprintf('in:%s', $triggers),
|
||||
'response' => sprintf('in:%s', $responses),
|
||||
|
@@ -65,7 +65,7 @@ class UpdateRequest extends FormRequest
|
||||
return ['value' => ['required', new IsBoolean()]];
|
||||
}
|
||||
if ('configuration.permission_update_check' === $name) {
|
||||
return ['value' => 'required|numeric|between:-1,1'];
|
||||
return ['value' => 'required|numeric|min:-1|max:1'];
|
||||
}
|
||||
if ('configuration.last_update_check' === $name) {
|
||||
return ['value' => 'required|numeric|min:464272080'];
|
||||
|
@@ -77,11 +77,7 @@ class BudgetController extends Controller
|
||||
}
|
||||
|
||||
/**
|
||||
* @param DateRequest $request
|
||||
*
|
||||
* TODO see autocomplete/accountcontroller
|
||||
*
|
||||
* @throws FireflyException
|
||||
*/
|
||||
public function dashboard(DateRequest $request): JsonResponse
|
||||
{
|
||||
|
43
app/Api/V2/Controllers/Model/Transaction/ShowController.php
Normal file
43
app/Api/V2/Controllers/Model/Transaction/ShowController.php
Normal file
@@ -0,0 +1,43 @@
|
||||
<?php
|
||||
/*
|
||||
* ShowController.php
|
||||
* Copyright (c) 2024 james@firefly-iii.org
|
||||
*
|
||||
* This file is part of Firefly III (https://github.com/firefly-iii).
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\Api\V2\Controllers\Model\Transaction;
|
||||
|
||||
use FireflyIII\Api\V2\Controllers\Controller;
|
||||
use FireflyIII\Models\TransactionGroup;
|
||||
use FireflyIII\Transformers\V2\TransactionGroupTransformer;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
|
||||
class ShowController extends Controller
|
||||
{
|
||||
/**
|
||||
* TODO this endpoint is not yet reachable.
|
||||
*/
|
||||
public function show(TransactionGroup $transactionGroup): JsonResponse
|
||||
{
|
||||
$transformer = new TransactionGroupTransformer();
|
||||
$transformer->setParameters($this->parameters);
|
||||
|
||||
return response()->api($this->jsonApiObject('transactions', $transactionGroup, $transformer))->header('Content-Type', self::CONTENT_TYPE);
|
||||
}
|
||||
}
|
@@ -28,6 +28,7 @@ use FireflyIII\Models\UserGroup;
|
||||
use FireflyIII\Rules\BelongsUserGroup;
|
||||
use FireflyIII\Rules\IsBoolean;
|
||||
use FireflyIII\Rules\IsDateOrTime;
|
||||
use FireflyIII\Rules\IsValidPositiveAmount;
|
||||
use FireflyIII\Support\NullArrayObject;
|
||||
use FireflyIII\Support\Request\AppendsLocationData;
|
||||
use FireflyIII\Support\Request\ChecksLogin;
|
||||
@@ -74,7 +75,6 @@ class StoreRequest extends FormRequest
|
||||
'fire_webhooks' => $this->boolean('fire_webhooks', true),
|
||||
'transactions' => $this->getTransactionData(),
|
||||
];
|
||||
// TODO include location and ability to process it.
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -91,7 +91,7 @@ class StoreRequest extends FormRequest
|
||||
|
||||
return [
|
||||
// basic fields for group:
|
||||
'group_title' => 'between:1,1000|nullable',
|
||||
'group_title' => 'min:1|max:1000|nullable',
|
||||
'error_if_duplicate_hash' => [new IsBoolean()],
|
||||
'apply_rules' => [new IsBoolean()],
|
||||
|
||||
@@ -107,40 +107,41 @@ class StoreRequest extends FormRequest
|
||||
'transactions.*.foreign_currency_code' => 'min:3|max:51|exists:transaction_currencies,code|nullable',
|
||||
|
||||
// amount
|
||||
'transactions.*.amount' => 'required|numeric|gt:0|max:1000000000',
|
||||
'transactions.*.foreign_amount' => 'numeric|gt:0|max:1000000000',
|
||||
'transactions.*.amount' => ['required', new IsValidPositiveAmount()],
|
||||
'transactions.*.foreign_amount' => ['nullable', new IsValidPositiveAmount()],
|
||||
|
||||
// description
|
||||
'transactions.*.description' => 'nullable|between:1,1000',
|
||||
'transactions.*.description' => 'nullable|min:1|max:1000',
|
||||
|
||||
// source of transaction
|
||||
'transactions.*.source_id' => ['numeric', 'nullable', new BelongsUserGroup($userGroup)],
|
||||
'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',
|
||||
'transactions.*.source_name' => 'min:1|max:255|nullable',
|
||||
'transactions.*.source_iban' => 'min:1|max:255|nullable|iban',
|
||||
'transactions.*.source_number' => 'min:1|max:255|nullable',
|
||||
'transactions.*.source_bic' => 'min:1|max:255|nullable|bic',
|
||||
|
||||
// destination of transaction
|
||||
'transactions.*.destination_id' => ['numeric', 'nullable', new BelongsUserGroup($userGroup)],
|
||||
'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',
|
||||
'transactions.*.destination_name' => 'min:1|max:255|nullable',
|
||||
'transactions.*.destination_iban' => 'min:1|max:255|nullable|iban',
|
||||
'transactions.*.destination_number' => 'min:1|max:255|nullable',
|
||||
'transactions.*.destination_bic' => 'min:1|max:255|nullable|bic',
|
||||
|
||||
// budget, category, bill and piggy
|
||||
'transactions.*.budget_id' => ['mustExist:budgets,id', new BelongsUserGroup($userGroup)],
|
||||
'transactions.*.budget_name' => ['between:1,255', 'nullable', new BelongsUserGroup($userGroup)],
|
||||
'transactions.*.budget_name' => ['min:1', 'max:255', 'nullable', new BelongsUserGroup($userGroup)],
|
||||
'transactions.*.category_id' => ['mustExist:categories,id', new BelongsUserGroup($userGroup), 'nullable'],
|
||||
'transactions.*.category_name' => 'between:1,255|nullable',
|
||||
'transactions.*.category_name' => 'min:1|max:255|nullable',
|
||||
'transactions.*.bill_id' => ['numeric', 'nullable', 'mustExist:bills,id', new BelongsUserGroup($userGroup)],
|
||||
'transactions.*.bill_name' => ['between:1,255', 'nullable', new BelongsUserGroup($userGroup)],
|
||||
'transactions.*.bill_name' => ['min:1', 'max:255', 'nullable', new BelongsUserGroup($userGroup)],
|
||||
'transactions.*.piggy_bank_id' => ['numeric', 'nullable', 'mustExist:piggy_banks,id', new BelongsUserGroup($userGroup)],
|
||||
'transactions.*.piggy_bank_name' => ['between:1,255', 'nullable', new BelongsUserGroup($userGroup)],
|
||||
'transactions.*.piggy_bank_name' => ['min:1', 'max:255', 'nullable', new BelongsUserGroup($userGroup)],
|
||||
|
||||
// other interesting fields
|
||||
'transactions.*.reconciled' => [new IsBoolean()],
|
||||
'transactions.*.notes' => 'min:1|max:50000|nullable',
|
||||
'transactions.*.tags' => 'between:0,255',
|
||||
'transactions.*.notes' => 'min:1|max:32768|nullable',
|
||||
'transactions.*.tags' => 'min:0|max:255',
|
||||
'transactions.*.tags.*' => 'min:0|max:255',
|
||||
|
||||
// meta info fields
|
||||
'transactions.*.internal_reference' => 'min:1|max:255|nullable',
|
||||
@@ -166,6 +167,8 @@ class StoreRequest extends FormRequest
|
||||
'transactions.*.due_date' => 'date|nullable',
|
||||
'transactions.*.payment_date' => 'date|nullable',
|
||||
'transactions.*.invoice_date' => 'date|nullable',
|
||||
|
||||
// TODO include location and ability to process it.
|
||||
];
|
||||
}
|
||||
|
||||
@@ -222,7 +225,7 @@ class StoreRequest extends FormRequest
|
||||
*/
|
||||
foreach ($this->get('transactions') as $transaction) {
|
||||
$object = new NullArrayObject($transaction);
|
||||
$return[] = [
|
||||
$result = [
|
||||
'type' => $this->clearString($object['type']),
|
||||
'date' => $this->dateFromValue($object['date']),
|
||||
'order' => $this->integerFromValue((string)$object['order']),
|
||||
@@ -300,6 +303,8 @@ class StoreRequest extends FormRequest
|
||||
'payment_date' => $this->dateFromValue($object['payment_date']),
|
||||
'invoice_date' => $this->dateFromValue($object['invoice_date']),
|
||||
];
|
||||
$result = $this->addFromromTransactionStore($transaction, $result);
|
||||
$return[] = $result;
|
||||
}
|
||||
|
||||
return $return;
|
||||
|
@@ -49,7 +49,7 @@ class StoreRequest extends FormRequest
|
||||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
'title' => 'unique:user_groups,title|required|min:2|max:255',
|
||||
'title' => 'unique:user_groups,title|required|min:1|max:255',
|
||||
];
|
||||
}
|
||||
}
|
||||
|
@@ -53,7 +53,7 @@ class UpdateRequest extends FormRequest
|
||||
$userGroup = $this->route()->parameter('userGroup');
|
||||
|
||||
return [
|
||||
'title' => sprintf('required|min:2|max:255|unique:user_groups,title,%d', $userGroup->id),
|
||||
'title' => sprintf('required|min:1|max:255|unique:user_groups,title,%d', $userGroup->id),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
@@ -216,6 +216,7 @@ class Handler extends ExceptionHandler
|
||||
'json' => request()->acceptsJson(),
|
||||
'method' => request()->method(),
|
||||
'headers' => $headers,
|
||||
'post' => 'POST' === request()->method() ? json_encode(request()->all()) : '',
|
||||
];
|
||||
|
||||
// create job that will mail.
|
||||
|
@@ -28,6 +28,7 @@ use Carbon\Carbon;
|
||||
use FireflyIII\Exceptions\DuplicateTransactionException;
|
||||
use FireflyIII\Exceptions\FireflyException;
|
||||
use FireflyIII\Models\Account;
|
||||
use FireflyIII\Models\Location;
|
||||
use FireflyIII\Models\Transaction;
|
||||
use FireflyIII\Models\TransactionCurrency;
|
||||
use FireflyIII\Models\TransactionJournal;
|
||||
@@ -49,6 +50,8 @@ use Illuminate\Support\Collection;
|
||||
|
||||
/**
|
||||
* Class TransactionJournalFactory
|
||||
*
|
||||
* @SuppressWarnings(PHPMD.CouplingBetweenObjects)
|
||||
*/
|
||||
class TransactionJournalFactory
|
||||
{
|
||||
@@ -318,10 +321,23 @@ class TransactionJournalFactory
|
||||
$this->storePiggyEvent($journal, $row);
|
||||
$this->storeTags($journal, $row['tags']);
|
||||
$this->storeMetaFields($journal, $row);
|
||||
$this->storeLocation($journal, $row);
|
||||
|
||||
return $journal;
|
||||
}
|
||||
|
||||
private function storeLocation(TransactionJournal $journal, NullArrayObject $data): void
|
||||
{
|
||||
if (true === $data['store_location']) {
|
||||
$location = new Location();
|
||||
$location->longitude = $data['longitude'];
|
||||
$location->latitude = $data['latitude'];
|
||||
$location->zoom_level = $data['zoom_level'];
|
||||
$location->locatable()->associate($journal);
|
||||
$location->save();
|
||||
}
|
||||
}
|
||||
|
||||
private function hashArray(NullArrayObject $row): string
|
||||
{
|
||||
$dataRow = $row->getArrayCopy();
|
||||
@@ -360,16 +376,13 @@ class TransactionJournalFactory
|
||||
->where('transaction_journals.user_id', $this->user->id)
|
||||
->where('data', json_encode($hash, JSON_THROW_ON_ERROR))
|
||||
->with(['transactionJournal', 'transactionJournal.transactionGroup'])
|
||||
->first()
|
||||
->first(['journal_meta.*'])
|
||||
;
|
||||
if (null !== $result) {
|
||||
app('log')->warning(sprintf('Found a duplicate in errorIfDuplicate because hash %s is not unique!', $hash));
|
||||
$journal = $result->transactionJournal()->withTrashed()->first();
|
||||
$group = $journal?->transactionGroup()->withTrashed()->first();
|
||||
$groupId = $group?->id;
|
||||
if (null === $group) {
|
||||
$groupId = 0;
|
||||
}
|
||||
$groupId = (int) $group?->id;
|
||||
|
||||
throw new DuplicateTransactionException(sprintf('Duplicate of transaction #%d.', $groupId));
|
||||
}
|
||||
|
@@ -462,7 +462,7 @@ trait TimeCollection
|
||||
*/
|
||||
public function setBefore(Carbon $date): GroupCollectorInterface
|
||||
{
|
||||
$beforeStr = $date->format('Y-m-d 00:00:00');
|
||||
$beforeStr = $date->format('Y-m-d 23:59:59');
|
||||
$this->query->where('transaction_journals.date', '<=', $beforeStr);
|
||||
|
||||
return $this;
|
||||
|
@@ -25,7 +25,6 @@ namespace FireflyIII\Helpers\Collector;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use Carbon\Exceptions\InvalidFormatException;
|
||||
use Exception;
|
||||
use FireflyIII\Exceptions\FireflyException;
|
||||
use FireflyIII\Helpers\Collector\Extensions\AccountCollection;
|
||||
use FireflyIII\Helpers\Collector\Extensions\AmountCollection;
|
||||
@@ -106,6 +105,8 @@ class GroupCollector implements GroupCollectorInterface
|
||||
'transaction_groups.created_at as created_at',
|
||||
'transaction_groups.updated_at as updated_at',
|
||||
'transaction_groups.title as transaction_group_title',
|
||||
'transaction_groups.created_at as group_created_at',
|
||||
'transaction_groups.updated_at as group_updated_at',
|
||||
|
||||
// journal
|
||||
'transaction_journals.id as transaction_journal_id',
|
||||
@@ -285,7 +286,7 @@ class GroupCollector implements GroupCollectorInterface
|
||||
foreach ($params as $param) {
|
||||
$replace = sprintf('"%s"', $param);
|
||||
if (is_int($param)) {
|
||||
$replace = (string)$param;
|
||||
$replace = (string) $param;
|
||||
}
|
||||
$pos = strpos($query, '?');
|
||||
if (false !== $pos) {
|
||||
@@ -691,24 +692,26 @@ class GroupCollector implements GroupCollectorInterface
|
||||
|
||||
/** @var TransactionJournal $augumentedJournal */
|
||||
foreach ($collection as $augumentedJournal) {
|
||||
$groupId = (int)$augumentedJournal->transaction_group_id;
|
||||
$groupId = (int) $augumentedJournal->transaction_group_id;
|
||||
|
||||
if (!array_key_exists($groupId, $groups)) {
|
||||
// make new array
|
||||
$parsedGroup = $this->parseAugmentedJournal($augumentedJournal);
|
||||
$groupArray = [
|
||||
'id' => (int)$augumentedJournal->transaction_group_id,
|
||||
'id' => (int) $augumentedJournal->transaction_group_id,
|
||||
'user_id' => $augumentedJournal->user_id,
|
||||
'user_group_id' => $augumentedJournal->user_group_id,
|
||||
// Field transaction_group_title was added by the query.
|
||||
'title' => $augumentedJournal->transaction_group_title, // @phpstan-ignore-line
|
||||
'created_at' => new Carbon($augumentedJournal->group_created_at, config('app.timezone')),
|
||||
'updated_at' => new Carbon($augumentedJournal->group_updated_at, config('app.timezone')),
|
||||
'transaction_type' => $parsedGroup['transaction_type_type'],
|
||||
'count' => 1,
|
||||
'sums' => [],
|
||||
'transactions' => [],
|
||||
];
|
||||
// Field transaction_journal_id was added by the query.
|
||||
$journalId = (int)$augumentedJournal->transaction_journal_id; // @phpstan-ignore-line
|
||||
$journalId = (int) $augumentedJournal->transaction_journal_id; // @phpstan-ignore-line
|
||||
$groupArray['transactions'][$journalId] = $parsedGroup;
|
||||
$groups[$groupId] = $groupArray;
|
||||
|
||||
@@ -716,7 +719,7 @@ class GroupCollector implements GroupCollectorInterface
|
||||
}
|
||||
// or parse the rest.
|
||||
// Field transaction_journal_id was added by the query.
|
||||
$journalId = (int)$augumentedJournal->transaction_journal_id; // @phpstan-ignore-line
|
||||
$journalId = (int) $augumentedJournal->transaction_journal_id; // @phpstan-ignore-line
|
||||
if (array_key_exists($journalId, $groups[$groupId]['transactions'])) {
|
||||
// append data to existing group + journal (for multiple tags or multiple attachments)
|
||||
$groups[$groupId]['transactions'][$journalId] = $this->mergeTags($groups[$groupId]['transactions'][$journalId], $augumentedJournal);
|
||||
@@ -769,7 +772,7 @@ class GroupCollector implements GroupCollectorInterface
|
||||
$dates = ['interest_date', 'payment_date', 'invoice_date', 'book_date', 'due_date', 'process_date'];
|
||||
if (array_key_exists('meta_name', $result) && in_array($result['meta_name'], $dates, true)) {
|
||||
$name = $result['meta_name'];
|
||||
if (array_key_exists('meta_data', $result) && '' !== (string)$result['meta_data']) {
|
||||
if (array_key_exists('meta_data', $result) && '' !== (string) $result['meta_data']) {
|
||||
$result[$name] = Carbon::createFromFormat('!Y-m-d', substr(json_decode($result['meta_data']), 0, 10));
|
||||
}
|
||||
}
|
||||
@@ -780,9 +783,9 @@ class GroupCollector implements GroupCollectorInterface
|
||||
// convert back to strings because SQLite is dumb like that.
|
||||
$result = $this->convertToStrings($result);
|
||||
|
||||
$result['reconciled'] = 1 === (int)$result['reconciled'];
|
||||
$result['reconciled'] = 1 === (int) $result['reconciled'];
|
||||
if (array_key_exists('tag_id', $result) && null !== $result['tag_id']) { // assume the other fields are present as well.
|
||||
$tagId = (int)$augumentedJournal['tag_id'];
|
||||
$tagId = (int) $augumentedJournal['tag_id'];
|
||||
$tagDate = null;
|
||||
|
||||
try {
|
||||
@@ -792,7 +795,7 @@ class GroupCollector implements GroupCollectorInterface
|
||||
}
|
||||
|
||||
$result['tags'][$tagId] = [
|
||||
'id' => (int)$result['tag_id'],
|
||||
'id' => (int) $result['tag_id'],
|
||||
'name' => $result['tag_name'],
|
||||
'date' => $tagDate,
|
||||
'description' => $result['tag_description'],
|
||||
@@ -801,8 +804,8 @@ class GroupCollector implements GroupCollectorInterface
|
||||
|
||||
// also merge attachments:
|
||||
if (array_key_exists('attachment_id', $result)) {
|
||||
$uploaded = 1 === (int)$result['attachment_uploaded'];
|
||||
$attachmentId = (int)$augumentedJournal['attachment_id'];
|
||||
$uploaded = 1 === (int) $result['attachment_uploaded'];
|
||||
$attachmentId = (int) $augumentedJournal['attachment_id'];
|
||||
if (0 !== $attachmentId && $uploaded) {
|
||||
$result['attachments'][$attachmentId] = [
|
||||
'id' => $attachmentId,
|
||||
@@ -828,7 +831,7 @@ class GroupCollector implements GroupCollectorInterface
|
||||
private function convertToInteger(array $array): array
|
||||
{
|
||||
foreach ($this->integerFields as $field) {
|
||||
$array[$field] = array_key_exists($field, $array) ? (int)$array[$field] : null;
|
||||
$array[$field] = array_key_exists($field, $array) ? (int) $array[$field] : null;
|
||||
}
|
||||
|
||||
return $array;
|
||||
@@ -837,7 +840,7 @@ class GroupCollector implements GroupCollectorInterface
|
||||
private function convertToStrings(array $array): array
|
||||
{
|
||||
foreach ($this->stringFields as $field) {
|
||||
$array[$field] = array_key_exists($field, $array) && null !== $array[$field] ? (string)$array[$field] : null;
|
||||
$array[$field] = array_key_exists($field, $array) && null !== $array[$field] ? (string) $array[$field] : null;
|
||||
}
|
||||
|
||||
return $array;
|
||||
@@ -847,7 +850,7 @@ class GroupCollector implements GroupCollectorInterface
|
||||
{
|
||||
$newArray = $newJournal->toArray();
|
||||
if (array_key_exists('tag_id', $newArray)) { // assume the other fields are present as well.
|
||||
$tagId = (int)$newJournal['tag_id'];
|
||||
$tagId = (int) $newJournal['tag_id'];
|
||||
|
||||
$tagDate = null;
|
||||
|
||||
@@ -858,7 +861,7 @@ class GroupCollector implements GroupCollectorInterface
|
||||
}
|
||||
|
||||
$existingJournal['tags'][$tagId] = [
|
||||
'id' => (int)$newArray['tag_id'],
|
||||
'id' => (int) $newArray['tag_id'],
|
||||
'name' => $newArray['tag_name'],
|
||||
'date' => $tagDate,
|
||||
'description' => $newArray['tag_description'],
|
||||
@@ -872,7 +875,7 @@ class GroupCollector implements GroupCollectorInterface
|
||||
{
|
||||
$newArray = $newJournal->toArray();
|
||||
if (array_key_exists('attachment_id', $newArray)) {
|
||||
$attachmentId = (int)$newJournal['attachment_id'];
|
||||
$attachmentId = (int) $newJournal['attachment_id'];
|
||||
|
||||
$existingJournal['attachments'][$attachmentId] = [
|
||||
'id' => $attachmentId,
|
||||
@@ -891,7 +894,7 @@ class GroupCollector implements GroupCollectorInterface
|
||||
foreach ($groups as $groudId => $group) {
|
||||
/** @var array $transaction */
|
||||
foreach ($group['transactions'] as $transaction) {
|
||||
$currencyId = (int)$transaction['currency_id'];
|
||||
$currencyId = (int) $transaction['currency_id'];
|
||||
if (null === $transaction['amount']) {
|
||||
throw new FireflyException(sprintf('Amount is NULL for a transaction in group #%d, please investigate.', $groudId));
|
||||
}
|
||||
@@ -907,7 +910,7 @@ class GroupCollector implements GroupCollectorInterface
|
||||
$groups[$groudId]['sums'][$currencyId]['amount'] = bcadd($groups[$groudId]['sums'][$currencyId]['amount'], $transaction['amount']);
|
||||
|
||||
if (null !== $transaction['foreign_amount'] && null !== $transaction['foreign_currency_id']) {
|
||||
$currencyId = (int)$transaction['foreign_currency_id'];
|
||||
$currencyId = (int) $transaction['foreign_currency_id'];
|
||||
|
||||
// set default:
|
||||
if (!array_key_exists($currencyId, $groups[$groudId]['sums'])) {
|
||||
@@ -951,11 +954,11 @@ class GroupCollector implements GroupCollectorInterface
|
||||
continue;
|
||||
}
|
||||
// if the result is a bool, use the unedited results.
|
||||
if(true === $result) {
|
||||
if (true === $result) {
|
||||
$nextCollection->push($item);
|
||||
}
|
||||
// if the result is an array, the filter has changed what's being returned.
|
||||
if(is_array($result)) {
|
||||
if (is_array($result)) {
|
||||
$nextCollection->push($result);
|
||||
}
|
||||
}
|
||||
|
@@ -231,6 +231,7 @@ class CreateController extends Controller
|
||||
|
||||
return redirect(route('recurring.create'))->withInput();
|
||||
}
|
||||
Log::channel('audit')->info('Stored new recurrence.', $data);
|
||||
|
||||
$request->session()->flash('success', (string)trans('firefly.stored_new_recurrence', ['title' => $recurrence->title]));
|
||||
app('preferences')->mark();
|
||||
|
@@ -172,6 +172,7 @@ class EditController extends Controller
|
||||
$this->recurring->update($recurrence, $data);
|
||||
|
||||
$request->session()->flash('success', (string)trans('firefly.updated_recurrence', ['title' => $recurrence->title]));
|
||||
Log::channel('audit')->info(sprintf('Updated recurrence #%d.', $recurrence->id), $data);
|
||||
|
||||
// store new attachment(s):
|
||||
/** @var null|array $files */
|
||||
|
@@ -50,7 +50,7 @@ class CreateController extends Controller
|
||||
|
||||
$this->middleware(
|
||||
function ($request, $next) {
|
||||
app('view')->share('title', (string)trans('firefly.transactions'));
|
||||
app('view')->share('title', (string) trans('firefly.transactions'));
|
||||
app('view')->share('mainTitleIcon', 'fa-exchange');
|
||||
$this->repository = app(TransactionGroupRepositoryInterface::class);
|
||||
|
||||
@@ -61,7 +61,7 @@ class CreateController extends Controller
|
||||
|
||||
public function cloneGroup(Request $request): JsonResponse
|
||||
{
|
||||
$groupId = (int)$request->get('id');
|
||||
$groupId = (int) $request->get('id');
|
||||
if (0 !== $groupId) {
|
||||
$group = $this->repository->find($groupId);
|
||||
if (null !== $group) {
|
||||
@@ -101,14 +101,14 @@ class CreateController extends Controller
|
||||
{
|
||||
app('preferences')->mark();
|
||||
|
||||
$sourceId = (int)request()->get('source');
|
||||
$destinationId = (int)request()->get('destination');
|
||||
$sourceId = (int) request()->get('source');
|
||||
$destinationId = (int) request()->get('destination');
|
||||
|
||||
/** @var AccountRepositoryInterface $accountRepository */
|
||||
$accountRepository = app(AccountRepositoryInterface::class);
|
||||
$cash = $accountRepository->getCashAccount();
|
||||
$preFilled = session()->has('preFilled') ? session('preFilled') : [];
|
||||
$subTitle = (string)trans(sprintf('breadcrumbs.create_%s', strtolower((string)$objectType)));
|
||||
$subTitle = (string) trans(sprintf('breadcrumbs.create_%s', strtolower((string) $objectType)));
|
||||
$subTitleIcon = 'fa-plus';
|
||||
$optionalFields = app('preferences')->get('transaction_journal_optional_fields', [])->data;
|
||||
$allowedOpposingTypes = config('firefly.allowed_opposing_types');
|
||||
@@ -118,6 +118,26 @@ class CreateController extends Controller
|
||||
$parts = parse_url($previousUrl);
|
||||
$search = sprintf('?%s', $parts['query'] ?? '');
|
||||
$previousUrl = str_replace($search, '', $previousUrl);
|
||||
if (!is_array($optionalFields)) {
|
||||
$optionalFields = [];
|
||||
}
|
||||
// not really a fan of this, but meh.
|
||||
$optionalDateFields = [
|
||||
'interest_date' => $optionalFields['interest_date'] ?? false,
|
||||
'book_date' => $optionalFields['book_date'] ?? false,
|
||||
'process_date' => $optionalFields['process_date'] ?? false,
|
||||
'due_date' => $optionalFields['due_date'] ?? false,
|
||||
'payment_date' => $optionalFields['payment_date'] ?? false,
|
||||
'invoice_date' => $optionalFields['invoice_date'] ?? false,
|
||||
];
|
||||
$optionalFields['external_url'] ??= false;
|
||||
$optionalFields['location'] ??= false;
|
||||
$optionalFields['location'] = $optionalFields['location'] && true === config('firefly.enable_external_map');
|
||||
|
||||
// map info:
|
||||
$longitude = config('firefly.default_location.longitude');
|
||||
$latitude = config('firefly.default_location.latitude');
|
||||
$zoomLevel = config('firefly.default_location.zoom_level');
|
||||
|
||||
session()->put('preFilled', $preFilled);
|
||||
|
||||
@@ -126,7 +146,11 @@ class CreateController extends Controller
|
||||
compact(
|
||||
'subTitleIcon',
|
||||
'cash',
|
||||
'longitude',
|
||||
'latitude',
|
||||
'zoomLevel',
|
||||
'objectType',
|
||||
'optionalDateFields',
|
||||
'subTitle',
|
||||
'defaultCurrency',
|
||||
'previousUrl',
|
||||
|
@@ -51,7 +51,7 @@ class EditController extends Controller
|
||||
// translations:
|
||||
$this->middleware(
|
||||
function ($request, $next) {
|
||||
app('view')->share('title', (string)trans('firefly.transactions'));
|
||||
app('view')->share('title', (string) trans('firefly.transactions'));
|
||||
app('view')->share('mainTitleIcon', 'fa-exchange');
|
||||
|
||||
$this->repository = app(JournalRepositoryInterface::class);
|
||||
@@ -78,7 +78,9 @@ class EditController extends Controller
|
||||
$accountToTypes = config('firefly.account_to_transaction');
|
||||
$expectedSourceTypes = config('firefly.expected_source_types');
|
||||
$allowedSourceDests = config('firefly.source_dests');
|
||||
|
||||
$title = $transactionGroup->transactionJournals()->count() > 1 ? $transactionGroup->title : $transactionGroup->transactionJournals()->first()->description;
|
||||
$subTitle = (string) trans('firefly.edit_transaction_title', ['description' => $title]);
|
||||
$subTitleIcon = 'fa-plus';
|
||||
$defaultCurrency = app('amount')->getDefaultCurrency();
|
||||
$cash = $repository->getCashAccount();
|
||||
$previousUrl = $this->rememberPreviousUrl('transactions.edit.url');
|
||||
@@ -86,12 +88,42 @@ class EditController extends Controller
|
||||
$search = sprintf('?%s', $parts['query'] ?? '');
|
||||
$previousUrl = str_replace($search, '', $previousUrl);
|
||||
|
||||
// settings necessary for v2
|
||||
$optionalFields = app('preferences')->get('transaction_journal_optional_fields', [])->data;
|
||||
if (!is_array($optionalFields)) {
|
||||
$optionalFields = [];
|
||||
}
|
||||
// not really a fan of this, but meh.
|
||||
$optionalDateFields = [
|
||||
'interest_date' => $optionalFields['interest_date'] ?? false,
|
||||
'book_date' => $optionalFields['book_date'] ?? false,
|
||||
'process_date' => $optionalFields['process_date'] ?? false,
|
||||
'due_date' => $optionalFields['due_date'] ?? false,
|
||||
'payment_date' => $optionalFields['payment_date'] ?? false,
|
||||
'invoice_date' => $optionalFields['invoice_date'] ?? false,
|
||||
];
|
||||
$optionalFields['external_url'] ??= false;
|
||||
$optionalFields['location'] ??= false;
|
||||
$optionalFields['location'] = $optionalFields['location'] && true === config('firefly.enable_external_map');
|
||||
|
||||
// map info:
|
||||
$longitude = config('firefly.default_location.longitude');
|
||||
$latitude = config('firefly.default_location.latitude');
|
||||
$zoomLevel = config('firefly.default_location.zoom_level');
|
||||
|
||||
return view(
|
||||
'transactions.edit',
|
||||
compact(
|
||||
'cash',
|
||||
'allowedSourceDests',
|
||||
'expectedSourceTypes',
|
||||
'optionalDateFields',
|
||||
'longitude',
|
||||
'latitude',
|
||||
'zoomLevel',
|
||||
'optionalFields',
|
||||
'subTitle',
|
||||
'subTitleIcon',
|
||||
'transactionGroup',
|
||||
'allowedOpposingTypes',
|
||||
'accountToTypes',
|
||||
|
@@ -108,7 +108,7 @@ class AccountFormRequest extends FormRequest
|
||||
'BIC' => 'bic|nullable',
|
||||
'virtual_balance' => ['nullable', new IsValidAmount()],
|
||||
'currency_id' => 'exists:transaction_currencies,id',
|
||||
'account_number' => 'between:1,255|uniqueAccountNumberForUser|nullable',
|
||||
'account_number' => 'min:1|max:255|uniqueAccountNumberForUser|nullable',
|
||||
'account_role' => 'in:'.$accountRoles,
|
||||
'active' => 'boolean',
|
||||
'cc_type' => 'in:'.$ccPaymentTypes,
|
||||
@@ -116,7 +116,7 @@ class AccountFormRequest extends FormRequest
|
||||
'amount_currency_id_virtual_balance' => 'exists:transaction_currencies,id',
|
||||
'what' => 'in:'.$types,
|
||||
'interest_period' => 'in:daily,monthly,yearly',
|
||||
'notes' => 'between:1,65536|nullable',
|
||||
'notes' => 'min:1|max:32768|nullable',
|
||||
];
|
||||
$rules = Location::requestRules($rules);
|
||||
|
||||
|
@@ -53,8 +53,8 @@ class AttachmentFormRequest extends FormRequest
|
||||
{
|
||||
// fixed
|
||||
return [
|
||||
'title' => 'between:1,255|nullable',
|
||||
'notes' => 'between:1,65536|nullable',
|
||||
'title' => 'min:1|max:255|nullable',
|
||||
'notes' => 'min:1|max:32768|nullable',
|
||||
];
|
||||
}
|
||||
}
|
||||
|
@@ -64,12 +64,12 @@ class BillStoreRequest extends FormRequest
|
||||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
'name' => 'required|between:1,255|uniqueObjectForUser:bills,name',
|
||||
'name' => 'required|min:1|max:255|uniqueObjectForUser:bills,name',
|
||||
'amount_min' => ['required', new IsValidPositiveAmount()],
|
||||
'amount_max' => ['required', new IsValidPositiveAmount()],
|
||||
'transaction_currency_id' => 'required|exists:transaction_currencies,id',
|
||||
'date' => 'required|date',
|
||||
'notes' => 'between:1,65536|nullable',
|
||||
'notes' => 'min:1|max:32768|nullable',
|
||||
'bill_end_date' => 'nullable|date',
|
||||
'extension_date' => 'nullable|date',
|
||||
'repeat_freq' => sprintf('required|in:%s', implode(',', config('firefly.bill_periods'))),
|
||||
|
@@ -68,7 +68,7 @@ class BillUpdateRequest extends FormRequest
|
||||
$bill = $this->route()->parameter('bill');
|
||||
|
||||
return [
|
||||
'name' => sprintf('required|between:1,255|uniqueObjectForUser:bills,name,%d', $bill->id),
|
||||
'name' => sprintf('required|min:1|max:255|uniqueObjectForUser:bills,name,%d', $bill->id),
|
||||
'amount_min' => ['required', new IsValidPositiveAmount()],
|
||||
'amount_max' => ['required', new IsValidPositiveAmount()],
|
||||
'transaction_currency_id' => 'required|exists:transaction_currencies,id',
|
||||
@@ -78,7 +78,7 @@ class BillUpdateRequest extends FormRequest
|
||||
'repeat_freq' => sprintf('required|in:%s', implode(',', config('firefly.bill_periods'))),
|
||||
'skip' => 'required|integer|gte:0|lte:31',
|
||||
'active' => 'boolean',
|
||||
'notes' => 'between:1,65536|nullable',
|
||||
'notes' => 'min:1|max:32768|nullable',
|
||||
];
|
||||
}
|
||||
}
|
||||
|
@@ -61,13 +61,13 @@ class BudgetFormStoreRequest extends FormRequest
|
||||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
'name' => 'required|between:1,100|uniqueObjectForUser:budgets,name',
|
||||
'active' => 'numeric|between:0,1',
|
||||
'name' => 'required|min:1|max:255|uniqueObjectForUser:budgets,name',
|
||||
'active' => 'numeric|min:0|max:1',
|
||||
'auto_budget_type' => 'numeric|integer|gte:0|lte:3',
|
||||
'auto_budget_currency_id' => 'exists:transaction_currencies,id',
|
||||
'auto_budget_amount' => ['required_if:auto_budget_type,1', 'required_if:auto_budget_type,2', new IsValidPositiveAmount()],
|
||||
'auto_budget_period' => 'in:daily,weekly,monthly,quarterly,half_year,yearly',
|
||||
'notes' => 'between:1,65536|nullable',
|
||||
'notes' => 'min:1|max:32768|nullable',
|
||||
];
|
||||
}
|
||||
|
||||
|
@@ -60,23 +60,23 @@ class BudgetFormUpdateRequest extends FormRequest
|
||||
*/
|
||||
public function rules(): array
|
||||
{
|
||||
$nameRule = 'required|between:1,100|uniqueObjectForUser:budgets,name';
|
||||
$nameRule = 'required|min:1|max:255|uniqueObjectForUser:budgets,name';
|
||||
|
||||
/** @var null|Budget $budget */
|
||||
$budget = $this->route()->parameter('budget');
|
||||
|
||||
if (null !== $budget) {
|
||||
$nameRule = 'required|between:1,100|uniqueObjectForUser:budgets,name,'.$budget->id;
|
||||
$nameRule = 'required|min:1|max:255|uniqueObjectForUser:budgets,name,'.$budget->id;
|
||||
}
|
||||
|
||||
return [
|
||||
'name' => $nameRule,
|
||||
'active' => 'numeric|between:0,1',
|
||||
'active' => 'numeric|min:0|max:1',
|
||||
'auto_budget_type' => 'numeric|integer|gte:0|lte:31',
|
||||
'auto_budget_currency_id' => 'exists:transaction_currencies,id',
|
||||
'auto_budget_amount' => ['required_if:auto_budget_type,1', 'required_if:auto_budget_type,2|numeric', new IsValidPositiveAmount()],
|
||||
'auto_budget_period' => 'in:daily,weekly,monthly,quarterly,half_year,yearly',
|
||||
'notes' => 'between:1,65536|nullable',
|
||||
'notes' => 'min:1|max:32768|nullable',
|
||||
];
|
||||
}
|
||||
|
||||
|
@@ -52,19 +52,19 @@ class CategoryFormRequest extends FormRequest
|
||||
*/
|
||||
public function rules(): array
|
||||
{
|
||||
$nameRule = 'required|between:1,100|uniqueObjectForUser:categories,name';
|
||||
$nameRule = 'required|min:1|max:255|uniqueObjectForUser:categories,name';
|
||||
|
||||
/** @var null|Category $category */
|
||||
$category = $this->route()->parameter('category');
|
||||
|
||||
if (null !== $category) {
|
||||
$nameRule = 'required|between:1,100|uniqueObjectForUser:categories,name,'.$category->id;
|
||||
$nameRule = 'required|min:1|max:255|uniqueObjectForUser:categories,name,'.$category->id;
|
||||
}
|
||||
|
||||
// fixed
|
||||
return [
|
||||
'name' => $nameRule,
|
||||
'notes' => 'between:1,65536|nullable',
|
||||
'notes' => 'min:1|max:32768|nullable',
|
||||
];
|
||||
}
|
||||
}
|
||||
|
@@ -51,8 +51,8 @@ class ConfigurationRequest extends FormRequest
|
||||
{
|
||||
// fixed
|
||||
return [
|
||||
'single_user_mode' => 'between:0,1|numeric',
|
||||
'is_demo_site' => 'between:0,1|numeric',
|
||||
'single_user_mode' => 'min:0|max:1|numeric',
|
||||
'is_demo_site' => 'min:0|max:1|numeric',
|
||||
];
|
||||
}
|
||||
}
|
||||
|
@@ -43,7 +43,7 @@ class NewUserFormRequest extends FormRequest
|
||||
{
|
||||
// fixed
|
||||
return [
|
||||
'bank_name' => 'required|between:1,200',
|
||||
'bank_name' => 'required|min:1|max:255',
|
||||
'bank_balance' => ['required', new IsValidAmount()],
|
||||
'savings_balance' => ['nullable', new IsValidAmount()],
|
||||
'credit_card_limit' => ['nullable', new IsValidAmount()],
|
||||
|
@@ -53,10 +53,10 @@ class ObjectGroupFormRequest extends FormRequest
|
||||
{
|
||||
/** @var null|ObjectGroup $objectGroup */
|
||||
$objectGroup = $this->route()->parameter('objectGroup');
|
||||
$titleRule = 'required|between:1,255|uniqueObjectGroup';
|
||||
$titleRule = 'required|min:1|max:255|uniqueObjectGroup';
|
||||
|
||||
if (null !== $objectGroup) {
|
||||
$titleRule = sprintf('required|between:1,255|uniqueObjectGroup:%d', $objectGroup->id);
|
||||
$titleRule = sprintf('required|min:1|max:255|uniqueObjectGroup:%d', $objectGroup->id);
|
||||
}
|
||||
|
||||
return [
|
||||
|
@@ -58,14 +58,14 @@ class PiggyBankStoreRequest extends FormRequest
|
||||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
'name' => 'required|between:1,255|uniquePiggyBankForUser',
|
||||
'name' => 'required|min:1|max:255|uniquePiggyBankForUser',
|
||||
'account_id' => 'required|belongsToUser:accounts',
|
||||
'targetamount' => ['nullable', new IsValidPositiveAmount()],
|
||||
'startdate' => 'date',
|
||||
'targetdate' => 'date|nullable',
|
||||
'order' => 'integer|min:1',
|
||||
'object_group' => 'min:0|max:255',
|
||||
'notes' => 'between:1,65536|nullable',
|
||||
'notes' => 'min:1|max:32768|nullable',
|
||||
];
|
||||
}
|
||||
}
|
||||
|
@@ -62,14 +62,14 @@ class PiggyBankUpdateRequest extends FormRequest
|
||||
$piggy = $this->route()->parameter('piggyBank');
|
||||
|
||||
return [
|
||||
'name' => sprintf('required|between:1,255|uniquePiggyBankForUser:%d', $piggy->id),
|
||||
'name' => sprintf('required|min:1|max:255|uniquePiggyBankForUser:%d', $piggy->id),
|
||||
'account_id' => 'required|belongsToUser:accounts',
|
||||
'targetamount' => ['nullable', new IsValidPositiveAmount()],
|
||||
'startdate' => 'date',
|
||||
'targetdate' => 'date|nullable',
|
||||
'order' => 'integer|max:65536|min:1',
|
||||
'object_group' => 'min:0|max:255',
|
||||
'notes' => 'between:1,65536|nullable',
|
||||
'notes' => 'min:1|max:32768|nullable',
|
||||
];
|
||||
}
|
||||
}
|
||||
|
@@ -156,28 +156,29 @@ class RecurrenceFormRequest extends FormRequest
|
||||
{
|
||||
$today = today(config('app.timezone'));
|
||||
$tomorrow = today(config('app.timezone'))->addDay();
|
||||
$before = today(config('app.timezone'))->addYears(25);
|
||||
$rules = [
|
||||
// mandatory info for recurrence.
|
||||
'title' => 'required|between:1,255|uniqueObjectForUser:recurrences,title',
|
||||
'first_date' => 'required|date|after:'.$today->format('Y-m-d'),
|
||||
'repetition_type' => ['required', new ValidRecurrenceRepetitionValue(), new ValidRecurrenceRepetitionType(), 'between:1,20'],
|
||||
'title' => 'required|min:1|max:255|uniqueObjectForUser:recurrences,title',
|
||||
'first_date' => sprintf('required|date|before:%s|after:%s', $before->format('Y-m-d'), $today->format('Y-m-d')),
|
||||
'repetition_type' => ['required', new ValidRecurrenceRepetitionValue(), new ValidRecurrenceRepetitionType(), 'min:1', 'max:32'],
|
||||
'skip' => 'required|numeric|integer|gte:0|lte:31',
|
||||
'notes' => 'between:1,65536|nullable',
|
||||
'notes' => 'min:1|max:32768|nullable',
|
||||
// optional for recurrence:
|
||||
'recurring_description' => 'between:0,65000',
|
||||
'active' => 'numeric|between:0,1',
|
||||
'apply_rules' => 'numeric|between:0,1',
|
||||
'recurring_description' => 'min:0|max:32768',
|
||||
'active' => 'numeric|min:0|max:1',
|
||||
'apply_rules' => 'numeric|min:0|max:1',
|
||||
|
||||
// mandatory for transaction:
|
||||
'transaction_description' => 'required|between:1,255',
|
||||
'transaction_description' => 'required|min:1|max:255',
|
||||
'transaction_type' => 'required|in:withdrawal,deposit,transfer',
|
||||
'transaction_currency_id' => 'required|exists:transaction_currencies,id',
|
||||
'amount' => ['required', new IsValidPositiveAmount()],
|
||||
// mandatory account info:
|
||||
'source_id' => 'numeric|belongsToUser:accounts,id|nullable',
|
||||
'source_name' => 'between:1,255|nullable',
|
||||
'source_name' => 'min:1|max:255|nullable',
|
||||
'destination_id' => 'numeric|belongsToUser:accounts,id|nullable',
|
||||
'destination_name' => 'between:1,255|nullable',
|
||||
'destination_name' => 'min:1|max:255|nullable',
|
||||
|
||||
// foreign amount data:
|
||||
'foreign_amount' => ['nullable', new IsValidPositiveAmount()],
|
||||
@@ -185,8 +186,8 @@ class RecurrenceFormRequest extends FormRequest
|
||||
// optional fields:
|
||||
'budget_id' => 'mustExist:budgets,id|belongsToUser:budgets,id|nullable',
|
||||
'bill_id' => 'mustExist:bills,id|belongsToUser:bills,id|nullable',
|
||||
'category' => 'between:1,255|nullable',
|
||||
'tags' => 'between:1,255|nullable',
|
||||
'category' => 'min:1|max:255|nullable',
|
||||
'tags' => 'min:1|max:255|nullable',
|
||||
];
|
||||
if ($this->convertInteger('foreign_currency_id') > 0) {
|
||||
$rules['foreign_currency_id'] = 'exists:transaction_currencies,id';
|
||||
@@ -194,7 +195,7 @@ class RecurrenceFormRequest extends FormRequest
|
||||
|
||||
// if ends after X repetitions, set another rule
|
||||
if ('times' === $this->convertString('repetition_end')) {
|
||||
$rules['repetitions'] = 'required|numeric|between:0,254';
|
||||
$rules['repetitions'] = 'required|numeric|min:0|max:255';
|
||||
}
|
||||
// if foreign amount, currency must be different.
|
||||
if (null !== $this->convertFloat('foreign_amount')) { // intentional float, used because it defaults to null.
|
||||
@@ -210,10 +211,10 @@ class RecurrenceFormRequest extends FormRequest
|
||||
$type = strtolower($this->convertString('transaction_type'));
|
||||
if (strtolower(TransactionType::WITHDRAWAL) === $type) {
|
||||
$rules['source_id'] = 'required|exists:accounts,id|belongsToUser:accounts';
|
||||
$rules['destination_name'] = 'between:1,255|nullable';
|
||||
$rules['destination_name'] = 'min:1|max:255|nullable';
|
||||
}
|
||||
if (strtolower(TransactionType::DEPOSIT) === $type) {
|
||||
$rules['source_name'] = 'between:1,255|nullable';
|
||||
$rules['source_name'] = 'min:1|max:255|nullable';
|
||||
$rules['destination_id'] = 'required|exists:accounts,id|belongsToUser:accounts';
|
||||
}
|
||||
if (strtolower(TransactionType::TRANSFER) === $type) {
|
||||
@@ -227,7 +228,7 @@ class RecurrenceFormRequest extends FormRequest
|
||||
$recurrence = $this->route()->parameter('recurrence');
|
||||
if ($recurrence instanceof Recurrence) {
|
||||
$rules['id'] = 'required|numeric|exists:recurrences,id';
|
||||
$rules['title'] = 'required|between:1,255|uniqueObjectForUser:recurrences,title,'.$recurrence->id;
|
||||
$rules['title'] = 'required|min:1|max:255|uniqueObjectForUser:recurrences,title,'.$recurrence->id;
|
||||
$rules['first_date'] = 'required|date';
|
||||
}
|
||||
|
||||
@@ -279,12 +280,12 @@ class RecurrenceFormRequest extends FormRequest
|
||||
if ('deposit' === $type) {
|
||||
$throwError = false;
|
||||
$sourceId = (int) $data['deposit_source_id'];
|
||||
$destinationId = (int) $data['destination_id'];
|
||||
$destinationId = (int) ($data['destination_id'] ?? 0);
|
||||
}
|
||||
if ('transfer' === $type) {
|
||||
$throwError = false;
|
||||
$sourceId = (int) $data['source_id'];
|
||||
$destinationId = (int) $data['destination_id'];
|
||||
$destinationId = (int) ($data['destination_id'] ?? 0);
|
||||
}
|
||||
if (true === $throwError) {
|
||||
throw new FireflyException(sprintf('Cannot handle transaction type "%s"', $this->convertString('transaction_type')));
|
||||
|
@@ -97,8 +97,8 @@ class RuleFormRequest extends FormRequest
|
||||
|
||||
// initial set of rules:
|
||||
$rules = [
|
||||
'title' => 'required|between:1,100|uniqueObjectForUser:rules,title',
|
||||
'description' => 'between:1,5000|nullable',
|
||||
'title' => 'required|min:1|max:255|uniqueObjectForUser:rules,title',
|
||||
'description' => 'min:1|max:32768|nullable',
|
||||
'stop_processing' => 'boolean',
|
||||
'rule_group_id' => 'required|belongsToUser:rule_groups',
|
||||
'trigger' => 'required|in:store-journal,update-journal',
|
||||
@@ -113,7 +113,7 @@ class RuleFormRequest extends FormRequest
|
||||
$rule = $this->route()->parameter('rule');
|
||||
|
||||
if (null !== $rule) {
|
||||
$rules['title'] = 'required|between:1,100|uniqueObjectForUser:rules,title,'.$rule->id;
|
||||
$rules['title'] = 'required|min:1|max:255|uniqueObjectForUser:rules,title,'.$rule->id;
|
||||
}
|
||||
|
||||
return $rules;
|
||||
|
@@ -59,18 +59,18 @@ class RuleGroupFormRequest extends FormRequest
|
||||
*/
|
||||
public function rules(): array
|
||||
{
|
||||
$titleRule = 'required|between:1,100|uniqueObjectForUser:rule_groups,title';
|
||||
$titleRule = 'required|min:1|max:255|uniqueObjectForUser:rule_groups,title';
|
||||
|
||||
/** @var null|RuleGroup $ruleGroup */
|
||||
$ruleGroup = $this->route()->parameter('ruleGroup');
|
||||
|
||||
if (null !== $ruleGroup) {
|
||||
$titleRule = 'required|between:1,100|uniqueObjectForUser:rule_groups,title,'.$ruleGroup->id;
|
||||
$titleRule = 'required|min:1|max:255|uniqueObjectForUser:rule_groups,title,'.$ruleGroup->id;
|
||||
}
|
||||
|
||||
return [
|
||||
'title' => $titleRule,
|
||||
'description' => 'between:1,5000|nullable',
|
||||
'description' => 'min:1|max:32768|nullable',
|
||||
'active' => [new IsBoolean()],
|
||||
];
|
||||
}
|
||||
|
@@ -58,9 +58,9 @@ class UserFormRequest extends FormRequest
|
||||
'id' => 'required|exists:users,id',
|
||||
'email' => 'email|required',
|
||||
'password' => 'confirmed|secure_password',
|
||||
'blocked_code' => 'between:0,30|nullable',
|
||||
'blocked' => 'between:0,1|numeric',
|
||||
'is_owner' => 'between:0,1|numeric',
|
||||
'blocked_code' => 'min:0|max:32|nullable',
|
||||
'blocked' => 'min:0|max:1|numeric',
|
||||
'is_owner' => 'min:0|max:1|numeric',
|
||||
];
|
||||
}
|
||||
}
|
||||
|
@@ -93,12 +93,14 @@ class Location extends Model
|
||||
return $rules;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all the accounts.
|
||||
*/
|
||||
public function accounts(): MorphMany
|
||||
{
|
||||
return $this->morphMany(Account::class, 'noteable');
|
||||
return $this->morphMany(Account::class, 'locatable');
|
||||
}
|
||||
|
||||
public function transactionJournals(): MorphMany
|
||||
{
|
||||
return $this->morphMany(TransactionJournal::class, 'locatable');
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -389,7 +389,7 @@ class AccountRepository implements AccountRepositoryInterface
|
||||
if (!in_array($type, $list, true)) {
|
||||
return null;
|
||||
}
|
||||
$currencyId = (int)$this->getMetaValue($account, 'currency_id');
|
||||
$currencyId = (int) $this->getMetaValue($account, 'currency_id');
|
||||
if ($currencyId > 0) {
|
||||
return TransactionCurrency::find($currencyId);
|
||||
}
|
||||
@@ -411,7 +411,7 @@ class AccountRepository implements AccountRepositoryInterface
|
||||
return null;
|
||||
}
|
||||
if (1 === $result->count()) {
|
||||
return (string)$result->first()->data;
|
||||
return (string) $result->first()->data;
|
||||
}
|
||||
|
||||
return null;
|
||||
@@ -432,8 +432,8 @@ class AccountRepository implements AccountRepositoryInterface
|
||||
$info = $account->transactions()->get(['transaction_currency_id', 'foreign_currency_id'])->toArray();
|
||||
$currencyIds = [];
|
||||
foreach ($info as $entry) {
|
||||
$currencyIds[] = (int)$entry['transaction_currency_id'];
|
||||
$currencyIds[] = (int)$entry['foreign_currency_id'];
|
||||
$currencyIds[] = (int) $entry['transaction_currency_id'];
|
||||
$currencyIds[] = (int) $entry['foreign_currency_id'];
|
||||
}
|
||||
$currencyIds = array_unique($currencyIds);
|
||||
|
||||
@@ -456,14 +456,14 @@ class AccountRepository implements AccountRepositoryInterface
|
||||
AccountType::MORTGAGE => [AccountType::LOAN, AccountType::DEBT, AccountType::CREDITCARD, AccountType::MORTGAGE],
|
||||
];
|
||||
if (array_key_exists(ucfirst($type), $sets)) {
|
||||
$order = (int)$this->getAccountsByType($sets[ucfirst($type)])->max('order');
|
||||
$order = (int) $this->getAccountsByType($sets[ucfirst($type)])->max('order');
|
||||
app('log')->debug(sprintf('Return max order of "%s" set: %d', $type, $order));
|
||||
|
||||
return $order;
|
||||
}
|
||||
$specials = [AccountType::CASH, AccountType::INITIAL_BALANCE, AccountType::IMPORT, AccountType::RECONCILIATION];
|
||||
|
||||
$order = (int)$this->getAccountsByType($specials)->max('order');
|
||||
$order = (int) $this->getAccountsByType($specials)->max('order');
|
||||
app('log')->debug(sprintf('Return max order of "%s" set (specials!): %d', $type, $order));
|
||||
|
||||
return $order;
|
||||
@@ -544,7 +544,7 @@ class AccountRepository implements AccountRepositoryInterface
|
||||
|
||||
continue;
|
||||
}
|
||||
if ($index !== (int)$account->order) {
|
||||
if ($index !== (int) $account->order) {
|
||||
app('log')->debug(sprintf('Account #%d ("%s"): order should %d be but is %d.', $account->id, $account->name, $index, $account->order));
|
||||
$account->order = $index;
|
||||
$account->save();
|
||||
|
@@ -42,6 +42,7 @@ use Illuminate\Contracts\Auth\Authenticatable;
|
||||
use Illuminate\Database\Query\JoinClause;
|
||||
use Illuminate\Pagination\LengthAwarePaginator;
|
||||
use Illuminate\Support\Collection;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
|
||||
/**
|
||||
* Class BillRepository.
|
||||
@@ -105,6 +106,7 @@ class BillRepository implements BillRepositoryInterface
|
||||
|
||||
public function destroyAll(): void
|
||||
{
|
||||
Log::channel('audit')->info('Delete all bills through destroyAll');
|
||||
$this->user->bills()->delete();
|
||||
}
|
||||
|
||||
|
@@ -30,6 +30,7 @@ use FireflyIII\User;
|
||||
use Illuminate\Contracts\Auth\Authenticatable;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Support\Collection;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
|
||||
/**
|
||||
* Class AvailableBudgetRepository
|
||||
@@ -79,6 +80,7 @@ class AvailableBudgetRepository implements AvailableBudgetRepositoryInterface
|
||||
*/
|
||||
public function destroyAll(): void
|
||||
{
|
||||
Log::channel('audit')->info('Delete all available budgets through destroyAll');
|
||||
$this->user->availableBudgets()->delete();
|
||||
}
|
||||
|
||||
|
@@ -33,6 +33,7 @@ use FireflyIII\User;
|
||||
use Illuminate\Contracts\Auth\Authenticatable;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Support\Collection;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
|
||||
/**
|
||||
* Class BudgetLimitRepository
|
||||
@@ -108,6 +109,7 @@ class BudgetLimitRepository implements BudgetLimitRepositoryInterface
|
||||
|
||||
/** @var Budget $budget */
|
||||
foreach ($budgets as $budget) {
|
||||
Log::channel('audit')->info(sprintf('Delete all budget limits of budget #%d ("%s") through destroyAll', $budget->id, $budget->name));
|
||||
$budget->budgetlimits()->delete();
|
||||
}
|
||||
}
|
||||
|
@@ -45,6 +45,7 @@ use FireflyIII\User;
|
||||
use Illuminate\Contracts\Auth\Authenticatable;
|
||||
use Illuminate\Database\QueryException;
|
||||
use Illuminate\Support\Collection;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
|
||||
/**
|
||||
* Class BudgetRepository.
|
||||
@@ -329,6 +330,7 @@ class BudgetRepository implements BudgetRepositoryInterface
|
||||
RuleAction::where('action_type', 'set_budget')->where('action_value', (string) $budget->id)->delete();
|
||||
$budget->delete();
|
||||
}
|
||||
Log::channel('audit')->info('Delete all budgets through destroyAll');
|
||||
}
|
||||
|
||||
public function getBudgets(): Collection
|
||||
|
@@ -36,6 +36,7 @@ use FireflyIII\Services\Internal\Update\CategoryUpdateService;
|
||||
use FireflyIII\User;
|
||||
use Illuminate\Contracts\Auth\Authenticatable;
|
||||
use Illuminate\Support\Collection;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
|
||||
/**
|
||||
* Class CategoryRepository.
|
||||
@@ -88,6 +89,7 @@ class CategoryRepository implements CategoryRepositoryInterface
|
||||
RuleAction::where('action_type', 'set_category')->where('action_value', $category->name)->delete();
|
||||
$category->delete();
|
||||
}
|
||||
Log::channel('audit')->info('Delete all categories through destroyAll');
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -36,6 +36,7 @@ use FireflyIII\Repositories\Journal\JournalRepositoryInterface;
|
||||
use FireflyIII\User;
|
||||
use Illuminate\Contracts\Auth\Authenticatable;
|
||||
use Illuminate\Support\Collection;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
|
||||
/**
|
||||
* Class PiggyBankRepository.
|
||||
@@ -48,6 +49,7 @@ class PiggyBankRepository implements PiggyBankRepositoryInterface
|
||||
|
||||
public function destroyAll(): void
|
||||
{
|
||||
Log::channel('audit')->info('Delete all piggy banks through destroyAll');
|
||||
$this->user->piggyBanks()->delete();
|
||||
}
|
||||
|
||||
|
@@ -47,6 +47,7 @@ use Illuminate\Contracts\Auth\Authenticatable;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Pagination\LengthAwarePaginator;
|
||||
use Illuminate\Support\Collection;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
|
||||
/**
|
||||
* Class RecurringRepository
|
||||
@@ -116,6 +117,7 @@ class RecurringRepository implements RecurringRepositoryInterface
|
||||
|
||||
public function destroyAll(): void
|
||||
{
|
||||
Log::channel('audit')->info('Delete all recurring transactions through destroyAll');
|
||||
$this->user->recurrences()->delete();
|
||||
}
|
||||
|
||||
|
@@ -31,6 +31,7 @@ use FireflyIII\User;
|
||||
use Illuminate\Contracts\Auth\Authenticatable;
|
||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||
use Illuminate\Support\Collection;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
|
||||
/**
|
||||
* Class RuleGroupRepository.
|
||||
@@ -152,6 +153,7 @@ class RuleGroupRepository implements RuleGroupRepositoryInterface
|
||||
|
||||
public function destroyAll(): void
|
||||
{
|
||||
Log::channel('audit')->info('Delete all rule groups through destroyAll');
|
||||
$groups = $this->get();
|
||||
|
||||
/** @var RuleGroup $group */
|
||||
|
@@ -34,6 +34,7 @@ use FireflyIII\Models\TransactionType;
|
||||
use FireflyIII\User;
|
||||
use Illuminate\Contracts\Auth\Authenticatable;
|
||||
use Illuminate\Support\Collection;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
|
||||
/**
|
||||
* Class TagRepository.
|
||||
@@ -64,6 +65,7 @@ class TagRepository implements TagRepositoryInterface
|
||||
*/
|
||||
public function destroyAll(): void
|
||||
{
|
||||
Log::channel('audit')->info('Delete all tags through destroyAll');
|
||||
$tags = $this->get();
|
||||
|
||||
/** @var Tag $tag */
|
||||
|
@@ -273,10 +273,6 @@ class CurrencyRepository implements CurrencyRepositoryInterface
|
||||
return TransactionCurrency::where('code', $currencyCode)->first();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param TransactionCurrency $currency
|
||||
* Enables a currency
|
||||
*/
|
||||
public function enable(TransactionCurrency $currency): void
|
||||
{
|
||||
$this->userGroup->currencies()->syncWithoutDetaching([$currency->id]);
|
||||
|
@@ -22,7 +22,9 @@ class IsValidAmount implements ValidationRule
|
||||
// must not be empty:
|
||||
if($this->emptyString($value)) {
|
||||
$fail('validation.filled')->translate();
|
||||
Log::info(sprintf('IsValidAmount: "%s" cannot be empty.', $value));
|
||||
$message = sprintf('IsValidAmount: "%s" cannot be empty.', $value);
|
||||
Log::debug($message);
|
||||
Log::channel('audit')->info($message);
|
||||
|
||||
return;
|
||||
}
|
||||
@@ -30,7 +32,9 @@ class IsValidAmount implements ValidationRule
|
||||
// must be a number:
|
||||
if(!$this->isValidNumber($value)) {
|
||||
$fail('validation.numeric')->translate();
|
||||
Log::info(sprintf('IsValidAmount: "%s" is not a number.', $value));
|
||||
$message = sprintf('IsValidAmount: "%s" is not a number.', $value);
|
||||
Log::debug($message);
|
||||
Log::channel('audit')->info($message);
|
||||
|
||||
return;
|
||||
}
|
||||
@@ -38,7 +42,9 @@ class IsValidAmount implements ValidationRule
|
||||
// must not be scientific notation:
|
||||
if($this->scientificNumber($value)) {
|
||||
$fail('validation.scientific_notation')->translate();
|
||||
Log::info(sprintf('IsValidAmount: "%s" cannot be in the scientific notation.', $value));
|
||||
$message = sprintf('IsValidAmount: "%s" cannot be in the scientific notation.', $value);
|
||||
Log::debug($message);
|
||||
Log::channel('audit')->info($message);
|
||||
|
||||
return;
|
||||
}
|
||||
@@ -47,16 +53,20 @@ class IsValidAmount implements ValidationRule
|
||||
if($this->lessThanLots($value)) {
|
||||
$amount = bcmul('-1', self::BIG_AMOUNT);
|
||||
$fail('validation.gte.numeric')->translate(['value' => $amount]);
|
||||
Log::info(sprintf('IsValidAmount: "%s" must be more than %s.', $value, $amount));
|
||||
$message = sprintf('IsValidAmount: "%s" must be more than %s.', $value, $amount);
|
||||
Log::debug($message);
|
||||
Log::channel('audit')->info($message);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// must be less than 100 million and 1709:
|
||||
if($this->moreThanLots($value)) {
|
||||
Log::info(sprintf('IsValidPositiveAmount: "%s" must be more than %s.', $value, self::BIG_AMOUNT));
|
||||
$fail('validation.lte.numeric')->translate(['value' => self::BIG_AMOUNT]);
|
||||
$message = sprintf('IsValidAmount: "%s" must be more than %s.', $value, self::BIG_AMOUNT);
|
||||
Log::debug($message);
|
||||
Log::channel('audit')->info($message);
|
||||
}
|
||||
Log::info(sprintf('IsValidAmount: "%s" is a valid positive amount.', $value));
|
||||
Log::debug(sprintf('IsValidAmount: "%s" is a valid positive amount.', $value));
|
||||
}
|
||||
}
|
||||
|
@@ -21,7 +21,9 @@ class IsValidPositiveAmount implements ValidationRule
|
||||
// must not be empty:
|
||||
if($this->emptyString($value)) {
|
||||
$fail('validation.filled')->translate();
|
||||
Log::info(sprintf('IsValidPositiveAmount: "%s" cannot be empty.', $value));
|
||||
$message = sprintf('IsValidPositiveAmount: "%s" cannot be empty.', $value);
|
||||
Log::debug($message);
|
||||
Log::channel('audit')->info($message);
|
||||
|
||||
return;
|
||||
}
|
||||
@@ -29,29 +31,37 @@ class IsValidPositiveAmount implements ValidationRule
|
||||
// must be a number:
|
||||
if(!$this->isValidNumber($value)) {
|
||||
$fail('validation.numeric')->translate();
|
||||
Log::info(sprintf('IsValidPositiveAmount: "%s" is not a number.', $value));
|
||||
$message = sprintf('IsValidPositiveAmount: "%s" is not a number.', $value);
|
||||
Log::debug($message);
|
||||
Log::channel('audit')->info($message);
|
||||
|
||||
return;
|
||||
}
|
||||
// must not be scientific notation:
|
||||
if($this->scientificNumber($value)) {
|
||||
$fail('validation.scientific_notation')->translate();
|
||||
Log::info(sprintf('IsValidPositiveAmount: "%s" cannot be in the scientific notation.', $value));
|
||||
$message = sprintf('IsValidPositiveAmount: "%s" cannot be in the scientific notation.', $value);
|
||||
Log::debug($message);
|
||||
Log::channel('audit')->info($message);
|
||||
|
||||
return;
|
||||
}
|
||||
// must be more than zero:
|
||||
if($this->lessOrEqualToZero($value)) {
|
||||
$fail('validation.more_than_zero')->translate();
|
||||
Log::info(sprintf('IsValidPositiveAmount: "%s" must be more than zero.', $value));
|
||||
$message = sprintf('IsValidPositiveAmount: "%s" must be more than zero.', $value);
|
||||
Log::debug($message);
|
||||
Log::channel('audit')->info($message);
|
||||
|
||||
return;
|
||||
}
|
||||
// must be less than 100 million and 1709:
|
||||
if($this->moreThanLots($value)) {
|
||||
Log::info(sprintf('IsValidPositiveAmount: "%s" must be less than %s.', $value, self::BIG_AMOUNT));
|
||||
$fail('validation.lte.numeric')->translate(['value' => self::BIG_AMOUNT]);
|
||||
$message = sprintf('IsValidPositiveAmount: "%s" must be less than %s.', $value, self::BIG_AMOUNT);
|
||||
Log::debug($message);
|
||||
Log::channel('audit')->info($message);
|
||||
}
|
||||
Log::info(sprintf('IsValidPositiveAmount: "%s" is a valid positive amount.', $value));
|
||||
Log::debug(sprintf('IsValidPositiveAmount: "%s" is a valid positive amount.', $value));
|
||||
}
|
||||
}
|
||||
|
@@ -21,7 +21,9 @@ class IsValidZeroOrMoreAmount implements ValidationRule
|
||||
// must not be empty:
|
||||
if($this->emptyString($value)) {
|
||||
$fail('validation.filled')->translate();
|
||||
Log::info(sprintf('IsValidZeroOrMoreAmount: "%s" cannot be empty.', $value));
|
||||
$message = sprintf('IsValidZeroOrMoreAmount: "%s" cannot be empty.', $value);
|
||||
Log::debug($message);
|
||||
Log::channel('audit')->info($message);
|
||||
|
||||
return;
|
||||
}
|
||||
@@ -29,29 +31,37 @@ class IsValidZeroOrMoreAmount implements ValidationRule
|
||||
// must be a number:
|
||||
if(!$this->isValidNumber($value)) {
|
||||
$fail('validation.numeric')->translate();
|
||||
Log::info(sprintf('IsValidZeroOrMoreAmount: "%s" is not a number.', $value));
|
||||
$message = sprintf('IsValidZeroOrMoreAmount: "%s" is not a number.', $value);
|
||||
Log::debug($message);
|
||||
Log::channel('audit')->info($message);
|
||||
|
||||
return;
|
||||
}
|
||||
// must not be scientific notation:
|
||||
if($this->scientificNumber($value)) {
|
||||
$fail('validation.scientific_notation')->translate();
|
||||
Log::info(sprintf('IsValidZeroOrMoreAmount: "%s" cannot be in the scientific notation.', $value));
|
||||
$message = sprintf('IsValidZeroOrMoreAmount: "%s" cannot be in the scientific notation.', $value);
|
||||
Log::debug($message);
|
||||
Log::channel('audit')->info($message);
|
||||
|
||||
return;
|
||||
}
|
||||
// must be more than zero:
|
||||
// must be zero or more
|
||||
if(!$this->zeroOrMore($value)) {
|
||||
$fail('validation.more_than_zero_correct')->translate();
|
||||
Log::info(sprintf('IsValidZeroOrMoreAmount: "%s" must be more than zero.', $value));
|
||||
$message = sprintf('IsValidZeroOrMoreAmount: "%s" must be zero or more.', $value);
|
||||
Log::debug($message);
|
||||
Log::channel('audit')->info($message);
|
||||
|
||||
return;
|
||||
}
|
||||
// must be less than 100 million and 1709:
|
||||
if($this->moreThanLots($value)) {
|
||||
Log::info(sprintf('IsValidPositiveAmount: "%s" must be less than %s.', $value, self::BIG_AMOUNT));
|
||||
$fail('validation.lte.numeric')->translate(['value' => self::BIG_AMOUNT]);
|
||||
$message = sprintf('IsValidPositiveAmount: "%s" must be less than %s.', $value, self::BIG_AMOUNT);
|
||||
Log::debug($message);
|
||||
Log::channel('audit')->info($message);
|
||||
}
|
||||
Log::info(sprintf('IsValidZeroOrMoreAmount: "%s" is a valid positive amount.', $value));
|
||||
Log::debug(sprintf('IsValidZeroOrMoreAmount: "%s" is a valid positive amount.', $value));
|
||||
}
|
||||
}
|
||||
|
@@ -41,7 +41,9 @@ class UniqueAccountNumber implements ValidationRule
|
||||
*/
|
||||
public function __construct(?Account $account, ?string $expectedType)
|
||||
{
|
||||
app('log')->debug('Constructed UniqueAccountNumber');
|
||||
app('log')
|
||||
->debug('Constructed UniqueAccountNumber')
|
||||
;
|
||||
$this->account = $account;
|
||||
$this->expectedType = $expectedType;
|
||||
// a very basic fix to make sure we get the correct account type:
|
||||
@@ -62,7 +64,7 @@ class UniqueAccountNumber implements ValidationRule
|
||||
*/
|
||||
public function message(): string
|
||||
{
|
||||
return (string)trans('validation.unique_account_number_for_user');
|
||||
return (string) trans('validation.unique_account_number_for_user');
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -41,12 +41,12 @@ class UserGroupAccount implements BinderInterface
|
||||
if (auth()->check()) {
|
||||
/** @var User $user */
|
||||
$user = auth()->user();
|
||||
$currency = Account::where('id', (int)$value)
|
||||
$account = Account::where('id', (int)$value)
|
||||
->where('user_group_id', $user->user_group_id)
|
||||
->first()
|
||||
;
|
||||
if (null !== $currency) {
|
||||
return $currency;
|
||||
if (null !== $account) {
|
||||
return $account;
|
||||
}
|
||||
}
|
||||
|
||||
|
52
app/Support/Binder/UserGroupTransaction.php
Normal file
52
app/Support/Binder/UserGroupTransaction.php
Normal file
@@ -0,0 +1,52 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* UserGroupTransaction.php
|
||||
* Copyright (c) 2024 james@firefly-iii.org
|
||||
*
|
||||
* This file is part of Firefly III (https://github.com/firefly-iii).
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\Support\Binder;
|
||||
|
||||
use FireflyIII\Models\TransactionGroup;
|
||||
use FireflyIII\User;
|
||||
use Illuminate\Routing\Route;
|
||||
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
|
||||
|
||||
/**
|
||||
* Class UserGroupTransaction.
|
||||
*/
|
||||
class UserGroupTransaction implements BinderInterface
|
||||
{
|
||||
public static function routeBinder(string $value, Route $route): TransactionGroup
|
||||
{
|
||||
if (auth()->check()) {
|
||||
/** @var User $user */
|
||||
$user = auth()->user();
|
||||
$group = TransactionGroup::where('id', (int) $value)
|
||||
->where('user_group_id', $user->user_group_id)
|
||||
->first()
|
||||
;
|
||||
if (null !== $group) {
|
||||
return $group;
|
||||
}
|
||||
}
|
||||
|
||||
throw new NotFoundHttpException();
|
||||
}
|
||||
}
|
@@ -216,10 +216,9 @@ class Preferences
|
||||
}
|
||||
|
||||
/**
|
||||
* @param null $default
|
||||
* TODO remove me.
|
||||
*
|
||||
* @return null|preference
|
||||
* TODO remove me
|
||||
* @param null $default
|
||||
*
|
||||
* @throws FireflyException
|
||||
*/
|
||||
|
@@ -65,6 +65,23 @@ trait AppendsLocationData
|
||||
*/
|
||||
abstract public function boolean($key = null, $default = false);
|
||||
|
||||
public function addFromromTransactionStore(array $information, array $return): array
|
||||
{
|
||||
$return['store_location'] = false;
|
||||
if (true === $information['store_location']) {
|
||||
$long = array_key_exists('longitude', $information) ? $information['longitude'] : null;
|
||||
$lat = array_key_exists('latitude', $information) ? $information['latitude'] : null;
|
||||
if (null !== $long && null !== $lat && $this->validLongitude($long) && $this->validLatitude($lat)) {
|
||||
$return['store_location'] = true;
|
||||
$return['longitude'] = $information['longitude'];
|
||||
$return['latitude'] = $information['latitude'];
|
||||
$return['zoom_level'] = $information['zoom_level'];
|
||||
}
|
||||
}
|
||||
|
||||
return $return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read the submitted Request data and add new or updated Location data to the array.
|
||||
*/
|
||||
@@ -119,6 +136,20 @@ trait AppendsLocationData
|
||||
return $data;
|
||||
}
|
||||
|
||||
private function validLongitude(string $longitude): bool
|
||||
{
|
||||
$number = (float) $longitude;
|
||||
|
||||
return $number >= -180 && $number <= 180;
|
||||
}
|
||||
|
||||
private function validLatitude(string $latitude): bool
|
||||
{
|
||||
$number = (float) $latitude;
|
||||
|
||||
return $number >= -90 && $number <= 90;
|
||||
}
|
||||
|
||||
private function getLocationKey(?string $prefix, string $key): string
|
||||
{
|
||||
if (null === $prefix) {
|
||||
|
@@ -81,8 +81,7 @@ class AmountFormat extends AbstractExtension
|
||||
/**
|
||||
* Will format the amount by the currency related to the given account.
|
||||
*
|
||||
* @return twigFunction
|
||||
* TODO remove me when layout v1 is deprecated
|
||||
* TODO Remove me when v2 hits.
|
||||
*/
|
||||
protected function formatAmountByAccount(): TwigFunction
|
||||
{
|
||||
|
@@ -346,8 +346,7 @@ class General extends AbstractExtension
|
||||
}
|
||||
|
||||
/**
|
||||
* @return twigFunction
|
||||
* TODO remove me when layout v1 is deprecated
|
||||
* TODO Remove me when v2 hits.
|
||||
*/
|
||||
protected function getMetaField(): TwigFunction
|
||||
{
|
||||
|
@@ -199,8 +199,6 @@ class AccountTransformer extends AbstractTransformer
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*
|
||||
* TODO refactor call to get~OpeningBalanceAmount / Date because it is a lot of queries
|
||||
*/
|
||||
private function getOpeningBalance(Account $account, string $accountType): array
|
||||
|
@@ -26,8 +26,13 @@ namespace FireflyIII\Transformers\V2;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use FireflyIII\Exceptions\FireflyException;
|
||||
use FireflyIII\Models\Budget;
|
||||
use FireflyIII\Models\Category;
|
||||
use FireflyIII\Models\Location;
|
||||
use FireflyIII\Models\Note;
|
||||
use FireflyIII\Models\Transaction;
|
||||
use FireflyIII\Models\TransactionCurrency;
|
||||
use FireflyIII\Models\TransactionGroup;
|
||||
use FireflyIII\Models\TransactionJournal;
|
||||
use FireflyIII\Models\TransactionJournalMeta;
|
||||
use FireflyIII\Models\TransactionType;
|
||||
@@ -35,86 +40,71 @@ use FireflyIII\Support\Http\Api\ExchangeRateConverter;
|
||||
use FireflyIII\Support\NullArrayObject;
|
||||
use Illuminate\Support\Collection;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
|
||||
/**
|
||||
* Class TransactionGroupTransformer
|
||||
*/
|
||||
class TransactionGroupTransformer extends AbstractTransformer
|
||||
{
|
||||
private ExchangeRateConverter $converter;
|
||||
private array $currencies = [];
|
||||
private array $accountTypes = []; // account types collection.
|
||||
private array $journals = []; // collection of all journals and some important meta-data.
|
||||
private array $objects = [];
|
||||
private array $currencies = []; // collection of all currencies for this transformer.
|
||||
private TransactionCurrency $default;
|
||||
private array $meta;
|
||||
private array $notes;
|
||||
private array $tags;
|
||||
private ExchangeRateConverter $converter;
|
||||
|
||||
// private array $currencies = [];
|
||||
// private array $transactionTypes = [];
|
||||
// private array $meta = [];
|
||||
// private array $notes = [];
|
||||
// private array $locations = [];
|
||||
// private array $tags = [];
|
||||
// private array $amounts = [];
|
||||
// private array $foreignAmounts = [];
|
||||
// private array $journalCurrencies = [];
|
||||
// private array $foreignCurrencies = [];
|
||||
|
||||
public function collectMetaData(Collection $objects): void
|
||||
{
|
||||
// start with currencies:
|
||||
$currencies = [];
|
||||
$journals = [];
|
||||
$collectForObjects = false;
|
||||
|
||||
/** @var array $object */
|
||||
/** @var array|TransactionGroup $object */
|
||||
foreach ($objects as $object) {
|
||||
foreach ($object['sums'] as $sum) {
|
||||
$id = (int) $sum['currency_id'];
|
||||
$currencies[$id] ??= TransactionCurrency::find($sum['currency_id']);
|
||||
if (is_array($object)) {
|
||||
$this->collectForArray($object);
|
||||
}
|
||||
if ($object instanceof TransactionGroup) {
|
||||
$this->collectForObject($object);
|
||||
$collectForObjects = true;
|
||||
}
|
||||
}
|
||||
|
||||
/** @var array $transaction */
|
||||
foreach ($object['transactions'] as $transaction) {
|
||||
$id = (int) $transaction['transaction_journal_id'];
|
||||
$journals[$id] = [];
|
||||
}
|
||||
}
|
||||
$this->currencies = $currencies;
|
||||
$this->default = app('amount')->getDefaultCurrency();
|
||||
|
||||
// grab meta for all journals:
|
||||
$meta = TransactionJournalMeta::whereIn('transaction_journal_id', array_keys($journals))->get();
|
||||
|
||||
/** @var TransactionJournalMeta $entry */
|
||||
foreach ($meta as $entry) {
|
||||
$id = $entry->transaction_journal_id;
|
||||
$this->meta[$id][$entry->name] = $entry->data;
|
||||
}
|
||||
|
||||
// grab all notes for all journals:
|
||||
$notes = Note::whereNoteableType(TransactionJournal::class)->whereIn('noteable_id', array_keys($journals))->get();
|
||||
|
||||
/** @var Note $note */
|
||||
foreach ($notes as $note) {
|
||||
$id = $note->noteable_id;
|
||||
$this->notes[$id] = $note;
|
||||
}
|
||||
|
||||
// grab all tags for all journals:
|
||||
$tags = DB::table('tag_transaction_journal')
|
||||
->leftJoin('tags', 'tags.id', 'tag_transaction_journal.tag_id')
|
||||
->whereIn('tag_transaction_journal.transaction_journal_id', array_keys($journals))
|
||||
->get(['tag_transaction_journal.transaction_journal_id', 'tags.tag'])
|
||||
;
|
||||
|
||||
/** @var \stdClass $tag */
|
||||
foreach ($tags as $tag) {
|
||||
$id = (int) $tag->transaction_journal_id;
|
||||
$this->tags[$id][] = $tag->tag;
|
||||
}
|
||||
|
||||
// create converter
|
||||
Log::debug(sprintf('Created new ExchangeRateConverter in %s', __METHOD__));
|
||||
$this->converter = new ExchangeRateConverter();
|
||||
|
||||
$this->collectAllMetaData();
|
||||
$this->collectAllNotes();
|
||||
$this->collectAllLocations();
|
||||
$this->collectAllTags();
|
||||
if ($collectForObjects) {
|
||||
$this->collectAllCurrencies();
|
||||
// $this->collectAllAmounts();
|
||||
// $this->collectTransactionTypes();
|
||||
// $this->collectAccounts();
|
||||
// source accounts
|
||||
// destination accounts
|
||||
}
|
||||
}
|
||||
|
||||
public function transform(array $group): array
|
||||
public function transform(array|TransactionGroup $group): array
|
||||
{
|
||||
if (is_array($group)) {
|
||||
$first = reset($group['transactions']);
|
||||
|
||||
return [
|
||||
'id' => (string) $group['id'],
|
||||
'created_at' => $first['created_at']->toAtomString(),
|
||||
'updated_at' => $first['updated_at']->toAtomString(),
|
||||
'created_at' => $group['created_at']->toAtomString(),
|
||||
'updated_at' => $group['updated_at']->toAtomString(),
|
||||
'user' => (string) $first['user_id'],
|
||||
'user_group' => (string) $first['user_group_id'],
|
||||
'group_title' => $group['title'] ?? null,
|
||||
@@ -128,6 +118,154 @@ class TransactionGroupTransformer extends AbstractTransformer
|
||||
];
|
||||
}
|
||||
|
||||
return [
|
||||
'id' => (string) $group->id,
|
||||
'created_at' => $group->created_at->toAtomString(),
|
||||
'updated_at' => $group->created_at->toAtomString(),
|
||||
'user' => (string) $group->user_id,
|
||||
'user_group' => (string) $group->user_group_id,
|
||||
'group_title' => $group->title ?? null,
|
||||
'transactions' => $this->transformJournals($group),
|
||||
'links' => [
|
||||
[
|
||||
'rel' => 'self',
|
||||
'uri' => sprintf('/transactions/%d', $group->id),
|
||||
],
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
private function transformJournals(TransactionGroup $group): array
|
||||
{
|
||||
$return = [];
|
||||
|
||||
/** @var TransactionJournal $journal */
|
||||
foreach ($group->transactionJournals as $journal) {
|
||||
$return[] = $this->transformJournal($journal);
|
||||
}
|
||||
|
||||
return $return;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws FireflyException
|
||||
*
|
||||
* @SuppressWarnings(PHPMD.ExcessiveMethodLength)
|
||||
*/
|
||||
private function transformJournal(TransactionJournal $journal): array
|
||||
{
|
||||
$id = $journal->id;
|
||||
|
||||
/** @var null|TransactionCurrency $foreignCurrency */
|
||||
$foreignCurrency = null;
|
||||
|
||||
/** @var TransactionCurrency $currency */
|
||||
$currency = $this->currencies[$this->journals[$id]['currency_id']];
|
||||
$nativeForeignAmount = null;
|
||||
$amount = $this->journals[$journal->id]['amount'];
|
||||
$foreignAmount = $this->journals[$journal->id]['foreign_amount'];
|
||||
$meta = new NullArrayObject($this->meta[$id] ?? []);
|
||||
|
||||
// has foreign amount?
|
||||
if (null !== $foreignAmount) {
|
||||
$foreignCurrency = $this->currencies[$this->journals[$id]['foreign_currency_id']];
|
||||
$nativeForeignAmount = $this->converter->convert($this->default, $foreignCurrency, $journal->date, $foreignAmount);
|
||||
}
|
||||
|
||||
$nativeAmount = $this->converter->convert($this->default, $currency, $journal->date, $amount);
|
||||
|
||||
$longitude = null;
|
||||
$latitude = null;
|
||||
$zoomLevel = null;
|
||||
if (array_key_exists('location', $this->journals[$id])) {
|
||||
$latitude = (string) $this->journals[$id]['location']['latitude'];
|
||||
$longitude = (string) $this->journals[$id]['location']['longitude'];
|
||||
$zoomLevel = $this->journals[$id]['location']['zoom_level'];
|
||||
}
|
||||
|
||||
return [
|
||||
'user' => (string) $journal->user_id,
|
||||
'user_group' => (string) $journal->user_group_id,
|
||||
'transaction_journal_id' => (string) $journal->id,
|
||||
'type' => $this->journals[$journal->id]['type'],
|
||||
'date' => $journal->date->toAtomString(),
|
||||
'order' => $journal->order,
|
||||
'amount' => $amount,
|
||||
'native_amount' => $nativeAmount,
|
||||
'foreign_amount' => $foreignAmount,
|
||||
'native_foreign_amount' => $nativeForeignAmount,
|
||||
'currency_id' => (string) $currency->id,
|
||||
'currency_code' => $currency->code,
|
||||
'currency_name' => $currency->name,
|
||||
'currency_symbol' => $currency->symbol,
|
||||
'currency_decimal_places' => $currency->decimal_places,
|
||||
|
||||
// converted to native currency
|
||||
'native_currency_id' => (string) $this->default->id,
|
||||
'native_currency_code' => $this->default->code,
|
||||
'native_currency_name' => $this->default->name,
|
||||
'native_currency_symbol' => $this->default->symbol,
|
||||
'native_currency_decimal_places' => $this->default->decimal_places,
|
||||
|
||||
// foreign currency amount:
|
||||
'foreign_currency_id' => $foreignCurrency?->id,
|
||||
'foreign_currency_code' => $foreignCurrency?->code,
|
||||
'foreign_currency_name' => $foreignCurrency?->name,
|
||||
'foreign_currency_symbol' => $foreignCurrency?->symbol,
|
||||
'foreign_currency_decimal_places' => $foreignCurrency?->decimal_places,
|
||||
|
||||
'description' => $journal->description,
|
||||
'source_id' => (string) $this->journals[$id]['source_account_id'],
|
||||
'source_name' => $this->journals[$id]['source_account_name'],
|
||||
'source_iban' => $this->journals[$id]['source_account_iban'],
|
||||
'source_type' => $this->journals[$id]['source_account_type'],
|
||||
|
||||
'destination_id' => (string) $this->journals[$id]['destination_account_id'],
|
||||
'destination_name' => $this->journals[$id]['destination_account_name'],
|
||||
'destination_iban' => $this->journals[$id]['destination_account_iban'],
|
||||
'destination_type' => $this->journals[$id]['destination_account_type'],
|
||||
|
||||
'budget_id' => $this->journals[$id]['budget_id'],
|
||||
'budget_name' => $this->journals[$id]['budget_name'],
|
||||
'category_id' => $this->journals[$id]['category_id'],
|
||||
'category_name' => $this->journals[$id]['category_name'],
|
||||
'bill_id' => $this->journals[$id]['bill_id'],
|
||||
'bill_name' => $this->journals[$id]['bill_name'],
|
||||
'reconciled' => $this->journals[$id]['reconciled'],
|
||||
'notes' => $this->journals[$id]['notes'] ?? null,
|
||||
'tags' => $this->journals[$id]['tags'] ?? [],
|
||||
'internal_reference' => $meta['internal_reference'],
|
||||
'external_id' => $meta['external_id'],
|
||||
'original_source' => $meta['original_source'],
|
||||
'recurrence_id' => $meta['recurrence_id'],
|
||||
'recurrence_total' => $meta['recurrence_total'],
|
||||
'recurrence_count' => $meta['recurrence_count'],
|
||||
'external_url' => $meta['external_url'],
|
||||
'import_hash_v2' => $meta['import_hash_v2'],
|
||||
'sepa_cc' => $meta['sepa_cc'],
|
||||
'sepa_ct_op' => $meta['sepa_ct_op'],
|
||||
'sepa_ct_id' => $meta['sepa_ct_id'],
|
||||
'sepa_db' => $meta['sepa_db'],
|
||||
'sepa_country' => $meta['sepa_country'],
|
||||
'sepa_ep' => $meta['sepa_ep'],
|
||||
'sepa_ci' => $meta['sepa_ci'],
|
||||
'sepa_batch_id' => $meta['sepa_batch_id'],
|
||||
'interest_date' => $this->date($meta['interest_date']),
|
||||
'book_date' => $this->date($meta['book_date']),
|
||||
'process_date' => $this->date($meta['process_date']),
|
||||
'due_date' => $this->date($meta['due_date']),
|
||||
'payment_date' => $this->date($meta['payment_date']),
|
||||
'invoice_date' => $this->date($meta['invoice_date']),
|
||||
|
||||
// location data
|
||||
'longitude' => $longitude,
|
||||
'latitude' => $latitude,
|
||||
'zoom_level' => $zoomLevel,
|
||||
//
|
||||
// 'has_attachments' => $this->hasAttachments((int) $row['transaction_journal_id']),
|
||||
];
|
||||
}
|
||||
|
||||
private function transformTransactions(array $transactions): array
|
||||
{
|
||||
$return = [];
|
||||
@@ -167,6 +305,15 @@ class TransactionGroupTransformer extends AbstractTransformer
|
||||
}
|
||||
$this->converter->summarize();
|
||||
|
||||
$longitude = null;
|
||||
$latitude = null;
|
||||
$zoomLevel = null;
|
||||
if (array_key_exists('location', $this->journals[$journalId])) {
|
||||
$latitude = (string) $this->journals[$journalId]['location']['latitude'];
|
||||
$longitude = (string) $this->journals[$journalId]['location']['longitude'];
|
||||
$zoomLevel = $this->journals[$journalId]['location']['zoom_level'];
|
||||
}
|
||||
|
||||
return [
|
||||
'user' => (string) $transaction['user_id'],
|
||||
'user_group' => (string) $transaction['user_group_id'],
|
||||
@@ -241,9 +388,9 @@ class TransactionGroupTransformer extends AbstractTransformer
|
||||
'invoice_date' => $this->date($meta['invoice_date']),
|
||||
|
||||
// location data
|
||||
// 'longitude' => $longitude,
|
||||
// 'latitude' => $latitude,
|
||||
// 'zoom_level' => $zoomLevel,
|
||||
'longitude' => $longitude,
|
||||
'latitude' => $latitude,
|
||||
'zoom_level' => $zoomLevel,
|
||||
//
|
||||
// 'has_attachments' => $this->hasAttachments((int) $row['transaction_journal_id']),
|
||||
];
|
||||
@@ -312,4 +459,162 @@ class TransactionGroupTransformer extends AbstractTransformer
|
||||
|
||||
return $res;
|
||||
}
|
||||
|
||||
private function collectForArray(array $object): void
|
||||
{
|
||||
foreach ($object['sums'] as $sum) {
|
||||
$this->currencies[(int) $sum['currency_id']] ??= TransactionCurrency::find($sum['currency_id']);
|
||||
}
|
||||
|
||||
/** @var array $transaction */
|
||||
foreach ($object['transactions'] as $transaction) {
|
||||
$this->journals[(int) $transaction['transaction_journal_id']] = [];
|
||||
}
|
||||
}
|
||||
|
||||
private function collectAllMetaData(): void
|
||||
{
|
||||
$meta = TransactionJournalMeta::whereIn('transaction_journal_id', array_keys($this->journals))->get();
|
||||
|
||||
/** @var TransactionJournalMeta $entry */
|
||||
foreach ($meta as $entry) {
|
||||
$id = $entry->transaction_journal_id;
|
||||
$this->journals[$id]['meta'] ??= [];
|
||||
$this->journals[$id]['meta'][$entry->name] = $entry->data;
|
||||
}
|
||||
}
|
||||
|
||||
private function collectAllNotes(): void
|
||||
{
|
||||
// grab all notes for all journals:
|
||||
$notes = Note::whereNoteableType(TransactionJournal::class)->whereIn('noteable_id', array_keys($this->journals))->get();
|
||||
|
||||
/** @var Note $note */
|
||||
foreach ($notes as $note) {
|
||||
$id = $note->noteable_id;
|
||||
$this->journals[$id]['notes'] = $note->text;
|
||||
}
|
||||
}
|
||||
|
||||
private function collectAllLocations(): void
|
||||
{
|
||||
// grab all locations for all journals:
|
||||
$locations = Location::whereLocatableType(TransactionJournal::class)->whereIn('locatable_id', array_keys($this->journals))->get();
|
||||
|
||||
/** @var Location $location */
|
||||
foreach ($locations as $location) {
|
||||
$id = $location->locatable_id;
|
||||
$this->journals[$id]['location'] = [
|
||||
'latitude' => $location->latitude,
|
||||
'longitude' => $location->longitude,
|
||||
'zoom_level' => $location->zoom_level,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
private function collectAllTags(): void
|
||||
{
|
||||
// grab all tags for all journals:
|
||||
$tags = DB::table('tag_transaction_journal')
|
||||
->leftJoin('tags', 'tags.id', 'tag_transaction_journal.tag_id')
|
||||
->whereIn('tag_transaction_journal.transaction_journal_id', array_keys($this->journals))
|
||||
->get(['tag_transaction_journal.transaction_journal_id', 'tags.tag'])
|
||||
;
|
||||
|
||||
/** @var \stdClass $tag */
|
||||
foreach ($tags as $tag) {
|
||||
$id = (int) $tag->transaction_journal_id;
|
||||
$this->journals[$id]['tags'][] = $tag->tag;
|
||||
}
|
||||
}
|
||||
|
||||
private function collectForObject(TransactionGroup $object): void
|
||||
{
|
||||
foreach ($object->transactionJournals as $journal) {
|
||||
$this->journals[$journal->id] = [];
|
||||
$this->objects[] = $journal;
|
||||
}
|
||||
}
|
||||
|
||||
private function collectAllCurrencies(): void
|
||||
{
|
||||
/** @var TransactionJournal $journal */
|
||||
foreach ($this->objects as $journal) {
|
||||
$id = $journal->id;
|
||||
$this->journals[$id]['reconciled'] = false;
|
||||
$this->journals[$id]['foreign_amount'] = null;
|
||||
$this->journals[$id]['foreign_currency_id'] = null;
|
||||
$this->journals[$id]['amount'] = null;
|
||||
$this->journals[$id]['currency_id'] = null;
|
||||
$this->journals[$id]['type'] = $journal->transactionType->type;
|
||||
$this->journals[$id]['budget_id'] = null;
|
||||
$this->journals[$id]['budget_name'] = null;
|
||||
$this->journals[$id]['category_id'] = null;
|
||||
$this->journals[$id]['category_name'] = null;
|
||||
$this->journals[$id]['bill_id'] = null;
|
||||
$this->journals[$id]['bill_name'] = null;
|
||||
|
||||
// collect budget:
|
||||
/** @var null|Budget $budget */
|
||||
$budget = $journal->budgets()->first();
|
||||
if (null !== $budget) {
|
||||
$this->journals[$id]['budget_id'] = (string) $budget->id;
|
||||
$this->journals[$id]['budget_name'] = $budget->name;
|
||||
}
|
||||
|
||||
// collect category:
|
||||
/** @var null|Category $category */
|
||||
$category = $journal->categories()->first();
|
||||
if (null !== $category) {
|
||||
$this->journals[$id]['category_id'] = (string) $category->id;
|
||||
$this->journals[$id]['category_name'] = $category->name;
|
||||
}
|
||||
|
||||
// collect bill:
|
||||
if (null !== $journal->bill_id) {
|
||||
$bill = $journal->bill;
|
||||
$this->journals[$id]['bill_id'] = (string) $bill->id;
|
||||
$this->journals[$id]['bill_name'] = $bill->name;
|
||||
}
|
||||
|
||||
/** @var Transaction $transaction */
|
||||
foreach ($journal->transactions as $transaction) {
|
||||
if (-1 === bccomp($transaction->amount, '0')) {
|
||||
// only collect source account info
|
||||
$account = $transaction->account;
|
||||
$this->accountTypes[$account->account_type_id] ??= $account->accountType->type;
|
||||
$this->journals[$id]['source_account_name'] = $account->name;
|
||||
$this->journals[$id]['source_account_iban'] = $account->iban;
|
||||
$this->journals[$id]['source_account_type'] = $this->accountTypes[$account->account_type_id];
|
||||
$this->journals[$id]['source_account_id'] = $transaction->account_id;
|
||||
$this->journals[$id]['reconciled'] = $transaction->reconciled;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
// add account
|
||||
$account = $transaction->account;
|
||||
$this->accountTypes[$account->account_type_id] ??= $account->accountType->type;
|
||||
$this->journals[$id]['destination_account_name'] = $account->name;
|
||||
$this->journals[$id]['destination_account_iban'] = $account->iban;
|
||||
$this->journals[$id]['destination_account_type'] = $this->accountTypes[$account->account_type_id];
|
||||
$this->journals[$id]['destination_account_id'] = $transaction->account_id;
|
||||
|
||||
// find and set currency
|
||||
$currencyId = $transaction->transaction_currency_id;
|
||||
$this->currencies[$currencyId] ??= $transaction->transactionCurrency;
|
||||
$this->journals[$id]['currency_id'] = $currencyId;
|
||||
$this->journals[$id]['amount'] = $transaction->amount;
|
||||
// find and set foreign currency
|
||||
if (null !== $transaction->foreign_currency_id) {
|
||||
$foreignCurrencyId = $transaction->foreign_currency_id;
|
||||
$this->currencies[$foreignCurrencyId] ??= $transaction->foreignCurrency;
|
||||
$this->journals[$id]['foreign_currency_id'] = $foreignCurrencyId;
|
||||
$this->journals[$id]['foreign_amount'] = $transaction->foreign_amount;
|
||||
}
|
||||
|
||||
// find and set destination account info.
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -24,6 +24,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\Validation;
|
||||
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Illuminate\Validation\Validator;
|
||||
|
||||
/**
|
||||
@@ -50,30 +51,42 @@ trait CurrencyValidation
|
||||
if (!is_array($transaction)) {
|
||||
continue;
|
||||
}
|
||||
// if foreign amount is present, then the currency must be as well.
|
||||
if (array_key_exists('foreign_amount', $transaction)
|
||||
&& !(array_key_exists('foreign_currency_id', $transaction)
|
||||
|| array_key_exists(
|
||||
'foreign_currency_code',
|
||||
$transaction
|
||||
))
|
||||
&& 0 !== bccomp('0', $transaction['foreign_amount'])
|
||||
) {
|
||||
$validator->errors()->add(
|
||||
'transactions.'.$index.'.foreign_amount',
|
||||
(string)trans('validation.require_currency_info')
|
||||
);
|
||||
|
||||
if (!array_key_exists('foreign_amount', $transaction) && !array_key_exists('foreign_currency_id', $transaction) && !array_key_exists('foreign_currency_code', $transaction)) {
|
||||
Log::debug('validateForeignCurrencyInformation: no foreign currency information present at all.');
|
||||
|
||||
continue;
|
||||
}
|
||||
// if the currency is present, then the amount must be present as well.
|
||||
if ((array_key_exists('foreign_currency_id', $transaction) || array_key_exists('foreign_currency_code', $transaction))
|
||||
&& !array_key_exists(
|
||||
'foreign_amount',
|
||||
$transaction
|
||||
)) {
|
||||
$validator->errors()->add(
|
||||
'transactions.'.$index.'.foreign_amount',
|
||||
(string)trans('validation.require_currency_amount')
|
||||
);
|
||||
$foreignAmount = (string) ($transaction['foreign_amount'] ?? '');
|
||||
$foreignId = $transaction['foreign_currency_id'] ?? null;
|
||||
$foreignCode = $transaction['foreign_currency_code'] ?? null;
|
||||
if ('' === $foreignAmount) {
|
||||
Log::debug('validateForeignCurrencyInformation: foreign amount is "".');
|
||||
if (
|
||||
(array_key_exists('foreign_currency_id', $transaction) || array_key_exists('foreign_currency_code', $transaction))
|
||||
&& (null !== $foreignId || null !== $foreignCode)
|
||||
) {
|
||||
$validator->errors()->add('transactions.'.$index.'.foreign_amount', (string) trans('validation.require_currency_amount'));
|
||||
$validator->errors()->add('transactions.'.$index.'.foreign_currency_id', (string) trans('validation.require_currency_amount'));
|
||||
$validator->errors()->add('transactions.'.$index.'.foreign_currency_code', (string) trans('validation.require_currency_amount'));
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
$compare = bccomp('0', $transaction['foreign_amount']);
|
||||
if (-1 === $compare) {
|
||||
Log::debug('validateForeignCurrencyInformation: array contains foreign amount info.');
|
||||
if (!array_key_exists('foreign_currency_id', $transaction) && !array_key_exists('foreign_currency_code', $transaction)) {
|
||||
Log::debug('validateForeignCurrencyInformation: array contains NO foreign currency info.');
|
||||
$validator->errors()->add('transactions.'.$index.'.foreign_amount', (string) trans('validation.require_currency_info'));
|
||||
}
|
||||
}
|
||||
if (0 === $compare && ('' !== (string) $foreignId || '' !== (string) $foreignCode)) {
|
||||
Log::debug('validateForeignCurrencyInformation: array contains foreign currency info, but zero amount.');
|
||||
$validator->errors()->add('transactions.'.$index.'.foreign_currency_id', (string) trans('validation.require_currency_amount'));
|
||||
$validator->errors()->add('transactions.'.$index.'.foreign_currency_code', (string) trans('validation.require_currency_amount'));
|
||||
$validator->errors()->add('transactions.'.$index.'.foreign_amount', (string) trans('validation.require_currency_amount'));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
17
changelog.md
17
changelog.md
@@ -3,6 +3,23 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
This project adheres to [Semantic Versioning](http://semver.org/).
|
||||
|
||||
## 6.1.5 - 2024-01-07
|
||||
|
||||
### Added
|
||||
|
||||
- More audit logs
|
||||
- Sanity check in date ranges
|
||||
- More uniform length and size validations
|
||||
|
||||
### Changed
|
||||
|
||||
- Slightly changed text, thanks @maureenferreira!
|
||||
|
||||
### Fixed
|
||||
|
||||
- [Issue 8328](https://github.com/firefly-iii/firefly-iii/issues/8328) Some extra fixes for non-zero foreign amounts
|
||||
- Updated links in `.env.example`, thanks @lemuelroberto!
|
||||
|
||||
## 6.1.4 - 2024-01-03
|
||||
|
||||
### Fixed
|
||||
|
102
composer.lock
generated
102
composer.lock
generated
@@ -5721,16 +5721,16 @@
|
||||
},
|
||||
{
|
||||
"name": "spatie/ignition",
|
||||
"version": "1.11.3",
|
||||
"version": "1.12.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/spatie/ignition.git",
|
||||
"reference": "3d886de644ff7a5b42e4d27c1e1f67c8b5f00044"
|
||||
"reference": "5b6f801c605a593106b623e45ca41496a6e7d56d"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/spatie/ignition/zipball/3d886de644ff7a5b42e4d27c1e1f67c8b5f00044",
|
||||
"reference": "3d886de644ff7a5b42e4d27c1e1f67c8b5f00044",
|
||||
"url": "https://api.github.com/repos/spatie/ignition/zipball/5b6f801c605a593106b623e45ca41496a6e7d56d",
|
||||
"reference": "5b6f801c605a593106b623e45ca41496a6e7d56d",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -5800,20 +5800,20 @@
|
||||
"type": "github"
|
||||
}
|
||||
],
|
||||
"time": "2023-10-18T14:09:40+00:00"
|
||||
"time": "2024-01-03T15:49:39+00:00"
|
||||
},
|
||||
{
|
||||
"name": "spatie/laravel-html",
|
||||
"version": "3.3.0",
|
||||
"version": "3.4.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/spatie/laravel-html.git",
|
||||
"reference": "00faf80c459ca2a4cd9c6fe9c0e1a16b89216c2e"
|
||||
"reference": "20bd3185ae085b2eced952bc5191cb8eb922250e"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/spatie/laravel-html/zipball/00faf80c459ca2a4cd9c6fe9c0e1a16b89216c2e",
|
||||
"reference": "00faf80c459ca2a4cd9c6fe9c0e1a16b89216c2e",
|
||||
"url": "https://api.github.com/repos/spatie/laravel-html/zipball/20bd3185ae085b2eced952bc5191cb8eb922250e",
|
||||
"reference": "20bd3185ae085b2eced952bc5191cb8eb922250e",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -5870,7 +5870,7 @@
|
||||
"spatie"
|
||||
],
|
||||
"support": {
|
||||
"source": "https://github.com/spatie/laravel-html/tree/3.3.0"
|
||||
"source": "https://github.com/spatie/laravel-html/tree/3.4.0"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@@ -5878,39 +5878,39 @@
|
||||
"type": "custom"
|
||||
}
|
||||
],
|
||||
"time": "2023-10-24T06:34:29+00:00"
|
||||
"time": "2024-01-05T16:35:10+00:00"
|
||||
},
|
||||
{
|
||||
"name": "spatie/laravel-ignition",
|
||||
"version": "2.3.3",
|
||||
"version": "2.4.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/spatie/laravel-ignition.git",
|
||||
"reference": "66499cd3c858642ded56dafb8fa0352057ca20dd"
|
||||
"reference": "b9395ba48d3f30d42092cf6ceff75ed7256cd604"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/spatie/laravel-ignition/zipball/66499cd3c858642ded56dafb8fa0352057ca20dd",
|
||||
"reference": "66499cd3c858642ded56dafb8fa0352057ca20dd",
|
||||
"url": "https://api.github.com/repos/spatie/laravel-ignition/zipball/b9395ba48d3f30d42092cf6ceff75ed7256cd604",
|
||||
"reference": "b9395ba48d3f30d42092cf6ceff75ed7256cd604",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"ext-curl": "*",
|
||||
"ext-json": "*",
|
||||
"ext-mbstring": "*",
|
||||
"illuminate/support": "^10.0",
|
||||
"illuminate/support": "^10.0|^11.0",
|
||||
"php": "^8.1",
|
||||
"spatie/flare-client-php": "^1.3.5",
|
||||
"spatie/ignition": "^1.9",
|
||||
"symfony/console": "^6.2.3",
|
||||
"symfony/var-dumper": "^6.2.3"
|
||||
"symfony/console": "^6.2.3|^7.0",
|
||||
"symfony/var-dumper": "^6.2.3|^7.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"livewire/livewire": "^2.11",
|
||||
"livewire/livewire": "^2.11|^3.3.5",
|
||||
"mockery/mockery": "^1.5.1",
|
||||
"openai-php/client": "^0.3.4",
|
||||
"orchestra/testbench": "^8.0",
|
||||
"pestphp/pest": "^1.22.3",
|
||||
"openai-php/client": "^0.8.1",
|
||||
"orchestra/testbench": "^8.0|^9.0",
|
||||
"pestphp/pest": "^2.30",
|
||||
"phpstan/extension-installer": "^1.2",
|
||||
"phpstan/phpstan-deprecation-rules": "^1.1.1",
|
||||
"phpstan/phpstan-phpunit": "^1.3.3",
|
||||
@@ -5970,7 +5970,7 @@
|
||||
"type": "github"
|
||||
}
|
||||
],
|
||||
"time": "2023-12-21T09:43:05+00:00"
|
||||
"time": "2024-01-04T14:51:24+00:00"
|
||||
},
|
||||
{
|
||||
"name": "spatie/period",
|
||||
@@ -9494,36 +9494,36 @@
|
||||
},
|
||||
{
|
||||
"name": "larastan/larastan",
|
||||
"version": "v2.7.0",
|
||||
"version": "v2.8.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/larastan/larastan.git",
|
||||
"reference": "a2610d46b9999cf558d9900ccb641962d1442f55"
|
||||
"reference": "d60c1a6d49fcbb54b78922a955a55820abdbe3c7"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/larastan/larastan/zipball/a2610d46b9999cf558d9900ccb641962d1442f55",
|
||||
"reference": "a2610d46b9999cf558d9900ccb641962d1442f55",
|
||||
"url": "https://api.github.com/repos/larastan/larastan/zipball/d60c1a6d49fcbb54b78922a955a55820abdbe3c7",
|
||||
"reference": "d60c1a6d49fcbb54b78922a955a55820abdbe3c7",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"ext-json": "*",
|
||||
"illuminate/console": "^9.52.16 || ^10.28.0",
|
||||
"illuminate/container": "^9.52.16 || ^10.28.0",
|
||||
"illuminate/contracts": "^9.52.16 || ^10.28.0",
|
||||
"illuminate/database": "^9.52.16 || ^10.28.0",
|
||||
"illuminate/http": "^9.52.16 || ^10.28.0",
|
||||
"illuminate/pipeline": "^9.52.16 || ^10.28.0",
|
||||
"illuminate/support": "^9.52.16 || ^10.28.0",
|
||||
"illuminate/console": "^9.52.16 || ^10.28.0 || ^11.0",
|
||||
"illuminate/container": "^9.52.16 || ^10.28.0 || ^11.0",
|
||||
"illuminate/contracts": "^9.52.16 || ^10.28.0 || ^11.0",
|
||||
"illuminate/database": "^9.52.16 || ^10.28.0 || ^11.0",
|
||||
"illuminate/http": "^9.52.16 || ^10.28.0 || ^11.0",
|
||||
"illuminate/pipeline": "^9.52.16 || ^10.28.0 || ^11.0",
|
||||
"illuminate/support": "^9.52.16 || ^10.28.0 || ^11.0",
|
||||
"php": "^8.0.2",
|
||||
"phpmyadmin/sql-parser": "^5.8.2",
|
||||
"phpstan/phpstan": "^1.10.41"
|
||||
"phpstan/phpstan": "^1.10.50"
|
||||
},
|
||||
"require-dev": {
|
||||
"nikic/php-parser": "^4.17.1",
|
||||
"orchestra/canvas": "^7.11.1 || ^8.11.0",
|
||||
"orchestra/testbench": "^7.33.0 || ^8.13.0",
|
||||
"phpunit/phpunit": "^9.6.13"
|
||||
"orchestra/canvas": "^7.11.1 || ^8.11.0 || ^9.0.0",
|
||||
"orchestra/testbench": "^7.33.0 || ^8.13.0 || ^9.0.0",
|
||||
"phpunit/phpunit": "^9.6.13 || ^10.5"
|
||||
},
|
||||
"suggest": {
|
||||
"orchestra/testbench": "Using Larastan for analysing a package needs Testbench"
|
||||
@@ -9571,7 +9571,7 @@
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/larastan/larastan/issues",
|
||||
"source": "https://github.com/larastan/larastan/tree/v2.7.0"
|
||||
"source": "https://github.com/larastan/larastan/tree/v2.8.0"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@@ -9591,7 +9591,7 @@
|
||||
"type": "patreon"
|
||||
}
|
||||
],
|
||||
"time": "2023-12-04T19:21:38+00:00"
|
||||
"time": "2024-01-02T22:09:07+00:00"
|
||||
},
|
||||
{
|
||||
"name": "mockery/mockery",
|
||||
@@ -10146,16 +10146,16 @@
|
||||
},
|
||||
{
|
||||
"name": "phpstan/phpdoc-parser",
|
||||
"version": "1.24.5",
|
||||
"version": "1.25.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/phpstan/phpdoc-parser.git",
|
||||
"reference": "fedf211ff14ec8381c9bf5714e33a7a552dd1acc"
|
||||
"reference": "bd84b629c8de41aa2ae82c067c955e06f1b00240"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/fedf211ff14ec8381c9bf5714e33a7a552dd1acc",
|
||||
"reference": "fedf211ff14ec8381c9bf5714e33a7a552dd1acc",
|
||||
"url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/bd84b629c8de41aa2ae82c067c955e06f1b00240",
|
||||
"reference": "bd84b629c8de41aa2ae82c067c955e06f1b00240",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -10187,22 +10187,22 @@
|
||||
"description": "PHPDoc parser with support for nullable, intersection and generic types",
|
||||
"support": {
|
||||
"issues": "https://github.com/phpstan/phpdoc-parser/issues",
|
||||
"source": "https://github.com/phpstan/phpdoc-parser/tree/1.24.5"
|
||||
"source": "https://github.com/phpstan/phpdoc-parser/tree/1.25.0"
|
||||
},
|
||||
"time": "2023-12-16T09:33:33+00:00"
|
||||
"time": "2024-01-04T17:06:16+00:00"
|
||||
},
|
||||
{
|
||||
"name": "phpstan/phpstan",
|
||||
"version": "1.10.50",
|
||||
"version": "1.10.54",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/phpstan/phpstan.git",
|
||||
"reference": "06a98513ac72c03e8366b5a0cb00750b487032e4"
|
||||
"reference": "3e25f279dada0adc14ffd7bad09af2e2fc3523bb"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/phpstan/phpstan/zipball/06a98513ac72c03e8366b5a0cb00750b487032e4",
|
||||
"reference": "06a98513ac72c03e8366b5a0cb00750b487032e4",
|
||||
"url": "https://api.github.com/repos/phpstan/phpstan/zipball/3e25f279dada0adc14ffd7bad09af2e2fc3523bb",
|
||||
"reference": "3e25f279dada0adc14ffd7bad09af2e2fc3523bb",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -10251,7 +10251,7 @@
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2023-12-13T10:59:42+00:00"
|
||||
"time": "2024-01-05T15:50:47+00:00"
|
||||
},
|
||||
{
|
||||
"name": "phpstan/phpstan-deprecation-rules",
|
||||
|
@@ -63,6 +63,7 @@ use FireflyIII\Support\Binder\TagList;
|
||||
use FireflyIII\Support\Binder\TagOrId;
|
||||
use FireflyIII\Support\Binder\UserGroupAccount;
|
||||
use FireflyIII\Support\Binder\UserGroupBill;
|
||||
use FireflyIII\Support\Binder\UserGroupTransaction;
|
||||
use FireflyIII\TransactionRules\Actions\AddTag;
|
||||
use FireflyIII\TransactionRules\Actions\AppendDescription;
|
||||
use FireflyIII\TransactionRules\Actions\AppendDescriptionToNotes;
|
||||
@@ -114,7 +115,7 @@ return [
|
||||
'handle_debts' => true,
|
||||
// see cer.php for exchange rates feature flag.
|
||||
],
|
||||
'version' => '6.1.4',
|
||||
'version' => '6.1.5',
|
||||
'api_version' => '2.0.12',
|
||||
'db_version' => 22,
|
||||
|
||||
@@ -485,6 +486,7 @@ return [
|
||||
|
||||
// V2 API endpoints:
|
||||
'userGroupAccount' => UserGroupAccount::class,
|
||||
'userGroupTransaction' => UserGroupTransaction::class,
|
||||
'userGroupBill' => UserGroupBill::class,
|
||||
'userGroup' => UserGroup::class,
|
||||
],
|
||||
|
@@ -210,8 +210,10 @@ return [
|
||||
'amount_exactly' => ['alias' => true, 'alias_for' => 'amount_is', 'needs_context' => true],
|
||||
'amount_less' => ['alias' => false, 'needs_context' => true],
|
||||
'amount_max' => ['alias' => true, 'alias_for' => 'amount_less', 'needs_context' => true],
|
||||
'less' => ['alias' => true, 'alias_for' => 'amount_less', 'needs_context' => true],
|
||||
'amount_more' => ['alias' => false, 'needs_context' => true],
|
||||
'amount_min' => ['alias' => true, 'alias_for' => 'amount_more', 'needs_context' => true],
|
||||
'more' => ['alias' => true, 'alias_for' => 'amount_more', 'needs_context' => true],
|
||||
'foreign_amount_is' => ['alias' => false, 'needs_context' => true],
|
||||
'foreign_amount' => ['alias' => true, 'alias_for' => 'foreign_amount_is', 'needs_context' => true],
|
||||
'foreign_amount_less' => ['alias' => false, 'needs_context' => true],
|
||||
|
26
package-lock.json
generated
26
package-lock.json
generated
@@ -443,12 +443,12 @@
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/axios": {
|
||||
"version": "1.6.3",
|
||||
"resolved": "https://registry.npmjs.org/axios/-/axios-1.6.3.tgz",
|
||||
"integrity": "sha512-fWyNdeawGam70jXSVlKl+SUNVcL6j6W79CuSIPfi6HnDUmSCH6gyUys/HrqHeA/wU0Az41rRgean494d0Jb+ww==",
|
||||
"version": "1.6.5",
|
||||
"resolved": "https://registry.npmjs.org/axios/-/axios-1.6.5.tgz",
|
||||
"integrity": "sha512-Ii012v05KEVuUoFWmMW/UQv9aRIc3ZwkWDcM+h5Il8izZCtRVpDUfwpoFf7eOtajT3QiGR4yDUx7lPqHJULgbg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"follow-redirects": "^1.15.0",
|
||||
"follow-redirects": "^1.15.4",
|
||||
"form-data": "^4.0.0",
|
||||
"proxy-from-env": "^1.1.0"
|
||||
}
|
||||
@@ -578,9 +578,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/date-fns": {
|
||||
"version": "3.0.6",
|
||||
"resolved": "https://registry.npmjs.org/date-fns/-/date-fns-3.0.6.tgz",
|
||||
"integrity": "sha512-W+G99rycpKMMF2/YD064b2lE7jJGUe+EjOES7Q8BIGY8sbNdbgcs9XFTZwvzc9Jx1f3k7LB7gZaZa7f8Agzljg==",
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/date-fns/-/date-fns-3.1.0.tgz",
|
||||
"integrity": "sha512-ZO7yefXV/wCWzd3I9haCHmfzlfA3i1a2HHO7ZXjtJrRjXt8FULKJ2Vl8wji3XYF4dQ0ZJ/tokXDZeYlFvgms9Q==",
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/kossnocorp"
|
||||
@@ -860,9 +860,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/postcss": {
|
||||
"version": "8.4.32",
|
||||
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.32.tgz",
|
||||
"integrity": "sha512-D/kj5JNu6oo2EIy+XL/26JEDTlIbB8hw85G8StOE6L74RQAVVP5rej6wxCNqyMbR4RkPfqvezVbPw81Ngd6Kcw==",
|
||||
"version": "8.4.33",
|
||||
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.33.tgz",
|
||||
"integrity": "sha512-Kkpbhhdjw2qQs2O2DGX+8m5OVqEcbB9HRBvuYM9pgrjEFUg30A9LmXNlTAUj4S9kgtGyrMbTzVjH7E+s5Re2yg==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
{
|
||||
@@ -922,9 +922,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/sass": {
|
||||
"version": "1.69.6",
|
||||
"resolved": "https://registry.npmjs.org/sass/-/sass-1.69.6.tgz",
|
||||
"integrity": "sha512-qbRr3k9JGHWXCvZU77SD2OTwUlC+gNT+61JOLcmLm+XqH4h/5D+p4IIsxvpkB89S9AwJOyb5+rWNpIucaFxSFQ==",
|
||||
"version": "1.69.7",
|
||||
"resolved": "https://registry.npmjs.org/sass/-/sass-1.69.7.tgz",
|
||||
"integrity": "sha512-rzj2soDeZ8wtE2egyLXgOOHQvaC2iosZrkF6v3EUG+tBwEvhqUCzm0VP3k9gHF9LXbSrRhT5SksoI56Iw8NPnQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"chokidar": ">=3.0.0 <4.0.0",
|
||||
|
1
public/build/assets/autocomplete-functions-31caaca5.js
Normal file
1
public/build/assets/autocomplete-functions-31caaca5.js
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
1
public/build/assets/create-d5d549ca.js
Normal file
1
public/build/assets/create-d5d549ca.js
Normal file
File diff suppressed because one or more lines are too long
1
public/build/assets/dashboard-e693d6f4.js
Normal file
1
public/build/assets/dashboard-e693d6f4.js
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
1
public/build/assets/edit-6247ff45.js
Normal file
1
public/build/assets/edit-6247ff45.js
Normal file
File diff suppressed because one or more lines are too long
1
public/build/assets/get-c2292133.js
Normal file
1
public/build/assets/get-c2292133.js
Normal file
@@ -0,0 +1 @@
|
||||
import{a as s}from"./get-c53daca3.js";class p{list(a){return s.get("/api/v2/transactions",{params:a})}show(a,t){return s.get("/api/v2/transactions/"+a,{params:t})}}export{p as G};
|
File diff suppressed because one or more lines are too long
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user