mirror of
https://github.com/firefly-iii/firefly-iii.git
synced 2025-08-27 16:05:37 +00:00
Compare commits
58 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
2c3f032a2b | ||
|
de57ab0874 | ||
|
6fb4aaecd3 | ||
|
45fdbf5a11 | ||
|
c6615a7b17 | ||
|
0efb3d2dcf | ||
|
b8a58f83ee | ||
|
110228e65e | ||
|
8ad27e0eda | ||
|
2e0d90c685 | ||
|
bd2ecb13b8 | ||
|
5725570dbb | ||
|
11f77685e4 | ||
|
0521c46d27 | ||
|
50d6225590 | ||
|
df55f7de79 | ||
|
5152ae9622 | ||
|
075d0da63d | ||
|
d804df2a2f | ||
|
ff0f8beb81 | ||
|
c00be92f97 | ||
|
88f6221424 | ||
|
f9463e02a2 | ||
|
25a23801be | ||
|
fe7bb02dc5 | ||
|
68edcfc4e8 | ||
|
5f8a24a684 | ||
|
0a5d62605a | ||
|
1873be8d95 | ||
|
01892c3828 | ||
|
b87e60c72f | ||
|
3a083f88b5 | ||
|
566bb2f097 | ||
|
1ba7847d84 | ||
|
c32044a8eb | ||
|
72a2d417af | ||
|
09c18d6d44 | ||
|
84ae6a633e | ||
|
82749cea07 | ||
|
23aa0e3ba3 | ||
|
8be27a2201 | ||
|
ff98f3cc3e | ||
|
01c4d25646 | ||
|
292b9ac9d0 | ||
|
6bdae03961 | ||
|
7426c6aac3 | ||
|
211526c032 | ||
|
e6fe08dd61 | ||
|
7bba67130a | ||
|
7186d8ddfd | ||
|
1a6bc6decd | ||
|
5b11c86113 | ||
|
98b95ab891 | ||
|
c3068d10bf | ||
|
fa1a1b084b | ||
|
387e44b8b9 | ||
|
6dafa89a15 | ||
|
5b5b8008b0 |
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',
|
||||
|
@@ -30,6 +30,7 @@ use FireflyIII\Rules\BelongsUser;
|
||||
use FireflyIII\Rules\IsBoolean;
|
||||
use FireflyIII\Rules\IsDateOrTime;
|
||||
use FireflyIII\Rules\IsValidPositiveAmount;
|
||||
use FireflyIII\Rules\IsValidZeroOrMoreAmount;
|
||||
use FireflyIII\Support\Request\ChecksLogin;
|
||||
use FireflyIII\Support\Request\ConvertsDataTypes;
|
||||
use FireflyIII\Validation\GroupValidation;
|
||||
@@ -98,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):
|
||||
@@ -117,32 +118,32 @@ class UpdateRequest extends FormRequest
|
||||
|
||||
// amount
|
||||
'transactions.*.amount' => ['nullable', 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_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,81 +91,84 @@ class StoreRequest extends FormRequest
|
||||
|
||||
return [
|
||||
// basic fields for group:
|
||||
'group_title' => 'between:1,1000|nullable',
|
||||
'error_if_duplicate_hash' => [new IsBoolean()],
|
||||
'apply_rules' => [new IsBoolean()],
|
||||
'group_title' => 'min:1|max:1000|nullable',
|
||||
'error_if_duplicate_hash' => [new IsBoolean()],
|
||||
'apply_rules' => [new IsBoolean()],
|
||||
|
||||
// transaction rules (in array for splits):
|
||||
'transactions.*.type' => 'required|in:withdrawal,deposit,transfer,opening-balance,reconciliation',
|
||||
'transactions.*.date' => ['required', new IsDateOrTime()],
|
||||
'transactions.*.order' => 'numeric|min:0',
|
||||
'transactions.*.type' => 'required|in:withdrawal,deposit,transfer,opening-balance,reconciliation',
|
||||
'transactions.*.date' => ['required', new IsDateOrTime()],
|
||||
'transactions.*.order' => 'numeric|min:0',
|
||||
|
||||
// currency info
|
||||
'transactions.*.currency_id' => 'numeric|exists:transaction_currencies,id|nullable',
|
||||
'transactions.*.currency_code' => 'min:3|max:51|exists:transaction_currencies,code|nullable',
|
||||
'transactions.*.foreign_currency_id' => 'numeric|exists:transaction_currencies,id|nullable',
|
||||
'transactions.*.foreign_currency_code' => 'min:3|max:51|exists:transaction_currencies,code|nullable',
|
||||
'transactions.*.currency_id' => 'numeric|exists:transaction_currencies,id|nullable',
|
||||
'transactions.*.currency_code' => 'min:3|max:51|exists:transaction_currencies,code|nullable',
|
||||
'transactions.*.foreign_currency_id' => 'numeric|exists:transaction_currencies,id|nullable',
|
||||
'transactions.*.foreign_currency_code' => 'min:3|max:51|exists:transaction_currencies,code|nullable',
|
||||
|
||||
// amount
|
||||
'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_id' => ['numeric', 'nullable', new BelongsUserGroup($userGroup)],
|
||||
'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_id' => ['numeric', 'nullable', new BelongsUserGroup($userGroup)],
|
||||
'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.*.category_id' => ['mustExist:categories,id', new BelongsUserGroup($userGroup), 'nullable'],
|
||||
'transactions.*.category_name' => 'between:1,255|nullable',
|
||||
'transactions.*.bill_id' => ['numeric', 'nullable', 'mustExist:bills,id', new BelongsUserGroup($userGroup)],
|
||||
'transactions.*.bill_name' => ['between:1,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.*.budget_id' => ['mustExist:budgets,id', 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' => 'min:1|max:255|nullable',
|
||||
'transactions.*.bill_id' => ['numeric', 'nullable', 'mustExist:bills,id', 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' => ['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.*.reconciled' => [new IsBoolean()],
|
||||
'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',
|
||||
'transactions.*.external_id' => 'min:1|max:255|nullable',
|
||||
'transactions.*.recurrence_id' => 'min:1|max:255|nullable',
|
||||
'transactions.*.bunq_payment_id' => 'min:1|max:255|nullable',
|
||||
'transactions.*.external_url' => 'min:1|max:255|nullable|url',
|
||||
'transactions.*.internal_reference' => 'min:1|max:255|nullable',
|
||||
'transactions.*.external_id' => 'min:1|max:255|nullable',
|
||||
'transactions.*.recurrence_id' => 'min:1|max:255|nullable',
|
||||
'transactions.*.bunq_payment_id' => 'min:1|max:255|nullable',
|
||||
'transactions.*.external_url' => 'min:1|max:255|nullable|url',
|
||||
|
||||
// SEPA fields:
|
||||
'transactions.*.sepa_cc' => 'min:1|max:255|nullable',
|
||||
'transactions.*.sepa_ct_op' => 'min:1|max:255|nullable',
|
||||
'transactions.*.sepa_ct_id' => 'min:1|max:255|nullable',
|
||||
'transactions.*.sepa_db' => 'min:1|max:255|nullable',
|
||||
'transactions.*.sepa_country' => 'min:1|max:255|nullable',
|
||||
'transactions.*.sepa_ep' => 'min:1|max:255|nullable',
|
||||
'transactions.*.sepa_ci' => 'min:1|max:255|nullable',
|
||||
'transactions.*.sepa_batch_id' => 'min:1|max:255|nullable',
|
||||
'transactions.*.sepa_cc' => 'min:1|max:255|nullable',
|
||||
'transactions.*.sepa_ct_op' => 'min:1|max:255|nullable',
|
||||
'transactions.*.sepa_ct_id' => 'min:1|max:255|nullable',
|
||||
'transactions.*.sepa_db' => 'min:1|max:255|nullable',
|
||||
'transactions.*.sepa_country' => 'min:1|max:255|nullable',
|
||||
'transactions.*.sepa_ep' => 'min:1|max:255|nullable',
|
||||
'transactions.*.sepa_ci' => 'min:1|max:255|nullable',
|
||||
'transactions.*.sepa_batch_id' => 'min:1|max:255|nullable',
|
||||
|
||||
// dates
|
||||
'transactions.*.interest_date' => 'date|nullable',
|
||||
'transactions.*.book_date' => 'date|nullable',
|
||||
'transactions.*.process_date' => 'date|nullable',
|
||||
'transactions.*.due_date' => 'date|nullable',
|
||||
'transactions.*.payment_date' => 'date|nullable',
|
||||
'transactions.*.invoice_date' => 'date|nullable',
|
||||
'transactions.*.interest_date' => 'date|nullable',
|
||||
'transactions.*.book_date' => 'date|nullable',
|
||||
'transactions.*.process_date' => 'date|nullable',
|
||||
'transactions.*.due_date' => 'date|nullable',
|
||||
'transactions.*.payment_date' => 'date|nullable',
|
||||
'transactions.*.invoice_date' => 'date|nullable',
|
||||
|
||||
// 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,23 +101,43 @@ 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)));
|
||||
$subTitleIcon = 'fa-plus';
|
||||
$optionalFields = app('preferences')->get('transaction_journal_optional_fields', [])->data;
|
||||
$allowedOpposingTypes = config('firefly.allowed_opposing_types');
|
||||
$accountToTypes = config('firefly.account_to_transaction');
|
||||
$defaultCurrency = app('amount')->getDefaultCurrency();
|
||||
$previousUrl = $this->rememberPreviousUrl('transactions.create.url');
|
||||
$parts = parse_url($previousUrl);
|
||||
$search = sprintf('?%s', $parts['query'] ?? '');
|
||||
$previousUrl = str_replace($search, '', $previousUrl);
|
||||
$accountRepository = app(AccountRepositoryInterface::class);
|
||||
$cash = $accountRepository->getCashAccount();
|
||||
$preFilled = session()->has('preFilled') ? session('preFilled') : [];
|
||||
$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');
|
||||
$accountToTypes = config('firefly.account_to_transaction');
|
||||
$defaultCurrency = app('amount')->getDefaultCurrency();
|
||||
$previousUrl = $this->rememberPreviousUrl('transactions.create.url');
|
||||
$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);
|
||||
@@ -73,18 +73,43 @@ class EditController extends Controller
|
||||
}
|
||||
|
||||
/** @var AccountRepositoryInterface $repository */
|
||||
$repository = app(AccountRepositoryInterface::class);
|
||||
$allowedOpposingTypes = config('firefly.allowed_opposing_types');
|
||||
$accountToTypes = config('firefly.account_to_transaction');
|
||||
$expectedSourceTypes = config('firefly.expected_source_types');
|
||||
$allowedSourceDests = config('firefly.source_dests');
|
||||
$repository = app(AccountRepositoryInterface::class);
|
||||
$allowedOpposingTypes = config('firefly.allowed_opposing_types');
|
||||
$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');
|
||||
$parts = parse_url($previousUrl);
|
||||
$search = sprintf('?%s', $parts['query'] ?? '');
|
||||
$previousUrl = str_replace($search, '', $previousUrl);
|
||||
|
||||
$defaultCurrency = app('amount')->getDefaultCurrency();
|
||||
$cash = $repository->getCashAccount();
|
||||
$previousUrl = $this->rememberPreviousUrl('transactions.edit.url');
|
||||
$parts = parse_url($previousUrl);
|
||||
$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',
|
||||
@@ -92,6 +117,13 @@ class EditController extends Controller
|
||||
'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,25 +42,31 @@ 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;
|
||||
}
|
||||
|
||||
// must be more than minus a lots:
|
||||
if($this->lessThanLots($value)) {
|
||||
$amount = bcmul('-1', self::BIG_AMOUNT);
|
||||
$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));
|
||||
}
|
||||
}
|
||||
|
67
app/Rules/IsValidZeroOrMoreAmount.php
Normal file
67
app/Rules/IsValidZeroOrMoreAmount.php
Normal file
@@ -0,0 +1,67 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\Rules;
|
||||
|
||||
use FireflyIII\Support\Validation\ValidatesAmountsTrait;
|
||||
use Illuminate\Contracts\Validation\ValidationRule;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
|
||||
class IsValidZeroOrMoreAmount implements ValidationRule
|
||||
{
|
||||
use ValidatesAmountsTrait;
|
||||
|
||||
/**
|
||||
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
|
||||
*/
|
||||
public function validate(string $attribute, mixed $value, \Closure $fail): void
|
||||
{
|
||||
$value = (string)$value;
|
||||
// must not be empty:
|
||||
if($this->emptyString($value)) {
|
||||
$fail('validation.filled')->translate();
|
||||
$message = sprintf('IsValidZeroOrMoreAmount: "%s" cannot be empty.', $value);
|
||||
Log::debug($message);
|
||||
Log::channel('audit')->info($message);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// must be a number:
|
||||
if(!$this->isValidNumber($value)) {
|
||||
$fail('validation.numeric')->translate();
|
||||
$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();
|
||||
$message = sprintf('IsValidZeroOrMoreAmount: "%s" cannot be in the scientific notation.', $value);
|
||||
Log::debug($message);
|
||||
Log::channel('audit')->info($message);
|
||||
|
||||
return;
|
||||
}
|
||||
// must be zero or more
|
||||
if(!$this->zeroOrMore($value)) {
|
||||
$fail('validation.more_than_zero_correct')->translate();
|
||||
$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)) {
|
||||
$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::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');
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -40,13 +40,13 @@ class UserGroupAccount implements BinderInterface
|
||||
{
|
||||
if (auth()->check()) {
|
||||
/** @var User $user */
|
||||
$user = auth()->user();
|
||||
$currency = Account::where('id', (int)$value)
|
||||
$user = auth()->user();
|
||||
$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
|
||||
{
|
||||
|
@@ -47,6 +47,11 @@ trait ValidatesAmountsTrait
|
||||
return -1 === bccomp($value, '0') || 0 === bccomp($value, '0');
|
||||
}
|
||||
|
||||
final protected function zeroOrMore(string $value): bool
|
||||
{
|
||||
return 1 === bccomp($value, '0') || 0 === bccomp($value, '0');
|
||||
}
|
||||
|
||||
final protected function moreThanLots(string $value): bool
|
||||
{
|
||||
return 1 === bccomp($value, self::BIG_AMOUNT) || 0 === bccomp($value, self::BIG_AMOUNT);
|
||||
|
@@ -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,99 +40,232 @@ 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);
|
||||
}
|
||||
|
||||
/** @var array $transaction */
|
||||
foreach ($object['transactions'] as $transaction) {
|
||||
$id = (int) $transaction['transaction_journal_id'];
|
||||
$journals[$id] = [];
|
||||
if ($object instanceof TransactionGroup) {
|
||||
$this->collectForObject($object);
|
||||
$collectForObjects = true;
|
||||
}
|
||||
}
|
||||
$this->currencies = $currencies;
|
||||
$this->default = app('amount')->getDefaultCurrency();
|
||||
|
||||
// grab meta for all journals:
|
||||
$meta = TransactionJournalMeta::whereIn('transaction_journal_id', array_keys($journals))->get();
|
||||
$this->default = app('amount')->getDefaultCurrency();
|
||||
$this->converter = new ExchangeRateConverter();
|
||||
|
||||
/** @var TransactionJournalMeta $entry */
|
||||
foreach ($meta as $entry) {
|
||||
$id = $entry->transaction_journal_id;
|
||||
$this->meta[$id][$entry->name] = $entry->data;
|
||||
$this->collectAllMetaData();
|
||||
$this->collectAllNotes();
|
||||
$this->collectAllLocations();
|
||||
$this->collectAllTags();
|
||||
if ($collectForObjects) {
|
||||
$this->collectAllCurrencies();
|
||||
// $this->collectAllAmounts();
|
||||
// $this->collectTransactionTypes();
|
||||
// $this->collectAccounts();
|
||||
// source accounts
|
||||
// destination accounts
|
||||
}
|
||||
|
||||
// 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();
|
||||
}
|
||||
|
||||
public function transform(array $group): array
|
||||
public function transform(array|TransactionGroup $group): array
|
||||
{
|
||||
$first = reset($group['transactions']);
|
||||
if (is_array($group)) {
|
||||
$first = reset($group['transactions']);
|
||||
|
||||
return [
|
||||
'id' => (string) $group['id'],
|
||||
'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,
|
||||
'transactions' => $this->transformTransactions($group['transactions'] ?? []),
|
||||
'links' => [
|
||||
[
|
||||
'rel' => 'self',
|
||||
'uri' => sprintf('/transactions/%d', $group['id']),
|
||||
],
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
return [
|
||||
'id' => (string) $group['id'],
|
||||
'created_at' => $first['created_at']->toAtomString(),
|
||||
'updated_at' => $first['updated_at']->toAtomString(),
|
||||
'user' => (string) $first['user_id'],
|
||||
'user_group' => (string) $first['user_group_id'],
|
||||
'group_title' => $group['title'] ?? null,
|
||||
'transactions' => $this->transformTransactions($group['transactions'] ?? []),
|
||||
'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']),
|
||||
'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', $foreignAmount);
|
||||
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'));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
35
changelog.md
35
changelog.md
@@ -3,6 +3,41 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
This project adheres to [Semantic Versioning](http://semver.org/).
|
||||
|
||||
## 6.1.6 - 2024-01-07
|
||||
|
||||
### Fixed
|
||||
|
||||
- Type validation error
|
||||
|
||||
## 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
|
||||
|
||||
- [Issue 8328](https://github.com/firefly-iii/firefly-iii/issues/8328) Asking for non-zero foreign amount despite not being used
|
||||
|
||||
## 6.1.3 - 2024-01-03
|
||||
|
||||
### Fixed
|
||||
|
||||
- [Issue 8326](https://github.com/firefly-iii/firefly-iii/issues/8326) Asking for non-zero foreign amount despite not being used
|
||||
|
||||
## 6.1.2 - 2024-01-03
|
||||
|
||||
### Changed
|
||||
|
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.2',
|
||||
'version' => '6.1.6',
|
||||
'api_version' => '2.0.12',
|
||||
'db_version' => 22,
|
||||
|
||||
@@ -433,60 +434,61 @@ return [
|
||||
|
||||
'bindables' => [
|
||||
// models
|
||||
'account' => Account::class,
|
||||
'attachment' => Attachment::class,
|
||||
'availableBudget' => AvailableBudget::class,
|
||||
'bill' => Bill::class,
|
||||
'budget' => Budget::class,
|
||||
'budgetLimit' => BudgetLimit::class,
|
||||
'category' => Category::class,
|
||||
'linkType' => LinkType::class,
|
||||
'transactionType' => TransactionTypeModel::class,
|
||||
'journalLink' => TransactionJournalLink::class,
|
||||
'currency' => TransactionCurrency::class,
|
||||
'objectGroup' => ObjectGroup::class,
|
||||
'piggyBank' => PiggyBank::class,
|
||||
'preference' => Preference::class,
|
||||
'tj' => TransactionJournal::class,
|
||||
'tag' => Tag::class,
|
||||
'recurrence' => Recurrence::class,
|
||||
'rule' => Rule::class,
|
||||
'ruleGroup' => RuleGroup::class,
|
||||
'transactionGroup' => TransactionGroup::class,
|
||||
'user' => User::class,
|
||||
'webhook' => Webhook::class,
|
||||
'webhookMessage' => WebhookMessage::class,
|
||||
'webhookAttempt' => WebhookAttempt::class,
|
||||
'invitedUser' => InvitedUser::class,
|
||||
'account' => Account::class,
|
||||
'attachment' => Attachment::class,
|
||||
'availableBudget' => AvailableBudget::class,
|
||||
'bill' => Bill::class,
|
||||
'budget' => Budget::class,
|
||||
'budgetLimit' => BudgetLimit::class,
|
||||
'category' => Category::class,
|
||||
'linkType' => LinkType::class,
|
||||
'transactionType' => TransactionTypeModel::class,
|
||||
'journalLink' => TransactionJournalLink::class,
|
||||
'currency' => TransactionCurrency::class,
|
||||
'objectGroup' => ObjectGroup::class,
|
||||
'piggyBank' => PiggyBank::class,
|
||||
'preference' => Preference::class,
|
||||
'tj' => TransactionJournal::class,
|
||||
'tag' => Tag::class,
|
||||
'recurrence' => Recurrence::class,
|
||||
'rule' => Rule::class,
|
||||
'ruleGroup' => RuleGroup::class,
|
||||
'transactionGroup' => TransactionGroup::class,
|
||||
'user' => User::class,
|
||||
'webhook' => Webhook::class,
|
||||
'webhookMessage' => WebhookMessage::class,
|
||||
'webhookAttempt' => WebhookAttempt::class,
|
||||
'invitedUser' => InvitedUser::class,
|
||||
|
||||
// strings
|
||||
'currency_code' => CurrencyCode::class,
|
||||
'currency_code' => CurrencyCode::class,
|
||||
|
||||
// dates
|
||||
'start_date' => Date::class,
|
||||
'end_date' => Date::class,
|
||||
'date' => Date::class,
|
||||
'start_date' => Date::class,
|
||||
'end_date' => Date::class,
|
||||
'date' => Date::class,
|
||||
|
||||
// lists
|
||||
'accountList' => AccountList::class,
|
||||
'doubleList' => AccountList::class,
|
||||
'budgetList' => BudgetList::class,
|
||||
'journalList' => JournalList::class,
|
||||
'categoryList' => CategoryList::class,
|
||||
'tagList' => TagList::class,
|
||||
'accountList' => AccountList::class,
|
||||
'doubleList' => AccountList::class,
|
||||
'budgetList' => BudgetList::class,
|
||||
'journalList' => JournalList::class,
|
||||
'categoryList' => CategoryList::class,
|
||||
'tagList' => TagList::class,
|
||||
|
||||
// others
|
||||
'fromCurrencyCode' => CurrencyCode::class,
|
||||
'toCurrencyCode' => CurrencyCode::class,
|
||||
'cliToken' => CLIToken::class,
|
||||
'tagOrId' => TagOrId::class,
|
||||
'dynamicConfigKey' => DynamicConfigKey::class,
|
||||
'eitherConfigKey' => EitherConfigKey::class,
|
||||
'fromCurrencyCode' => CurrencyCode::class,
|
||||
'toCurrencyCode' => CurrencyCode::class,
|
||||
'cliToken' => CLIToken::class,
|
||||
'tagOrId' => TagOrId::class,
|
||||
'dynamicConfigKey' => DynamicConfigKey::class,
|
||||
'eitherConfigKey' => EitherConfigKey::class,
|
||||
|
||||
// V2 API endpoints:
|
||||
'userGroupAccount' => UserGroupAccount::class,
|
||||
'userGroupBill' => UserGroupBill::class,
|
||||
'userGroup' => UserGroup::class,
|
||||
'userGroupAccount' => UserGroupAccount::class,
|
||||
'userGroupTransaction' => UserGroupTransaction::class,
|
||||
'userGroupBill' => UserGroupBill::class,
|
||||
'userGroup' => UserGroup::class,
|
||||
],
|
||||
'rule-actions' => [
|
||||
'set_category' => SetCategory::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};
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user