mirror of
https://github.com/firefly-iii/firefly-iii.git
synced 2025-10-16 01:06:46 +00:00
Compare commits
264 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
b8573d03a4 | ||
|
b104914076 | ||
|
1d30b58685 | ||
|
ab9f26f850 | ||
|
9a944175ac | ||
|
0bcc3240b7 | ||
|
1bf3608142 | ||
|
9dc971550c | ||
|
33c52513a3 | ||
|
2408ff11f6 | ||
|
3d130e5d99 | ||
|
bc0355c5b1 | ||
|
b90b13632e | ||
|
bad57c907c | ||
|
374793eb92 | ||
|
8a6ae6105e | ||
|
ecda8eebf2 | ||
|
f0a5257921 | ||
|
860a4bdc0a | ||
|
f6ddf72323 | ||
|
ec212bb98e | ||
|
fd7d37ac23 | ||
|
b4cd0d6bbc | ||
|
0a5908e4d8 | ||
|
59f0631685 | ||
|
7718deb4a5 | ||
|
75d52b7024 | ||
|
ecc72dd01b | ||
|
6591512cf6 | ||
|
4a0e97e36a | ||
|
f90b2d9410 | ||
|
582ba0ce5d | ||
|
ce411ab9b0 | ||
|
d90468db3f | ||
|
3a621e6ff7 | ||
|
a7ffe66547 | ||
|
4a57f4d6dd | ||
|
5b72ffa575 | ||
|
04a9e2cec2 | ||
|
17022c1174 | ||
|
59914a9d87 | ||
|
a7b9653de3 | ||
|
c8dc065e24 | ||
|
bac58ad0c2 | ||
|
6b2431b054 | ||
|
7e212ebd48 | ||
|
b3223b90da | ||
|
6ea773cdd7 | ||
|
997cddcb5a | ||
|
8228f76f5d | ||
|
b35ee9ccf4 | ||
|
16942af52b | ||
|
b190ce3538 | ||
|
9027915280 | ||
|
19aaaef37e | ||
|
a3bb90e160 | ||
|
06bae0dd8b | ||
|
7d48ab5671 | ||
|
8588405aa1 | ||
|
8238ae7d0a | ||
|
78ebec01a4 | ||
|
ac1137d7e8 | ||
|
212e9b833b | ||
|
52ad0ba352 | ||
|
318d86a1f5 | ||
|
fbffba4c21 | ||
|
c7f4e5423a | ||
|
5f282a9b7d | ||
|
7539ee3560 | ||
|
032389c6c0 | ||
|
788ac54f86 | ||
|
4e0fbc8532 | ||
|
5764d9756c | ||
|
85e79922ab | ||
|
6f3826a6ff | ||
|
c061b0b777 | ||
|
fc7bbfa8e2 | ||
|
0da20c7a1b | ||
|
4e2d5fe166 | ||
|
70767678ba | ||
|
7340e146f1 | ||
|
583bc59d23 | ||
|
ff5b872fa3 | ||
|
28517a8b78 | ||
|
34138d1533 | ||
|
ccf589f093 | ||
|
3ee100e71e | ||
|
1e0547a02c | ||
|
577e1f1b56 | ||
|
a7d25125a7 | ||
|
dbb09ef5d1 | ||
|
d840c94a29 | ||
|
114980480e | ||
|
95b1a821f3 | ||
|
206397cc81 | ||
|
a6b1a6d0c2 | ||
|
aba39a3b27 | ||
|
b5f095dd91 | ||
|
a9e92d4fa6 | ||
|
867a2eacd3 | ||
|
accecf6a76 | ||
|
f12b3bc9b7 | ||
|
a2ed755c9c | ||
|
235639b728 | ||
|
b1d5882fa6 | ||
|
39e632d950 | ||
|
87ced85657 | ||
|
0c53a8db66 | ||
|
84dcbaf0e7 | ||
|
047440eaea | ||
|
217ad0234b | ||
|
9795f46bea | ||
|
10bc0098b9 | ||
|
7a900fd3d9 | ||
|
45cfb4e565 | ||
|
5320480767 | ||
|
3e18e984fa | ||
|
fce759e98f | ||
|
f4439778f1 | ||
|
11176fc212 | ||
|
ffc71da2eb | ||
|
246fa0d6e4 | ||
|
3444146da3 | ||
|
5c4a482f64 | ||
|
01f84ea11d | ||
|
08402babbd | ||
|
7ef3dcbd23 | ||
|
8836fa205b | ||
|
f544a278c5 | ||
|
4b8fd6adb9 | ||
|
1ac8a9b4ac | ||
|
08a8a69b34 | ||
|
dbd668bda5 | ||
|
cc0760553b | ||
|
1142ff6b1f | ||
|
927da5d742 | ||
|
9a734e48fe | ||
|
345fe39b54 | ||
|
c7e9d62712 | ||
|
5918095f11 | ||
|
24612eb634 | ||
|
f7125d6198 | ||
|
47870cd50c | ||
|
45fbf83971 | ||
|
6044cffef3 | ||
|
4d933e1ef7 | ||
|
181e088ad5 | ||
|
179b86c3ba | ||
|
083c15b956 | ||
|
7c780dd75c | ||
|
d35d51c014 | ||
|
14c6926360 | ||
|
bedfcb5c5d | ||
|
19461020ef | ||
|
36ecf25804 | ||
|
0229fc243a | ||
|
a2f09b305c | ||
|
32d7a0fd1b | ||
|
5d3f95762e | ||
|
4ca9ee6eec | ||
|
9026b0bfd7 | ||
|
3d30a5938a | ||
|
bcb06779b9 | ||
|
26b70f37d6 | ||
|
b20a354344 | ||
|
753f518f77 | ||
|
33d3019ffd | ||
|
26e3157ae7 | ||
|
b592528318 | ||
|
936d55d7de | ||
|
6df45fccc5 | ||
|
f43ebcce87 | ||
|
23fc261a82 | ||
|
344db19232 | ||
|
5f777845a3 | ||
|
f32bdd6c76 | ||
|
219c3c11f9 | ||
|
b3b367fcb3 | ||
|
52a43e7f30 | ||
|
04f5098d06 | ||
|
9731503826 | ||
|
b431351a22 | ||
|
089097a41c | ||
|
4468fdd76b | ||
|
79c70c59e3 | ||
|
c8ffc81527 | ||
|
048db14a08 | ||
|
a9ee07b19a | ||
|
e316d9ce6d | ||
|
6bf354cf93 | ||
|
384ebf9341 | ||
|
b2c74ba86a | ||
|
0c915de314 | ||
|
327e7efa89 | ||
|
c361b6cc07 | ||
|
59948d6746 | ||
|
1d93cf41fd | ||
|
8bbbb05adc | ||
|
6e314b6a30 | ||
|
499b2ca7ef | ||
|
77823d3f33 | ||
|
88dfb954ca | ||
|
ddc229f270 | ||
|
8af8cc2c9a | ||
|
775492b391 | ||
|
1419176094 | ||
|
aa0ca5fdcf | ||
|
0d61a3ad5d | ||
|
bdbb3d9ad1 | ||
|
fc73bddd43 | ||
|
7515578ba7 | ||
|
84b9841886 | ||
|
6b66476927 | ||
|
7357f19f81 | ||
|
f29d8322d5 | ||
|
31a3f6f4d8 | ||
|
7432a0c5cb | ||
|
f6d58191b2 | ||
|
89fbf0869a | ||
|
b1e453438e | ||
|
284c6033d3 | ||
|
b85b32560c | ||
|
35bc92bb49 | ||
|
243a5217e7 | ||
|
b2eeeed0af | ||
|
afd4700758 | ||
|
03a1601bf3 | ||
|
edfff4ec57 | ||
|
7b5bc3a25e | ||
|
d98ca0bb44 | ||
|
518b4ba5a7 | ||
|
9c1f781be3 | ||
|
fa7c1b3ec8 | ||
|
89acb9b6f3 | ||
|
45971f8f26 | ||
|
21f12b87a0 | ||
|
d264333ab8 | ||
|
0f9c1b9427 | ||
|
c273f309d4 | ||
|
17fd2f9909 | ||
|
0436701f29 | ||
|
840632b34e | ||
|
764481690c | ||
|
66b9d7421b | ||
|
a02c1557ac | ||
|
ff8af87179 | ||
|
9c8b31fdbb | ||
|
bda1413da4 | ||
|
f1cc8a10f5 | ||
|
babf462bcf | ||
|
dba24d2dee | ||
|
42066cfca2 | ||
|
32e550c8e0 | ||
|
4f2c94a5a8 | ||
|
95076bdb6a | ||
|
547caadeb2 | ||
|
2469e1e811 | ||
|
ded267c9ac | ||
|
2c9733e739 | ||
|
72fd263ddf | ||
|
8b84a4b336 | ||
|
3caf1f2d36 | ||
|
16391fe99c | ||
|
6d3c858cbe |
@@ -69,6 +69,8 @@ DB_PORT=3306
|
||||
DB_DATABASE=firefly
|
||||
DB_USERNAME=firefly
|
||||
DB_PASSWORD=secret_firefly_password
|
||||
# leave empty or omit when not using a socket connection
|
||||
DB_SOCKET=
|
||||
|
||||
# MySQL supports SSL. You can configure it here.
|
||||
# If you use Docker or similar, you can set these variables from a file by appending them with _FILE
|
||||
@@ -89,6 +91,9 @@ PGSQL_SSL_CERT=null
|
||||
PGSQL_SSL_KEY=null
|
||||
PGSQL_SSL_CRL_FILE=null
|
||||
|
||||
# more PostgreSQL settings
|
||||
PGSQL_SCHEMA=public
|
||||
|
||||
# If you're looking for performance improvements, you could install memcached or redis
|
||||
CACHE_DRIVER=file
|
||||
SESSION_DRIVER=file
|
||||
|
3
.github/ISSUE_TEMPLATE/bug.yml
vendored
3
.github/ISSUE_TEMPLATE/bug.yml
vendored
@@ -14,8 +14,9 @@ body:
|
||||
label: I've found a bug and checked that ...
|
||||
description: Make sure that your request fulfills all of the following requirements. If one requirement cannot be satisfied, explain in detail why.
|
||||
options:
|
||||
- label: ... the documentation does not mention anything about my problem
|
||||
- label: ... [the documentation](https://docs.firefly-iii.org/) does not mention anything about my problem
|
||||
- label: ... there are no open or closed issues that are related to my problem
|
||||
- label: ... it's [definitely me, not you](https://github.com/firefly-iii/firefly-iii/blob/main/.github/its_you_not_me.md)
|
||||
|
||||
- type: textarea
|
||||
attributes:
|
||||
|
8
.github/dependabot.yml
vendored
8
.github/dependabot.yml
vendored
@@ -19,11 +19,3 @@ updates:
|
||||
schedule:
|
||||
interval: "weekly"
|
||||
|
||||
# yarn / JS updates for new frontend
|
||||
- package-ecosystem: "npm"
|
||||
directory: "/frontend"
|
||||
target-branch: develop
|
||||
versioning-strategy: increase
|
||||
labels: ["bug"]
|
||||
schedule:
|
||||
interval: "weekly"
|
||||
|
15
.github/its_you_not_me.md
vendored
Normal file
15
.github/its_you_not_me.md
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
# It's not you, it's me
|
||||
|
||||
Sometimes bugs reported to Firefly III are configuration and system problems on the user side.
|
||||
|
||||
If you run into one of the following problems, there's a good chance it's not a Firefly III issue, but a configuration issue.
|
||||
|
||||
- ⚠️ Can't connect to the database when starting Firefly III
|
||||
- ⚠️ Errors with missing app keys or encryption problems
|
||||
- ⚠️ Can't login due to 419 errors
|
||||
- ⚠️ Any 500 error when starting Firefly III
|
||||
- ⚠️ Any white page when starting Firefly III
|
||||
- ⚠️ Time-out when starting Firefly III for the first time
|
||||
- ⚠️ Firefly III does not work behind your reverse proxy
|
||||
|
||||
If you run into an issue like this, please start a [discussion](https://github.com/firefly-iii/firefly-iii/discussions) or chat on [Gitter.im](https://gitter.im/firefly-iii/firefly-iii). There's a good chance it's not a bug but something we can fix rather quickly :+1:
|
14
.github/mergify.yml
vendored
Normal file
14
.github/mergify.yml
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
pull_request_rules:
|
||||
- name: Security update by dependabot
|
||||
conditions:
|
||||
- author~=^dependabot(|-preview)\[bot\]$
|
||||
actions:
|
||||
merge:
|
||||
method: merge
|
||||
- name: Close all on main
|
||||
conditions:
|
||||
- base=main
|
||||
- -author~=^dependabot(|-preview)\[bot\]$
|
||||
actions:
|
||||
close:
|
||||
message: Please do not open PR's on the `main` branch, but on the `develop` branch only. Thank you!
|
21
.github/workflows/build.yml
vendored
Normal file
21
.github/workflows/build.yml
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
name: Sonarcloud CI
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
- develop
|
||||
pull_request:
|
||||
types: [opened, synchronize, reopened]
|
||||
jobs:
|
||||
sonarcloud:
|
||||
name: SonarCloud
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
with:
|
||||
fetch-depth: 0 # Shallow clones should be disabled for a better relevancy of analysis
|
||||
- name: SonarCloud Scan
|
||||
uses: SonarSource/sonarcloud-github-action@master
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # Needed to get PR information, if any
|
||||
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
|
3
.gitignore
vendored
3
.gitignore
vendored
@@ -15,4 +15,5 @@ yarn-error.log
|
||||
public/google*.html
|
||||
report.html
|
||||
composer.phar
|
||||
app.js.map
|
||||
app.js.map
|
||||
frontend-v3
|
||||
|
13
.mergify.yml
13
.mergify.yml
@@ -1,13 +0,0 @@
|
||||
pull_request_rules:
|
||||
- name: Automatic merge on approval
|
||||
conditions:
|
||||
- "#approved-reviews-by>=1"
|
||||
actions:
|
||||
merge:
|
||||
method: merge
|
||||
- name: Security update by dependabot
|
||||
conditions:
|
||||
- "author=dependabot"
|
||||
actions:
|
||||
merge:
|
||||
method: merge
|
@@ -75,6 +75,7 @@ class DestroyController extends Controller
|
||||
$this->repository->destroyGroup($transactionGroup);
|
||||
// trigger just after destruction
|
||||
event(new DestroyedTransactionGroup($transactionGroup));
|
||||
app('preferences')->mark();
|
||||
|
||||
return response()->json([], 204);
|
||||
}
|
||||
|
@@ -54,7 +54,7 @@ class AutocompleteRequest extends FormRequest
|
||||
return [
|
||||
'types' => $array,
|
||||
'query' => $this->string('query'),
|
||||
'date' => $this->date('date'),
|
||||
'date' => $this->getCarbonDate('date'),
|
||||
'limit' => $limit,
|
||||
];
|
||||
}
|
||||
|
@@ -45,8 +45,8 @@ class DateRequest extends FormRequest
|
||||
public function getAll(): array
|
||||
{
|
||||
return [
|
||||
'start' => $this->date('start'),
|
||||
'end' => $this->date('end'),
|
||||
'start' => $this->getCarbonDate('start'),
|
||||
'end' => $this->getCarbonDate('end'),
|
||||
];
|
||||
}
|
||||
|
||||
|
@@ -41,8 +41,8 @@ class ExportRequest extends FormRequest
|
||||
public function getAll(): array
|
||||
{
|
||||
$result = [
|
||||
'start' => $this->date('start') ?? Carbon::now()->subYear(),
|
||||
'end' => $this->date('end') ?? Carbon::now(),
|
||||
'start' => $this->getCarbonDate('start') ?? Carbon::now()->subYear(),
|
||||
'end' => $this->getCarbonDate('end') ?? Carbon::now(),
|
||||
'type' => $this->string('type'),
|
||||
];
|
||||
$parts = explode(',', $this->string('accounts'));
|
||||
|
@@ -58,8 +58,8 @@ class GenericRequest extends FormRequest
|
||||
public function getAll(): array
|
||||
{
|
||||
return [
|
||||
'start' => $this->date('start'),
|
||||
'end' => $this->date('end'),
|
||||
'start' => $this->getCarbonDate('start'),
|
||||
'end' => $this->getCarbonDate('end'),
|
||||
];
|
||||
}
|
||||
|
||||
@@ -204,7 +204,7 @@ class GenericRequest extends FormRequest
|
||||
*/
|
||||
public function getEnd(): Carbon
|
||||
{
|
||||
$date = $this->date('end');
|
||||
$date = $this->getCarbonDate('end');
|
||||
$date->endOfDay();
|
||||
|
||||
return $date;
|
||||
@@ -251,7 +251,7 @@ class GenericRequest extends FormRequest
|
||||
*/
|
||||
public function getStart(): Carbon
|
||||
{
|
||||
$date = $this->date('start');
|
||||
$date = $this->getCarbonDate('start');
|
||||
$date->startOfDay();
|
||||
|
||||
return $date;
|
||||
|
@@ -70,7 +70,7 @@ class StoreRequest extends FormRequest
|
||||
'account_number' => $this->string('account_number'),
|
||||
'account_role' => $this->string('account_role'),
|
||||
'opening_balance' => $this->string('opening_balance'),
|
||||
'opening_balance_date' => $this->date('opening_balance_date'),
|
||||
'opening_balance_date' => $this->getCarbonDate('opening_balance_date'),
|
||||
'cc_type' => $this->string('credit_card_type'),
|
||||
'cc_monthly_payment_date' => $this->string('monthly_payment_date'),
|
||||
'notes' => $this->stringWithNewlines('notes'),
|
||||
@@ -82,7 +82,7 @@ class StoreRequest extends FormRequest
|
||||
|
||||
if ('liability' === $data['account_type_name'] || 'liabilities' === $data['account_type_name']) {
|
||||
$data['opening_balance'] = app('steam')->negative($this->string('liability_amount'));
|
||||
$data['opening_balance_date'] = $this->date('liability_start_date');
|
||||
$data['opening_balance_date'] = $this->getCarbonDate('liability_start_date');
|
||||
$data['account_type_name'] = $this->string('liability_type');
|
||||
$data['liability_direction'] = $this->string('liability_direction');
|
||||
$data['account_type_id'] = null;
|
||||
|
@@ -44,8 +44,8 @@ class StoreRequest extends FormRequest
|
||||
public function getAll(): array
|
||||
{
|
||||
return [
|
||||
'start' => $this->date('start'),
|
||||
'end' => $this->date('end'),
|
||||
'start' => $this->getCarbonDate('start'),
|
||||
'end' => $this->getCarbonDate('end'),
|
||||
'amount' => $this->string('amount'),
|
||||
'currency_id' => $this->integer('currency_id'),
|
||||
'currency_code' => $this->string('currency_code'),
|
||||
|
@@ -51,8 +51,8 @@ class StoreRequest extends FormRequest
|
||||
$data['account_id'] = $this->integer('account_id');
|
||||
$data['targetamount'] = $this->string('target_amount');
|
||||
$data['current_amount'] = $this->string('current_amount');
|
||||
$data['startdate'] = $this->date('start_date');
|
||||
$data['targetdate'] = $this->date('target_date');
|
||||
$data['startdate'] = $this->getCarbonDate('start_date');
|
||||
$data['targetdate'] = $this->getCarbonDate('target_date');
|
||||
$data['notes'] = $this->stringWithNewlines('notes');
|
||||
$data['object_group_id'] = $this->integer('object_group_id');
|
||||
$data['object_group_title'] = $this->string('object_group_title');
|
||||
|
@@ -53,6 +53,7 @@ class UpdateRequest extends FormRequest
|
||||
'strict' => ['strict', 'boolean'],
|
||||
'stop_processing' => ['stop_processing', 'boolean'],
|
||||
'active' => ['active', 'boolean'],
|
||||
'order' => ['order', 'integer'],
|
||||
];
|
||||
|
||||
$return = $this->getAllData($fields);
|
||||
@@ -150,6 +151,7 @@ class UpdateRequest extends FormRequest
|
||||
'strict' => [new IsBoolean],
|
||||
'stop_processing' => [new IsBoolean],
|
||||
'active' => [new IsBoolean],
|
||||
'order' => 'numeric|between:1,1337',
|
||||
];
|
||||
}
|
||||
|
||||
|
@@ -47,7 +47,7 @@ class StoreRequest extends FormRequest
|
||||
{
|
||||
$data = [
|
||||
'tag' => $this->string('tag'),
|
||||
'date' => $this->date('date'),
|
||||
'date' => $this->getCarbonDate('date'),
|
||||
'description' => $this->string('description'),
|
||||
'has_location' => true,
|
||||
];
|
||||
|
@@ -137,7 +137,7 @@ class StoreRequest extends FormRequest
|
||||
'original_source' => sprintf('ff3-v%s|api-v%s', config('firefly.version'), config('firefly.api_version')),
|
||||
'recurrence_id' => $this->integerFromValue($object['recurrence_id']),
|
||||
'bunq_payment_id' => $this->clearString((string)$object['bunq_payment_id'], false),
|
||||
'external_uri' => $this->clearString((string)$object['external_uri'], false),
|
||||
'external_url' => $this->clearString((string)$object['external_url'], false),
|
||||
|
||||
'sepa_cc' => $this->clearString((string)$object['sepa_cc'], false),
|
||||
'sepa_ct_op' => $this->clearString((string)$object['sepa_ct_op'], false),
|
||||
@@ -228,7 +228,7 @@ class StoreRequest extends FormRequest
|
||||
'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_uri' => 'min:1,max:255|nullable|url',
|
||||
'transactions.*.external_url' => 'min:1,max:255|nullable|url',
|
||||
|
||||
// SEPA fields:
|
||||
'transactions.*.sepa_cc' => 'min:1,max:255|nullable',
|
||||
|
@@ -113,7 +113,7 @@ class UpdateRequest extends FormRequest
|
||||
'sepa_ep',
|
||||
'sepa_ci',
|
||||
'sepa_batch_id',
|
||||
'external_uri',
|
||||
'external_url',
|
||||
];
|
||||
$this->booleanFields = [
|
||||
'reconciled',
|
||||
@@ -284,72 +284,76 @@ class UpdateRequest extends FormRequest
|
||||
{
|
||||
return [
|
||||
// basic fields for group:
|
||||
'group_title' => 'between:1,1000',
|
||||
'apply_rules' => [new IsBoolean],
|
||||
'group_title' => 'between:1,1000',
|
||||
'apply_rules' => [new IsBoolean],
|
||||
|
||||
// transaction rules (in array for splits):
|
||||
'transactions.*.type' => 'in:withdrawal,deposit,transfer,opening-balance,reconciliation',
|
||||
'transactions.*.date' => [new IsDateOrTime],
|
||||
'transactions.*.order' => 'numeric|min:0',
|
||||
'transactions.*.type' => 'in:withdrawal,deposit,transfer,opening-balance,reconciliation',
|
||||
'transactions.*.date' => [new IsDateOrTime],
|
||||
'transactions.*.order' => 'numeric|min:0',
|
||||
|
||||
// group id:
|
||||
'transactions.*.transaction_journal_id' => ['numeric', 'exists:transaction_journals,id', new BelongsUser],
|
||||
|
||||
|
||||
// currency info
|
||||
'transactions.*.currency_id' => 'numeric|exists:transaction_currencies,id',
|
||||
'transactions.*.currency_code' => 'min:3|max:3|exists:transaction_currencies,code',
|
||||
'transactions.*.foreign_currency_id' => 'nullable|numeric|exists:transaction_currencies,id',
|
||||
'transactions.*.foreign_currency_code' => 'nullable|min:3|max:3|exists:transaction_currencies,code',
|
||||
'transactions.*.currency_id' => 'numeric|exists:transaction_currencies,id',
|
||||
'transactions.*.currency_code' => 'min:3|max:3|exists:transaction_currencies,code',
|
||||
'transactions.*.foreign_currency_id' => 'nullable|numeric|exists:transaction_currencies,id',
|
||||
'transactions.*.foreign_currency_code' => 'nullable|min:3|max:3|exists:transaction_currencies,code',
|
||||
|
||||
// amount
|
||||
'transactions.*.amount' => 'numeric|gt:0|max:100000000000',
|
||||
'transactions.*.foreign_amount' => 'nullable|numeric|gte:0',
|
||||
'transactions.*.amount' => 'numeric|gt:0|max:100000000000',
|
||||
'transactions.*.foreign_amount' => 'nullable|numeric|gte:0',
|
||||
|
||||
// description
|
||||
'transactions.*.description' => 'nullable|between:1,1000',
|
||||
'transactions.*.description' => 'nullable|between:1,1000',
|
||||
|
||||
// source of transaction
|
||||
'transactions.*.source_id' => ['numeric', 'nullable', new BelongsUser],
|
||||
'transactions.*.source_name' => 'between:1,255|nullable',
|
||||
'transactions.*.source_id' => ['numeric', 'nullable', new BelongsUser],
|
||||
'transactions.*.source_name' => 'between:1,255|nullable',
|
||||
|
||||
// destination of transaction
|
||||
'transactions.*.destination_id' => ['numeric', 'nullable', new BelongsUser],
|
||||
'transactions.*.destination_name' => 'between:1,255|nullable',
|
||||
'transactions.*.destination_id' => ['numeric', 'nullable', new BelongsUser],
|
||||
'transactions.*.destination_name' => 'between:1,255|nullable',
|
||||
|
||||
// budget, category, bill and piggy
|
||||
'transactions.*.budget_id' => ['mustExist:budgets,id', new BelongsUser],
|
||||
'transactions.*.budget_name' => ['between:1,255', 'nullable', new BelongsUser],
|
||||
'transactions.*.category_id' => ['mustExist:categories,id', new BelongsUser],
|
||||
'transactions.*.category_name' => 'between:1,255|nullable',
|
||||
'transactions.*.bill_id' => ['numeric', 'nullable', 'mustExist:bills,id', new BelongsUser],
|
||||
'transactions.*.bill_name' => ['between:1,255', 'nullable', new BelongsUser],
|
||||
'transactions.*.budget_id' => ['mustExist:budgets,id', new BelongsUser],
|
||||
'transactions.*.budget_name' => ['between:1,255', 'nullable', new BelongsUser],
|
||||
'transactions.*.category_id' => ['mustExist:categories,id', new BelongsUser],
|
||||
'transactions.*.category_name' => 'between:1,255|nullable',
|
||||
'transactions.*.bill_id' => ['numeric', 'nullable', 'mustExist:bills,id', new BelongsUser],
|
||||
'transactions.*.bill_name' => ['between:1,255', 'nullable', new BelongsUser],
|
||||
|
||||
// other interesting fields
|
||||
'transactions.*.reconciled' => [new IsBoolean],
|
||||
'transactions.*.notes' => 'min:1,max:50000|nullable',
|
||||
'transactions.*.tags' => 'between:0,255',
|
||||
'transactions.*.reconciled' => [new IsBoolean],
|
||||
'transactions.*.notes' => 'min:1,max:50000|nullable',
|
||||
'transactions.*.tags' => 'between:0,255',
|
||||
|
||||
// meta info fields
|
||||
'transactions.*.internal_reference' => 'min:1,max:255|nullable',
|
||||
'transactions.*.external_id' => 'min:1,max:255|nullable',
|
||||
'transactions.*.recurrence_id' => 'min:1,max:255|nullable',
|
||||
'transactions.*.bunq_payment_id' => 'min:1,max:255|nullable',
|
||||
'transactions.*.external_uri' => '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',
|
||||
];
|
||||
}
|
||||
|
||||
@@ -375,6 +379,9 @@ class UpdateRequest extends FormRequest
|
||||
// validate source/destination is equal, depending on the transaction journal type.
|
||||
$this->validateEqualAccountsForUpdate($validator, $transactionGroup);
|
||||
|
||||
// a catch when users submit splits with no source or destination info at all.
|
||||
$this->preventNoAccountInfo($validator,);
|
||||
|
||||
// validate that the currency fits the source and/or destination account.
|
||||
// validate all account info
|
||||
$this->validateAccountInformationUpdate($validator, $transactionGroup);
|
||||
|
@@ -62,7 +62,7 @@ class CronRequest extends FormRequest
|
||||
$data['force'] = $this->boolean('force');
|
||||
}
|
||||
if ($this->has('date')) {
|
||||
$data['date'] = $this->date('date');
|
||||
$data['date'] = $this->getCarbonDate('date');
|
||||
}
|
||||
|
||||
return $data;
|
||||
|
@@ -69,6 +69,7 @@ class CorrectDatabase extends Command
|
||||
'firefly-iii:delete-empty-journals',
|
||||
'firefly-iii:delete-empty-groups',
|
||||
'firefly-iii:fix-account-types',
|
||||
'firefly-iii:fix-ibans',
|
||||
'firefly-iii:fix-account-order',
|
||||
'firefly-iii:rename-meta-fields',
|
||||
'firefly-iii:fix-ob-currencies',
|
||||
|
@@ -72,9 +72,7 @@ class CorrectOpeningBalanceCurrencies extends Command
|
||||
$count = 0;
|
||||
/** @var TransactionJournal $journal */
|
||||
foreach ($set as $journal) {
|
||||
Log::debug(sprintf('Going to fix journal #%d', $journal->id));
|
||||
$count += $this->correctJournal($journal);
|
||||
Log::debug(sprintf('Done, count is now %d', $count));
|
||||
}
|
||||
|
||||
if ($count > 0) {
|
||||
@@ -100,7 +98,6 @@ class CorrectOpeningBalanceCurrencies extends Command
|
||||
*/
|
||||
private function correctJournal(TransactionJournal $journal): int
|
||||
{
|
||||
Log::debug(sprintf('Going to correct journal #%d', $journal->id));
|
||||
// get the asset account for this opening balance:
|
||||
$account = $this->getAccount($journal);
|
||||
if (null === $account) {
|
||||
@@ -110,9 +107,7 @@ class CorrectOpeningBalanceCurrencies extends Command
|
||||
|
||||
return 0;
|
||||
}
|
||||
Log::debug(sprintf('Found "%s" #%d "%s".', $account->accountType->type, $account->id, $account->name));
|
||||
$currency = $this->getCurrency($account);
|
||||
Log::debug(sprintf('Found currency #%d (%s)', $currency->id, $currency->code));
|
||||
|
||||
// update journal and all transactions:
|
||||
return $this->setCurrency($journal, $currency);
|
||||
@@ -126,20 +121,14 @@ class CorrectOpeningBalanceCurrencies extends Command
|
||||
private function getAccount(TransactionJournal $journal): ?Account
|
||||
{
|
||||
$transactions = $journal->transactions()->get();
|
||||
Log::debug(sprintf('Found %d transactions for journal #%d.', $transactions->count(), $journal->id));
|
||||
/** @var Transaction $transaction */
|
||||
foreach ($transactions as $transaction) {
|
||||
Log::debug(sprintf('Testing transaction #%d', $transaction->id));
|
||||
/** @var Account $account */
|
||||
$account = $transaction->account()->first();
|
||||
if (null !== $account && AccountType::INITIAL_BALANCE !== $account->accountType()->first()->type) {
|
||||
Log::debug(sprintf('Account of transaction #%d is opposite of IB account (%s).', $transaction->id, $account->accountType()->first()->type));
|
||||
|
||||
return $account;
|
||||
}
|
||||
}
|
||||
Log::debug('Found no IB account in transactions of journal.');
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -165,10 +154,8 @@ class CorrectOpeningBalanceCurrencies extends Command
|
||||
*/
|
||||
private function setCurrency(TransactionJournal $journal, TransactionCurrency $currency): int
|
||||
{
|
||||
Log::debug('Now in setCurrency');
|
||||
$count = 0;
|
||||
if ((int)$journal->transaction_currency_id !== (int)$currency->id) {
|
||||
Log::debug(sprintf('Currency ID of journal #%d was #%d, now set to #%d', $journal->id, $journal->transaction_currency_id, $currency->id));
|
||||
$journal->transaction_currency_id = $currency->id;
|
||||
$journal->save();
|
||||
$count = 1;
|
||||
@@ -177,15 +164,11 @@ class CorrectOpeningBalanceCurrencies extends Command
|
||||
/** @var Transaction $transaction */
|
||||
foreach ($journal->transactions as $transaction) {
|
||||
if ((int)$transaction->transaction_currency_id !== (int)$currency->id) {
|
||||
Log::debug(
|
||||
sprintf('Currency ID of transaction #%d was #%d, now set to #%d', $transaction->id, $transaction->transaction_currency_id, $currency->id)
|
||||
);
|
||||
$transaction->transaction_currency_id = $currency->id;
|
||||
$transaction->save();
|
||||
$count = 1;
|
||||
}
|
||||
}
|
||||
Log::debug(sprintf('Return %d', $count));
|
||||
|
||||
return $count;
|
||||
}
|
||||
|
@@ -6,6 +6,7 @@ use Illuminate\Console\Command;
|
||||
|
||||
/**
|
||||
* Class CorrectionSkeleton
|
||||
* TODO DONT FORGET TO ADD THIS TO THE DOCKER BUILD
|
||||
*/
|
||||
class CorrectionSkeleton extends Command
|
||||
{
|
||||
|
51
app/Console/Commands/Correction/FixIbans.php
Normal file
51
app/Console/Commands/Correction/FixIbans.php
Normal file
@@ -0,0 +1,51 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\Console\Commands\Correction;
|
||||
|
||||
use FireflyIII\Models\Account;
|
||||
use Illuminate\Console\Command;
|
||||
|
||||
/**
|
||||
* Class FixIbans
|
||||
*/
|
||||
class FixIbans extends Command
|
||||
{
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Removes spaces from IBANs';
|
||||
/**
|
||||
* The name and signature of the console command.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'firefly-iii:fix-ibans';
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function handle(): int
|
||||
{
|
||||
$accounts = Account::whereNotNull('iban')->get();
|
||||
/** @var Account $account */
|
||||
foreach ($accounts as $account) {
|
||||
$iban = $account->iban;
|
||||
if (str_contains($iban, ' ')) {
|
||||
|
||||
$iban = app('steam')->filterSpaces((string)$account->iban);
|
||||
if ('' !== $iban) {
|
||||
$account->iban = $iban;
|
||||
$account->save();
|
||||
$this->line(sprintf('Removed spaces from IBAN of account #%d', $account->id));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
@@ -63,6 +63,12 @@ class CreateDatabase extends Command
|
||||
$exists = false;
|
||||
$checked = false; // checked for existence of DB?
|
||||
$dsn = sprintf('mysql:host=%s;port=%d;charset=utf8mb4', env('DB_HOST', 'localhost'), env('DB_PORT', '3306'));
|
||||
|
||||
if ('' !== env('DB_SOCKET', '')) {
|
||||
$dsn = sprintf('mysql:unix_socket=%s;charset=utf8mb4', env('DB_SOCKET', ''));
|
||||
}
|
||||
$this->info(sprintf('DSN is %s', $dsn));
|
||||
|
||||
$options = [
|
||||
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
|
||||
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
|
||||
|
@@ -6,6 +6,7 @@ use Illuminate\Console\Command;
|
||||
|
||||
/**
|
||||
* Class ReportSkeleton
|
||||
* TODO DONT FORGET TO ADD THIS TO THE DOCKER BUILD
|
||||
*/
|
||||
class ReportSkeleton extends Command
|
||||
{
|
||||
|
@@ -74,8 +74,16 @@ class RestoreOAuthKeys extends Command
|
||||
}
|
||||
if ($this->keysInDatabase() && !$this->keysOnDrive()) {
|
||||
Log::debug('Keys are in DB and keys are not on the drive. Restore.');
|
||||
$this->restoreKeysFromDB();
|
||||
$this->line('Restored OAuth keys from database.');
|
||||
$result = $this->restoreKeysFromDB();
|
||||
if(true === $result) {
|
||||
$this->line('Restored OAuth keys from database.');
|
||||
|
||||
return;
|
||||
}
|
||||
Log::warning('Could not restore keys. Will create new ones.');
|
||||
$this->generateKeys();
|
||||
$this->storeKeysInDB();
|
||||
$this->line('Generated and stored new keys.');
|
||||
|
||||
return;
|
||||
}
|
||||
@@ -124,8 +132,8 @@ class RestoreOAuthKeys extends Command
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private function restoreKeysFromDB(): void
|
||||
private function restoreKeysFromDB(): bool
|
||||
{
|
||||
OAuthKeys::restoreKeysFromDB();
|
||||
return OAuthKeys::restoreKeysFromDB();
|
||||
}
|
||||
}
|
||||
|
@@ -98,6 +98,7 @@ class UpgradeDatabase extends Command
|
||||
'firefly-iii:unify-group-accounts',
|
||||
'firefly-iii:fix-transaction-types',
|
||||
'firefly-iii:fix-frontpage-accounts',
|
||||
'firefly-iii:fix-ibans',
|
||||
|
||||
// two report commands
|
||||
'firefly-iii:report-empty-objects',
|
||||
@@ -143,11 +144,6 @@ class UpgradeDatabase extends Command
|
||||
$result = Artisan::output();
|
||||
echo $result;
|
||||
|
||||
$this->line('Now installing OAuth2 keys...');
|
||||
Artisan::call('passport:install');
|
||||
$result = Artisan::output();
|
||||
echo $result;
|
||||
|
||||
$this->line('Done!');
|
||||
}
|
||||
}
|
||||
|
@@ -5,7 +5,8 @@ namespace FireflyIII\Console\Commands\Upgrade;
|
||||
use Illuminate\Console\Command;
|
||||
|
||||
/**
|
||||
* Class UpgradeSkeleton
|
||||
* Class UpgradeSkeleton.
|
||||
* TODO DONT FORGET TO ADD THIS TO THE DOCKER BUILD
|
||||
*/
|
||||
class UpgradeSkeleton extends Command
|
||||
{
|
||||
|
@@ -1,8 +1,8 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* ActuallyLoggedIn.php
|
||||
* Copyright (c) 2021 james@firefly-iii.org
|
||||
* Copyright (c) 2022 james@firefly-iii.org
|
||||
*
|
||||
* This file is part of Firefly III (https://github.com/firefly-iii).
|
||||
*
|
||||
@@ -20,6 +20,8 @@ declare(strict_types=1);
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\Events;
|
||||
|
||||
use FireflyIII\User;
|
||||
|
@@ -91,6 +91,7 @@ class GracefulNotFoundHandler extends ExceptionHandler
|
||||
return redirect(route('currencies.index'));
|
||||
case 'budgets.show':
|
||||
case 'budgets.edit':
|
||||
case 'budgets.show.limit':
|
||||
$request->session()->reflash();
|
||||
|
||||
return redirect(route('budgets.index'));
|
||||
|
@@ -365,19 +365,29 @@ class TransactionJournalFactory
|
||||
$this->accountValidator->setTransactionType($transactionType);
|
||||
|
||||
// validate source account.
|
||||
$sourceId = $data['source_id'] ? (int)$data['source_id'] : null;
|
||||
$sourceName = $data['source_name'] ? (string)$data['source_name'] : null;
|
||||
$validSource = $this->accountValidator->validateSource($sourceId, $sourceName, null);
|
||||
$array = [
|
||||
'id' => $data['source_id'] ? (int)$data['source_id'] : null,
|
||||
'name' => $data['source_name'] ? (string)$data['source_name'] : null,
|
||||
'iban' => $data['source_iban'] ? (string)$data['source_iban'] : null,
|
||||
'number' => $data['source_number'] ? (string)$data['source_number'] : null,
|
||||
];
|
||||
$validSource = $this->accountValidator->validateSource($array);
|
||||
|
||||
// do something with result:
|
||||
if (false === $validSource) {
|
||||
throw new FireflyException(sprintf('Source: %s', $this->accountValidator->sourceError));
|
||||
}
|
||||
Log::debug('Source seems valid.');
|
||||
|
||||
// validate destination account
|
||||
$destinationId = $data['destination_id'] ? (int)$data['destination_id'] : null;
|
||||
$destinationName = $data['destination_name'] ? (string)$data['destination_name'] : null;
|
||||
$validDestination = $this->accountValidator->validateDestination($destinationId, $destinationName, null);
|
||||
$array = [
|
||||
'id' => $data['destination_id'] ? (int)$data['destination_id'] : null,
|
||||
'name' => $data['destination_name'] ? (string)$data['destination_name'] : null,
|
||||
'iban' => $data['destination_iban'] ? (string)$data['destination_iban'] : null,
|
||||
'number' => $data['destination_number'] ? (string)$data['destination_number'] : null,
|
||||
];
|
||||
|
||||
$validDestination = $this->accountValidator->validateDestination($array);
|
||||
// do something with result:
|
||||
if (false === $validDestination) {
|
||||
throw new FireflyException(sprintf('Destination: %s', $this->accountValidator->destError));
|
||||
|
@@ -123,7 +123,6 @@ class UpdatedGroupEventHandler
|
||||
if (1 === $group->transactionJournals->count()) {
|
||||
return;
|
||||
}
|
||||
Log::debug(sprintf('Correct inconsistent accounts in group #%d', $group->id));
|
||||
// first journal:
|
||||
/** @var TransactionJournal $first */
|
||||
$first = $group->transactionJournals()
|
||||
@@ -132,6 +131,12 @@ class UpdatedGroupEventHandler
|
||||
->orderBy('transaction_journals.id', 'DESC')
|
||||
->orderBy('transaction_journals.description', 'DESC')
|
||||
->first();
|
||||
|
||||
if(null === $first) {
|
||||
Log::warning(sprintf('Group #%d has no transaction journals.', $group->id));
|
||||
return;
|
||||
}
|
||||
|
||||
$all = $group->transactionJournals()->get()->pluck('id')->toArray();
|
||||
/** @var Account $sourceAccount */
|
||||
$sourceAccount = $first->transactions()->where('amount', '<', '0')->first()->account;
|
||||
|
@@ -176,7 +176,9 @@ class AttachmentHelper implements AttachmentHelperInterface
|
||||
return false;
|
||||
}
|
||||
// is allowed? Save the file, without encryption.
|
||||
$this->uploadDisk->put($attachment->fileName(), $content);
|
||||
$parts = explode('/', $attachment->fileName());
|
||||
$file = $parts[count($parts) - 1];
|
||||
$this->uploadDisk->put($file, $content);
|
||||
|
||||
// update attachment.
|
||||
$attachment->md5 = md5_file($path);
|
||||
|
@@ -212,6 +212,40 @@ trait MetaCollection
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function withoutExternalUrl(): GroupCollectorInterface
|
||||
{
|
||||
if (false === $this->hasJoinedMetaTables) {
|
||||
$this->hasJoinedMetaTables = true;
|
||||
$this->query->leftJoin('journal_meta', 'transaction_journals.id', '=', 'journal_meta.transaction_journal_id');
|
||||
}
|
||||
$this->query->where(function(Builder $q1) {
|
||||
$q1->where(function(Builder $q2) {
|
||||
$q2->where('journal_meta.name', '=', 'external_url');
|
||||
$q2->whereNull('journal_meta.data');
|
||||
})->orWhereNull('journal_meta.name');
|
||||
});
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function withExternalUrl(): GroupCollectorInterface
|
||||
{
|
||||
if (false === $this->hasJoinedMetaTables) {
|
||||
$this->hasJoinedMetaTables = true;
|
||||
$this->query->leftJoin('journal_meta', 'transaction_journals.id', '=', 'journal_meta.transaction_journal_id');
|
||||
}
|
||||
$this->query->where('journal_meta.name', '=', 'external_url');
|
||||
$this->query->whereNotNull('journal_meta.data');
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
|
@@ -119,4 +119,61 @@ trait TimeCollection
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function yearIs(string $year): GroupCollectorInterface
|
||||
{
|
||||
$this->query->whereYear('transaction_journals.date', '=', $year);
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function monthIs(string $month): GroupCollectorInterface
|
||||
{
|
||||
$this->query->whereMonth('transaction_journals.date', '=', $month);
|
||||
return $this;
|
||||
|
||||
}
|
||||
|
||||
public function dayIs(string $day): GroupCollectorInterface
|
||||
{
|
||||
$this->query->whereDay('transaction_journals.date', '=', $day);
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function yearBefore(string $year): GroupCollectorInterface
|
||||
{
|
||||
$this->query->whereYear('transaction_journals.date', '<=', $year);
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function monthBefore(string $month): GroupCollectorInterface
|
||||
{
|
||||
$this->query->whereMonth('transaction_journals.date', '<=', $month);
|
||||
return $this;
|
||||
|
||||
}
|
||||
|
||||
public function dayBefore(string $day): GroupCollectorInterface
|
||||
{
|
||||
$this->query->whereDay('transaction_journals.date', '<=', $day);
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function yearAfter(string $year): GroupCollectorInterface
|
||||
{
|
||||
$this->query->whereYear('transaction_journals.date', '>=', $year);
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function monthAfter(string $month): GroupCollectorInterface
|
||||
{
|
||||
$this->query->whereMonth('transaction_journals.date', '>=', $month);
|
||||
return $this;
|
||||
|
||||
}
|
||||
|
||||
public function dayAfter(string $day): GroupCollectorInterface
|
||||
{
|
||||
$this->query->whereDay('transaction_journals.date', '>=', $day);
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
|
@@ -559,6 +559,15 @@ class GroupCollector implements GroupCollectorInterface
|
||||
echo '</pre>';
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public function dumpQueryInLogs(): void
|
||||
{
|
||||
Log::debug($this->query->select($this->fields)->toSql()) ;
|
||||
Log::debug('Bindings',$this->query->getBindings());
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a selected set of fields to arrays.
|
||||
*
|
||||
|
@@ -305,6 +305,20 @@ interface GroupCollectorInterface
|
||||
*/
|
||||
public function setExternalId(string $externalId): GroupCollectorInterface;
|
||||
|
||||
/**
|
||||
* Transactions without an external URL
|
||||
*
|
||||
* @return GroupCollectorInterface
|
||||
*/
|
||||
public function withoutExternalUrl(): GroupCollectorInterface;
|
||||
|
||||
/**
|
||||
* Transactions with an external URL
|
||||
*
|
||||
* @return GroupCollectorInterface
|
||||
*/
|
||||
public function withExternalUrl(): GroupCollectorInterface;
|
||||
|
||||
/**
|
||||
* Limit results to a specific foreign currency.
|
||||
*
|
||||
@@ -565,4 +579,15 @@ interface GroupCollectorInterface
|
||||
*/
|
||||
public function withoutTags(): GroupCollectorInterface;
|
||||
|
||||
|
||||
public function yearIs(string $year): GroupCollectorInterface;
|
||||
public function monthIs(string $month): GroupCollectorInterface;
|
||||
public function dayIs(string $day): GroupCollectorInterface;
|
||||
public function yearBefore(string $year): GroupCollectorInterface;
|
||||
public function monthBefore(string $month): GroupCollectorInterface;
|
||||
public function dayBefore(string $day): GroupCollectorInterface;
|
||||
public function yearAfter(string $year): GroupCollectorInterface;
|
||||
public function monthAfter(string $month): GroupCollectorInterface;
|
||||
public function dayAfter(string $day): GroupCollectorInterface;
|
||||
|
||||
}
|
||||
|
@@ -38,9 +38,16 @@ class Sha3SignatureGenerator implements SignatureGeneratorInterface
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
* @throws FireflyException
|
||||
*/
|
||||
public function generate(WebhookMessage $message): string
|
||||
{
|
||||
// webhook is deleted
|
||||
if (null === $message->webhook) {
|
||||
throw new FireflyException('Part of a deleted webhook.');
|
||||
}
|
||||
|
||||
|
||||
try {
|
||||
$json = json_encode($message->message, JSON_THROW_ON_ERROR);
|
||||
} catch (JsonException $e) {
|
||||
@@ -48,7 +55,7 @@ class Sha3SignatureGenerator implements SignatureGeneratorInterface
|
||||
Log::error(sprintf('JSON value: %s', $message->message));
|
||||
Log::error($e->getMessage());
|
||||
Log::error($e->getTraceAsString());
|
||||
throw new FireflyException('Could not generate JSON for SHA3 hash.', $e);
|
||||
throw new FireflyException('Could not generate JSON for SHA3 hash.', 0, $e);
|
||||
}
|
||||
|
||||
// signature v1 is generated using the following structure:
|
||||
|
@@ -122,7 +122,7 @@ class EditController extends Controller
|
||||
}
|
||||
$request->session()->forget('accounts.edit.fromUpdate');
|
||||
|
||||
$openingBalanceAmount = app('steam')->positive((string)$repository->getOpeningBalanceAmount($account));
|
||||
$openingBalanceAmount = (string)$repository->getOpeningBalanceAmount($account);
|
||||
if ('0' === $openingBalanceAmount) {
|
||||
$openingBalanceAmount = '';
|
||||
}
|
||||
@@ -143,9 +143,9 @@ class EditController extends Controller
|
||||
'BIC' => $repository->getMetaValue($account, 'BIC'),
|
||||
'opening_balance_date' => $openingBalanceDate,
|
||||
'liability_type_id' => $account->account_type_id,
|
||||
'opening_balance' => $openingBalanceAmount,
|
||||
'opening_balance' => number_format((float)$openingBalanceAmount, $currency->decimal_places,'.',''),
|
||||
'liability_direction' => $this->repository->getMetaValue($account, 'liability_direction'),
|
||||
'virtual_balance' => $account->virtual_balance,
|
||||
'virtual_balance' => number_format((float)$account->virtual_balance, $currency->decimal_places,'.',''),
|
||||
'currency_id' => $currency->id,
|
||||
'include_net_worth' => $includeNetWorth,
|
||||
'interest' => $repository->getMetaValue($account, 'interest'),
|
||||
@@ -153,6 +153,9 @@ class EditController extends Controller
|
||||
'notes' => $this->repository->getNoteText($account),
|
||||
'active' => $hasOldInput ? (bool)$request->old('active') : $account->active,
|
||||
];
|
||||
if('' === $openingBalanceAmount) {
|
||||
$preFilled['opening_balance'] = '';
|
||||
}
|
||||
|
||||
$request->session()->flash('preFilled', $preFilled);
|
||||
|
||||
|
@@ -109,6 +109,7 @@ class IndexController extends Controller
|
||||
$account->interestPeriod = (string)trans(sprintf('firefly.interest_calc_%s', $this->repository->getMetaValue($account, 'interest_period')));
|
||||
$account->accountTypeString = (string)trans(sprintf('firefly.account_type_%s', $account->accountType->type));
|
||||
$account->current_debt = '0';
|
||||
$account->iban = implode(' ', str_split((string)$account->iban, 4));
|
||||
}
|
||||
);
|
||||
|
||||
@@ -175,6 +176,7 @@ class IndexController extends Controller
|
||||
$account->location = $this->repository->getLocation($account);
|
||||
$account->liability_direction = $this->repository->getMetaValue($account, 'liability_direction');
|
||||
$account->current_debt = $this->repository->getMetaValue($account, 'current_debt') ?? '-';
|
||||
$account->iban = implode(' ', str_split((string)$account->iban, 4));
|
||||
}
|
||||
);
|
||||
// make paginator:
|
||||
|
@@ -181,7 +181,7 @@ class BudgetLimitController extends Controller
|
||||
return response()->json($array);
|
||||
}
|
||||
|
||||
return response()->json([]);
|
||||
return redirect(route('budgets.index'));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -102,7 +102,8 @@ class EditController extends Controller
|
||||
'auto_budget_currency_id' => $hasOldInput ? (int)$request->old('auto_budget_currency_id') : $currency->id,
|
||||
];
|
||||
if ($autoBudget) {
|
||||
$preFilled['auto_budget_amount'] = $hasOldInput ? $request->old('auto_budget_amount') : $autoBudget->amount;
|
||||
$amount = $hasOldInput ? $request->old('auto_budget_amount') : $autoBudget->amount;
|
||||
$preFilled['auto_budget_amount'] = number_format((float)$amount, $autoBudget->transactionCurrency->decimal_places, '.', '');
|
||||
}
|
||||
|
||||
// put previous url in session if not redirect from store (not "return_to_edit").
|
||||
|
@@ -29,6 +29,7 @@ use FireflyIII\Repositories\Currency\CurrencyRepositoryInterface;
|
||||
use FireflyIII\Repositories\User\UserRepositoryInterface;
|
||||
use FireflyIII\User;
|
||||
use Illuminate\Contracts\View\Factory;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Http\RedirectResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Pagination\LengthAwarePaginator;
|
||||
@@ -210,10 +211,9 @@ class CurrencyController extends Controller
|
||||
* @param Request $request
|
||||
* @param TransactionCurrency $currency
|
||||
*
|
||||
* @return RedirectResponse|Redirector
|
||||
* @throws FireflyException
|
||||
*/
|
||||
public function disableCurrency(Request $request)
|
||||
public function disableCurrency(Request $request): JsonResponse
|
||||
{
|
||||
$currencyId = (int)$request->get('id');
|
||||
if ($currencyId > 0) {
|
||||
@@ -228,8 +228,7 @@ class CurrencyController extends Controller
|
||||
|
||||
$request->session()->flash('error', (string)trans('firefly.ask_site_owner', ['owner' => e(config('firefly.site_owner'))]));
|
||||
Log::channel('audit')->info(sprintf('Tried to disable currency %s but is not site owner.', $currency->code));
|
||||
|
||||
return redirect(route('currencies.index'));
|
||||
return response()->json([]);
|
||||
|
||||
}
|
||||
|
||||
@@ -240,8 +239,7 @@ class CurrencyController extends Controller
|
||||
|
||||
$request->session()->flash('error', $message);
|
||||
Log::channel('audit')->info(sprintf('Tried to disable currency %s but is in use.', $currency->code));
|
||||
|
||||
return redirect(route('currencies.index'));
|
||||
return response()->json([]);
|
||||
}
|
||||
|
||||
$this->repository->disable($currency);
|
||||
@@ -267,7 +265,7 @@ class CurrencyController extends Controller
|
||||
}
|
||||
}
|
||||
|
||||
return redirect(route('currencies.index'));
|
||||
return response()->json([]);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -144,6 +144,15 @@ class DebugController extends Controller
|
||||
$bcscale = bcscale();
|
||||
$layout = env('FIREFLY_III_LAYOUT');
|
||||
$tz = env('TZ');
|
||||
$buildNr = '(unknown)';
|
||||
$buildDate = '(unknown)';
|
||||
|
||||
if (file_exists('/var/www/counter-main.txt')) {
|
||||
$buildNr = trim(file_get_contents('/var/www/counter-main.txt'));
|
||||
}
|
||||
if (file_exists('/var/www/build-date-main.txt')) {
|
||||
$buildDate = trim(file_get_contents('/var/www/build-date-main.txt'));
|
||||
}
|
||||
|
||||
// expected + found DB version:
|
||||
$expectedDBversion = config('firefly.db_version');
|
||||
@@ -206,6 +215,8 @@ class DebugController extends Controller
|
||||
'drivers',
|
||||
'currentDriver',
|
||||
'loginProvider',
|
||||
'buildNr',
|
||||
'buildDate',
|
||||
'bcscale',
|
||||
'layout',
|
||||
'userAgent',
|
||||
|
@@ -102,6 +102,9 @@ class HomeController extends Controller
|
||||
*/
|
||||
public function index(AccountRepositoryInterface $repository): mixed
|
||||
{
|
||||
if ('v3' === config('firefly.layout')) {
|
||||
die('Please set your layout to "v1".');
|
||||
}
|
||||
$types = config('firefly.accountTypesByIdentifier.asset');
|
||||
$count = $repository->count($types);
|
||||
Log::channel('audit')->info('User visits homepage.');
|
||||
|
@@ -83,14 +83,18 @@ class ReconcileController extends Controller
|
||||
*
|
||||
* @return JsonResponse
|
||||
*/
|
||||
public function overview(Request $request, Account $account, Carbon $start, Carbon $end): JsonResponse
|
||||
public function overview(Request $request, Account $account = null, Carbon $start = null, Carbon $end = null): JsonResponse
|
||||
{
|
||||
$startBalance = $request->get('startBalance');
|
||||
$endBalance = $request->get('endBalance');
|
||||
$accountCurrency = $this->accountRepos->getAccountCurrency($account) ?? app('amount')->getDefaultCurrency();
|
||||
$amount = '0';
|
||||
$clearedAmount = '0';
|
||||
$route = '';
|
||||
|
||||
if (null === $start && null === $end) {
|
||||
throw new FireflyException('Invalid dates submitted.');
|
||||
}
|
||||
if ($end->lt($start)) {
|
||||
[$start, $end] = [$end, $start];
|
||||
}
|
||||
@@ -220,8 +224,11 @@ class ReconcileController extends Controller
|
||||
* @throws FireflyException
|
||||
* @throws JsonException
|
||||
*/
|
||||
public function transactions(Account $account, Carbon $start, Carbon $end)
|
||||
public function transactions(Account $account, Carbon $start = null, Carbon $end = null)
|
||||
{
|
||||
if (null === $start || null === $end) {
|
||||
throw new FireflyException('Invalid dates submitted.');
|
||||
}
|
||||
if ($end->lt($start)) {
|
||||
[$end, $start] = [$start, $end];
|
||||
}
|
||||
|
@@ -28,6 +28,7 @@ use FireflyIII\Helpers\Attachments\AttachmentHelperInterface;
|
||||
use FireflyIII\Http\Controllers\Controller;
|
||||
use FireflyIII\Http\Requests\PiggyBankUpdateRequest;
|
||||
use FireflyIII\Models\PiggyBank;
|
||||
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
|
||||
use FireflyIII\Repositories\PiggyBank\PiggyBankRepositoryInterface;
|
||||
use Illuminate\Contracts\View\Factory;
|
||||
use Illuminate\Http\RedirectResponse;
|
||||
@@ -41,6 +42,7 @@ class EditController extends Controller
|
||||
{
|
||||
private AttachmentHelperInterface $attachments;
|
||||
private PiggyBankRepositoryInterface $piggyRepos;
|
||||
private AccountRepositoryInterface $accountRepository;
|
||||
|
||||
/**
|
||||
* PiggyBankController constructor.
|
||||
@@ -58,7 +60,7 @@ class EditController extends Controller
|
||||
|
||||
$this->attachments = app(AttachmentHelperInterface::class);
|
||||
$this->piggyRepos = app(PiggyBankRepositoryInterface::class);
|
||||
|
||||
$this->accountRepository = app(AccountRepositoryInterface::class);
|
||||
return $next($request);
|
||||
}
|
||||
);
|
||||
@@ -81,10 +83,11 @@ class EditController extends Controller
|
||||
// Flash some data to fill the form.
|
||||
$targetDate = $piggyBank->targetdate?->format('Y-m-d');
|
||||
$startDate = $piggyBank->startdate?->format('Y-m-d');
|
||||
$currency = $this->accountRepository->getAccountCurrency($piggyBank->account);
|
||||
|
||||
$preFilled = ['name' => $piggyBank->name,
|
||||
'account_id' => $piggyBank->account_id,
|
||||
'targetamount' => $piggyBank->targetamount,
|
||||
'targetamount' => number_format((float)$piggyBank->targetamount, $currency->decimal_places,'.',''),
|
||||
'targetdate' => $targetDate,
|
||||
'startdate' => $startDate,
|
||||
'object_group' => $piggyBank->objectGroups->first() ? $piggyBank->objectGroups->first()->title : '',
|
||||
|
@@ -214,7 +214,7 @@ class PreferencesController extends Controller
|
||||
'internal_reference' => array_key_exists('internal_reference', $setOptions),
|
||||
'notes' => array_key_exists('notes', $setOptions),
|
||||
'attachments' => array_key_exists('attachments', $setOptions),
|
||||
'external_uri' => array_key_exists('external_uri', $setOptions),
|
||||
'external_url' => array_key_exists('external_url', $setOptions),
|
||||
'location' => array_key_exists('location', $setOptions),
|
||||
'links' => array_key_exists('links', $setOptions),
|
||||
];
|
||||
|
@@ -109,6 +109,7 @@ class InstallController extends Controller
|
||||
'firefly-iii:unify-group-accounts' => [],
|
||||
'firefly-iii:fix-transaction-types' => [],
|
||||
'firefly-iii:fix-frontpage-accounts' => [],
|
||||
'firefly-iii:fix-ibans' => [],
|
||||
|
||||
// final command to set latest version in DB
|
||||
'firefly-iii:set-latest-version' => ['--james-is-cool' => true],
|
||||
|
@@ -336,8 +336,8 @@ class ConvertController extends Controller
|
||||
$sourceName = '' === $sourceName ? null : (string)$sourceName;
|
||||
$destinationId = '' === $destinationId || null === $destinationId ? null : (int)$destinationId;
|
||||
$destinationName = '' === $destinationName ? null : (string)$destinationName;
|
||||
$validSource = $validator->validateSource($sourceId, $sourceName, null);
|
||||
$validDestination = $validator->validateDestination($destinationId, $destinationName, null);
|
||||
$validSource = $validator->validateSource(['id' => $sourceId, 'name' => $sourceName,]);
|
||||
$validDestination = $validator->validateDestination(['id' => $destinationId, 'name' => $destinationName,]);
|
||||
|
||||
if (false === $validSource) {
|
||||
throw new FireflyException(sprintf(trans('firefly.convert_invalid_source'), $journal->id));
|
||||
|
@@ -26,7 +26,6 @@ namespace FireflyIII\Http\Controllers\Transaction;
|
||||
use FireflyIII\Events\StoredTransactionGroup;
|
||||
use FireflyIII\Exceptions\FireflyException;
|
||||
use FireflyIII\Http\Controllers\Controller;
|
||||
use FireflyIII\Models\TransactionGroup;
|
||||
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
|
||||
use FireflyIII\Repositories\TransactionGroup\TransactionGroupRepositoryInterface;
|
||||
use FireflyIII\Services\Internal\Update\GroupCloneService;
|
||||
@@ -109,9 +108,9 @@ class CreateController extends Controller
|
||||
$sourceId = (int)request()->get('source');
|
||||
$destinationId = (int)request()->get('destination');
|
||||
|
||||
/** @var AccountRepositoryInterface $repository */
|
||||
$repository = app(AccountRepositoryInterface::class);
|
||||
$cash = $repository->getCashAccount();
|
||||
/** @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';
|
||||
|
@@ -30,6 +30,7 @@ use FireflyIII\Repositories\Journal\JournalRepositoryInterface;
|
||||
use FireflyIII\Repositories\LinkType\LinkTypeRepositoryInterface;
|
||||
use Illuminate\Contracts\View\Factory;
|
||||
use Illuminate\Http\RedirectResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Routing\Redirector;
|
||||
use Illuminate\View\View;
|
||||
use Log;
|
||||
@@ -157,9 +158,10 @@ class LinkController extends Controller
|
||||
*
|
||||
* @return RedirectResponse|Redirector
|
||||
*/
|
||||
public function switchLink(TransactionJournalLink $link)
|
||||
public function switchLink(Request $request)
|
||||
{
|
||||
$this->repository->switchLink($link);
|
||||
$linkId = (int)$request->get('id');
|
||||
$this->repository->switchLinkById($linkId);
|
||||
|
||||
return redirect(app('steam')->getSafePreviousUrl());
|
||||
}
|
||||
|
@@ -146,7 +146,7 @@ class MassController extends Controller
|
||||
|
||||
// reverse amounts
|
||||
foreach ($journals as $index => $journal) {
|
||||
$journals[$index]['amount'] = app('steam')->positive($journal['amount']);
|
||||
$journals[$index]['amount'] = number_format((float) app('steam')->positive($journal['amount']), $journal['currency_decimal_places'],'.','');
|
||||
$journals[$index]['foreign_amount'] = null === $journal['foreign_amount'] ?
|
||||
null : app('steam')->positive($journal['foreign_amount']);
|
||||
}
|
||||
|
@@ -57,7 +57,7 @@ class SecureHeaders
|
||||
"style-src 'unsafe-inline' 'self'",
|
||||
"base-uri 'self'",
|
||||
"font-src 'self' data:",
|
||||
"connect-src 'self'",
|
||||
sprintf("connect-src 'self' %s", $trackingScriptSrc),
|
||||
sprintf("img-src data: 'strict-dynamic' 'self' *.tile.openstreetmap.org %s", $trackingScriptSrc),
|
||||
"manifest-src 'self'",
|
||||
];
|
||||
@@ -94,7 +94,6 @@ class SecureHeaders
|
||||
$response->header('X-XSS-Protection', '1; mode=block');
|
||||
$response->header('X-Content-Type-Options', 'nosniff');
|
||||
$response->header('Referrer-Policy', 'no-referrer');
|
||||
$response->header('X-Download-Options', 'noopen');
|
||||
$response->header('X-Permitted-Cross-Domain-Policies', 'none');
|
||||
$response->header('X-Robots-Tag', 'none');
|
||||
$response->header('Feature-Policy', implode('; ', $featurePolicies));
|
||||
@@ -103,7 +102,7 @@ class SecureHeaders
|
||||
}
|
||||
|
||||
/**
|
||||
* Return part of a CSP header allowing scripts from Google.
|
||||
* Return part of a CSP header allowing scripts from Matomo.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
|
@@ -46,16 +46,11 @@ class StartFireflySession extends StartSession
|
||||
$safeUrl = app('steam')->getSafeUrl($url, route('index'));
|
||||
|
||||
if ($url !== $safeUrl) {
|
||||
//Log::debug(sprintf('storeCurrentUrl: converted "%s" to "%s", so will not use it.', $url, $safeUrl));
|
||||
return;
|
||||
}
|
||||
|
||||
if ('GET' === $request->method() && !$request->ajax()) {
|
||||
//Log::debug(sprintf('storeCurrentUrl: Redirect is now "%s".', $safeUrl));
|
||||
$session->setPreviousUrl($safeUrl);
|
||||
|
||||
return;
|
||||
}
|
||||
//Log::debug(sprintf('storeCurrentUrl: Refuse to set "%s" as current URL.', $safeUrl));
|
||||
}
|
||||
}
|
||||
|
@@ -55,7 +55,7 @@ class AccountFormRequest extends FormRequest
|
||||
'account_number' => $this->string('account_number'),
|
||||
'account_role' => $this->string('account_role'),
|
||||
'opening_balance' => $this->string('opening_balance'),
|
||||
'opening_balance_date' => $this->date('opening_balance_date'),
|
||||
'opening_balance_date' => $this->getCarbonDate('opening_balance_date'),
|
||||
'cc_type' => $this->string('cc_type'),
|
||||
'cc_monthly_payment_date' => $this->string('cc_monthly_payment_date'),
|
||||
'notes' => $this->stringWithNewlines('notes'),
|
||||
|
@@ -46,7 +46,7 @@ class BillStoreRequest extends FormRequest
|
||||
'currency_id' => $this->integer('transaction_currency_id'),
|
||||
'currency_code' => '',
|
||||
'amount_max' => $this->string('amount_max'),
|
||||
'date' => $this->date('date'),
|
||||
'date' => $this->getCarbonDate('date'),
|
||||
'repeat_freq' => $this->string('repeat_freq'),
|
||||
'skip' => $this->integer('skip'),
|
||||
'notes' => $this->stringWithNewlines('notes'),
|
||||
|
@@ -47,7 +47,7 @@ class BillUpdateRequest extends FormRequest
|
||||
'currency_id' => $this->integer('transaction_currency_id'),
|
||||
'currency_code' => '',
|
||||
'amount_max' => $this->string('amount_max'),
|
||||
'date' => $this->date('date'),
|
||||
'date' => $this->getCarbonDate('date'),
|
||||
'repeat_freq' => $this->string('repeat_freq'),
|
||||
'skip' => $this->integer('skip'),
|
||||
'notes' => $this->stringWithNewlines('notes'),
|
||||
|
@@ -42,10 +42,10 @@ class PiggyBankStoreRequest extends FormRequest
|
||||
{
|
||||
return [
|
||||
'name' => $this->string('name'),
|
||||
'startdate' => $this->date('startdate'),
|
||||
'startdate' => $this->getCarbonDate('startdate'),
|
||||
'account_id' => $this->integer('account_id'),
|
||||
'targetamount' => $this->string('targetamount'),
|
||||
'targetdate' => $this->date('targetdate'),
|
||||
'targetdate' => $this->getCarbonDate('targetdate'),
|
||||
'notes' => $this->stringWithNewlines('notes'),
|
||||
'object_group_title' => $this->string('object_group'),
|
||||
];
|
||||
|
@@ -43,10 +43,10 @@ class PiggyBankUpdateRequest extends FormRequest
|
||||
{
|
||||
return [
|
||||
'name' => $this->string('name'),
|
||||
'startdate' => $this->date('startdate'),
|
||||
'startdate' => $this->getCarbonDate('startdate'),
|
||||
'account_id' => $this->integer('account_id'),
|
||||
'targetamount' => $this->string('targetamount'),
|
||||
'targetdate' => $this->date('targetdate'),
|
||||
'targetdate' => $this->getCarbonDate('targetdate'),
|
||||
'notes' => $this->stringWithNewlines('notes'),
|
||||
'object_group_title' => $this->string('object_group'),
|
||||
];
|
||||
|
@@ -48,8 +48,8 @@ class ReconciliationStoreRequest extends FormRequest
|
||||
$transactions = [];
|
||||
}
|
||||
$data = [
|
||||
'start' => $this->date('start'),
|
||||
'end' => $this->date('end'),
|
||||
'start' => $this->getCarbonDate('start'),
|
||||
'end' => $this->getCarbonDate('end'),
|
||||
'start_balance' => $this->string('startBalance'),
|
||||
'end_balance' => $this->string('endBalance'),
|
||||
'difference' => $this->string('difference'),
|
||||
|
@@ -59,8 +59,8 @@ class RecurrenceFormRequest extends FormRequest
|
||||
'type' => $this->string('transaction_type'),
|
||||
'title' => $this->string('title'),
|
||||
'description' => $this->string('recurring_description'),
|
||||
'first_date' => $this->date('first_date'),
|
||||
'repeat_until' => $this->date('repeat_until'),
|
||||
'first_date' => $this->getCarbonDate('first_date'),
|
||||
'repeat_until' => $this->getCarbonDate('repeat_until'),
|
||||
'nr_of_repetitions' => $this->integer('repetitions'),
|
||||
'apply_rules' => $this->boolean('apply_rules'),
|
||||
'active' => $this->boolean('active'),
|
||||
@@ -295,7 +295,7 @@ class RecurrenceFormRequest extends FormRequest
|
||||
*/
|
||||
public function validateAccountInformation(Validator $validator): void
|
||||
{
|
||||
Log::debug('Now in validateAccountInformation()');
|
||||
Log::debug('Now in validateAccountInformation (RecurrenceFormRequest)()');
|
||||
/** @var AccountValidator $accountValidator */
|
||||
$accountValidator = app(AccountValidator::class);
|
||||
$data = $validator->getData();
|
||||
@@ -326,7 +326,7 @@ class RecurrenceFormRequest extends FormRequest
|
||||
break;
|
||||
}
|
||||
// validate source account.
|
||||
$validSource = $accountValidator->validateSource($sourceId, null, null);
|
||||
$validSource = $accountValidator->validateSource(['id' => $sourceId,]);
|
||||
|
||||
// do something with result:
|
||||
if (false === $validSource) {
|
||||
@@ -338,7 +338,7 @@ class RecurrenceFormRequest extends FormRequest
|
||||
}
|
||||
|
||||
// validate destination account
|
||||
$validDestination = $accountValidator->validateDestination($destinationId, null, null);
|
||||
$validDestination = $accountValidator->validateDestination(['id' => $destinationId,]);
|
||||
// do something with result:
|
||||
if (false === $validDestination) {
|
||||
$message = (string)trans('validation.generic_invalid_destination');
|
||||
|
@@ -147,18 +147,24 @@ class ReportFormRequest extends FormRequest
|
||||
$range = $this->get('daterange');
|
||||
$parts = explode(' - ', (string)$range);
|
||||
if (2 === count($parts)) {
|
||||
try {
|
||||
$date = new Carbon($parts[1]);
|
||||
|
||||
} catch (Exception $e) {
|
||||
$error = sprintf('"%s" is not a valid date range: %s', $range, $e->getMessage());
|
||||
Log::error($error);
|
||||
throw new FireflyException($error, 0, $e);
|
||||
|
||||
$string = $parts[1];
|
||||
// validate as date
|
||||
// if regex for YYYY-MM-DD:
|
||||
$pattern = '/^(19|20)\d\d-(0[1-9]|1[012])-(0[1-9]|[12][\d]|3[01])$/';
|
||||
if (preg_match($pattern, $string)) {
|
||||
try {
|
||||
$date = new Carbon($parts[1]);
|
||||
} catch (Exception $e) {
|
||||
$error = sprintf('"%s" is not a valid date range: %s', $range, $e->getMessage());
|
||||
Log::error($error);
|
||||
throw new FireflyException($error, 0, $e);
|
||||
}
|
||||
return $date;
|
||||
}
|
||||
|
||||
$error = sprintf('"%s" is not a valid date range: %s', $range, 'invalid format :(');
|
||||
Log::error($error);
|
||||
throw new FireflyException($error, 0);
|
||||
}
|
||||
|
||||
return $date;
|
||||
}
|
||||
|
||||
@@ -175,15 +181,23 @@ class ReportFormRequest extends FormRequest
|
||||
$range = $this->get('daterange');
|
||||
$parts = explode(' - ', (string)$range);
|
||||
if (2 === count($parts)) {
|
||||
try {
|
||||
$date = new Carbon($parts[0]);
|
||||
|
||||
} catch (Exception $e) {
|
||||
$error = sprintf('"%s" is not a valid date range: %s', $range, $e->getMessage());
|
||||
Log::error($error);
|
||||
throw new FireflyException($error, 0, $e);
|
||||
|
||||
$string = $parts[0];
|
||||
// validate as date
|
||||
// if regex for YYYY-MM-DD:
|
||||
$pattern = '/^(19|20)\d\d-(0[1-9]|1[012])-(0[1-9]|[12][\d]|3[01])$/';
|
||||
if (preg_match($pattern, $string)) {
|
||||
try {
|
||||
$date = new Carbon($parts[1]);
|
||||
} catch (Exception $e) {
|
||||
$error = sprintf('"%s" is not a valid date range: %s', $range, $e->getMessage());
|
||||
Log::error($error);
|
||||
throw new FireflyException($error, 0, $e);
|
||||
}
|
||||
return $date;
|
||||
}
|
||||
$error = sprintf('"%s" is not a valid date range: %s', $range, 'invalid format :(');
|
||||
Log::error($error);
|
||||
throw new FireflyException($error, 0);
|
||||
}
|
||||
|
||||
return $date;
|
||||
|
@@ -45,7 +45,7 @@ class TagFormRequest extends FormRequest
|
||||
{
|
||||
$data = [
|
||||
'tag' => $this->string('tag'),
|
||||
'date' => $this->date('date'),
|
||||
'date' => $this->getCarbonDate('date'),
|
||||
'description' => $this->string('description'),
|
||||
];
|
||||
|
||||
|
@@ -68,13 +68,13 @@ class MailError extends Job implements ShouldQueue
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
$email = config('firefly.site_owner');
|
||||
$email = (string) config('firefly.site_owner');
|
||||
$args = $this->exception;
|
||||
$args['loggedIn'] = $this->userData['id'] > 0;
|
||||
$args['user'] = $this->userData;
|
||||
$args['ip'] = $this->ipAddress;
|
||||
$args['token'] = config('firefly.ipinfo_token');
|
||||
if ($this->attempts() < 3) {
|
||||
if ($this->attempts() < 3 && strlen($email) > 0) {
|
||||
try {
|
||||
Mail::send(
|
||||
['emails.error-html', 'emails.error-text'],
|
||||
|
@@ -24,6 +24,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\Jobs;
|
||||
|
||||
use Log;
|
||||
use FireflyIII\Models\WebhookMessage;
|
||||
use FireflyIII\Services\Webhook\WebhookSenderInterface;
|
||||
use Illuminate\Bus\Queueable;
|
||||
@@ -58,6 +59,7 @@ class SendWebhookMessage implements ShouldQueue
|
||||
*/
|
||||
public function handle(): void
|
||||
{
|
||||
Log::debug(sprintf('Now handling webhook message #%d', $this->message->id));
|
||||
// send job!
|
||||
$sender = app(WebhookSenderInterface::class);
|
||||
$sender->setMessage($this->message);
|
||||
|
@@ -25,7 +25,7 @@ declare(strict_types=1);
|
||||
namespace FireflyIII\Ldap;
|
||||
|
||||
use FireflyIII\User as DatabaseUser;
|
||||
use LdapRecord\Models\OpenLDAP\User as LdapUser;
|
||||
use LdapRecord\Models\Entry;
|
||||
|
||||
/**
|
||||
* Class AttributeHandler
|
||||
@@ -33,12 +33,12 @@ use LdapRecord\Models\OpenLDAP\User as LdapUser;
|
||||
class AttributeHandler
|
||||
{
|
||||
/**
|
||||
* @param LdapUser $ldap
|
||||
* @param Entry $ldapUser
|
||||
* @param DatabaseUser $database
|
||||
*/
|
||||
public function handle(LdapUser $ldap, DatabaseUser $database)
|
||||
public function handle(Entry $ldapUser, DatabaseUser $database)
|
||||
{
|
||||
$database->email = $ldap->getFirstAttribute('mail');
|
||||
$database->email = $ldapUser->getFirstAttribute('mail');
|
||||
$database->save();
|
||||
}
|
||||
}
|
||||
|
@@ -1,4 +1,25 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* UserDefinedRule.php
|
||||
* Copyright (c) 2022 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\Ldap\Rules;
|
||||
|
@@ -1,4 +1,25 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* UserDefinedScope.php
|
||||
* Copyright (c) 2022 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\Ldap\Scopes;
|
||||
|
@@ -494,10 +494,8 @@ class AccountRepository implements AccountRepositoryInterface
|
||||
public function getOpeningBalanceGroup(Account $account): ?TransactionGroup
|
||||
{
|
||||
$journal = $this->getOpeningBalance($account);
|
||||
$group = null;
|
||||
$group = $journal?->transactionGroup;
|
||||
|
||||
return $group;
|
||||
return $journal?->transactionGroup;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -637,11 +635,9 @@ class AccountRepository implements AccountRepositoryInterface
|
||||
*/
|
||||
public function oldestJournalDate(Account $account): ?Carbon
|
||||
{
|
||||
$result = null;
|
||||
$journal = $this->oldestJournal($account);
|
||||
$result = $journal?->date;
|
||||
|
||||
return $result;
|
||||
return $journal?->date;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -389,4 +389,18 @@ class LinkTypeRepository implements LinkTypeRepositoryInterface
|
||||
|
||||
return TransactionJournalLink::whereDestinationId($two->id)->whereSourceId($one->id)->first();
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function switchLinkById(int $linkId): bool
|
||||
{
|
||||
/** @var TransactionJournalLink $link */
|
||||
$link = TransactionJournalLink::find($linkId);
|
||||
if (null !== $link && $link->source->user->id === $this->user->id) {
|
||||
$this->switchLink($link);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@@ -154,6 +154,13 @@ interface LinkTypeRepositoryInterface
|
||||
*/
|
||||
public function switchLink(TransactionJournalLink $link): bool;
|
||||
|
||||
/**
|
||||
* @param int $linkId
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function switchLinkById(int $linkId): bool;
|
||||
|
||||
/**
|
||||
* @param LinkType $linkType
|
||||
* @param array $data
|
||||
|
@@ -466,6 +466,15 @@ class RuleRepository implements RuleRepositoryInterface
|
||||
}
|
||||
}
|
||||
$rule->save();
|
||||
$rule->refresh();
|
||||
$group = $rule->ruleGroup;
|
||||
// update the order:
|
||||
$this->resetRuleOrder($group);
|
||||
if (array_key_exists('order', $data)) {
|
||||
$this->moveRule($rule, $group, (int)$data['order']);
|
||||
}
|
||||
|
||||
|
||||
// update the triggers:
|
||||
if (array_key_exists('trigger', $data) && 'update-journal' === $data['trigger']) {
|
||||
$this->setRuleTrigger('update-journal', $rule);
|
||||
|
@@ -141,7 +141,7 @@ class TransactionGroupRepository implements TransactionGroupRepositoryInterface
|
||||
)
|
||||
->with(['source', 'destination', 'source.transactions'])
|
||||
->leftJoin('link_types', 'link_types.id', '=', 'journal_links.link_type_id')
|
||||
->get(['journal_links.*', 'link_types.inward', 'link_types.outward']);
|
||||
->get(['journal_links.*', 'link_types.inward', 'link_types.outward', 'link_types.editable']);
|
||||
/** @var TransactionJournalLink $entry */
|
||||
foreach ($set as $entry) {
|
||||
$journalId = in_array($entry->source_id, $journals, true) ? $entry->source_id : $entry->destination_id;
|
||||
@@ -155,6 +155,7 @@ class TransactionGroupRepository implements TransactionGroupRepositoryInterface
|
||||
'link' => $entry->outward,
|
||||
'group' => $entry->destination->transaction_group_id,
|
||||
'description' => $entry->destination->description,
|
||||
'editable' => 1===$entry->editable,
|
||||
'amount' => $amount,
|
||||
'foreign_amount' => $foreignAmount,
|
||||
];
|
||||
@@ -167,6 +168,7 @@ class TransactionGroupRepository implements TransactionGroupRepositoryInterface
|
||||
'link' => $entry->inward,
|
||||
'group' => $entry->source->transaction_group_id,
|
||||
'description' => $entry->source->description,
|
||||
'editable' => 1===$entry->editable,
|
||||
'amount' => $amount,
|
||||
'foreign_amount' => $foreignAmount,
|
||||
];
|
||||
@@ -484,4 +486,16 @@ class TransactionGroupRepository implements TransactionGroupRepositoryInterface
|
||||
|
||||
return $return;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function countAttachments(int $journalId): int
|
||||
{
|
||||
/** @var TransactionJournal $journal */
|
||||
$journal = $this->user->transactionJournals()->find($journalId);
|
||||
|
||||
return $journal->attachments()->count();
|
||||
|
||||
}
|
||||
}
|
||||
|
@@ -86,6 +86,13 @@ interface TransactionGroupRepositoryInterface
|
||||
*/
|
||||
public function getLocation(int $journalId): ?Location;
|
||||
|
||||
/**
|
||||
* @param int $journalId
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function countAttachments(int $journalId): int;
|
||||
|
||||
/**
|
||||
* Return object with all found meta field things as Carbon objects.
|
||||
*
|
||||
|
@@ -30,6 +30,7 @@ use FireflyIII\Models\Bill;
|
||||
use FireflyIII\Models\Budget;
|
||||
use FireflyIII\Models\Category;
|
||||
use FireflyIII\Models\PiggyBank;
|
||||
use FireflyIII\Models\TransactionJournal;
|
||||
use Illuminate\Contracts\Validation\Rule;
|
||||
use Log;
|
||||
|
||||
@@ -83,6 +84,7 @@ class BelongsUser implements Rule
|
||||
'piggy_bank_id' => $this->validatePiggyBankId((int)$value),
|
||||
'piggy_bank_name' => $this->validatePiggyBankName($value),
|
||||
'bill_id' => $this->validateBillId((int)$value),
|
||||
'transaction_journal_id' => $this->validateJournalId((int)$value),
|
||||
'bill_name' => $this->validateBillName($value),
|
||||
'budget_id' => $this->validateBudgetId((int)$value),
|
||||
'category_id' => $this->validateCategoryId((int)$value),
|
||||
@@ -185,6 +187,21 @@ class BelongsUser implements Rule
|
||||
return 1 === $count;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $value
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private function validateJournalId(int $value): bool
|
||||
{
|
||||
if (0 === $value) {
|
||||
return true;
|
||||
}
|
||||
$count = TransactionJournal::where('id', '=', $value)->where('user_id', '=', auth()->user()->id)->count();
|
||||
|
||||
return 1 === $count;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $value
|
||||
*
|
||||
|
@@ -60,14 +60,14 @@ class IsTransferAccount implements Rule
|
||||
$validator->setTransactionType(TransactionType::TRANSFER);
|
||||
$validator->setUser(auth()->user());
|
||||
|
||||
$validAccount = $validator->validateSource(null, (string)$value, null);
|
||||
$validAccount = $validator->validateSource(['name' => (string)$value,]);
|
||||
if (true === $validAccount) {
|
||||
Log::debug('Found account based on name. Return true.');
|
||||
|
||||
// found by name, use repos to return.
|
||||
return true;
|
||||
}
|
||||
$validAccount = $validator->validateSource((int)$value, null, null);
|
||||
$validAccount = $validator->validateSource(['id' => (int)$value,]);
|
||||
Log::debug(sprintf('Search by id (%d), result is %s.', (int)$value, var_export($validAccount, true)));
|
||||
|
||||
return false !== $validAccount;
|
||||
|
@@ -69,7 +69,8 @@ trait AccountServiceTrait
|
||||
return null;
|
||||
}
|
||||
|
||||
return $iban;
|
||||
|
||||
return app('steam')->filterSpaces($iban);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -530,6 +531,7 @@ trait AccountServiceTrait
|
||||
if (null === $obGroup) {
|
||||
return $this->createOBGroupV2($account, $openingBalance, $openingBalanceDate);
|
||||
}
|
||||
Log::debug('Update OB group');
|
||||
|
||||
// if exists, update:
|
||||
$currency = $this->accountRepository->getAccountCurrency($account);
|
||||
@@ -547,6 +549,7 @@ trait AccountServiceTrait
|
||||
|
||||
// if amount is negative:
|
||||
if (1 === bccomp('0', $openingBalance)) {
|
||||
Log::debug('Amount is negative.');
|
||||
// account transaction loses money:
|
||||
$accountTransaction->amount = app('steam')->negative($openingBalance);
|
||||
$accountTransaction->transaction_currency_id = $currency->id;
|
||||
@@ -556,6 +559,7 @@ trait AccountServiceTrait
|
||||
$obTransaction->transaction_currency_id = $currency->id;
|
||||
}
|
||||
if (-1 === bccomp('0', $openingBalance)) {
|
||||
Log::debug('Amount is positive.');
|
||||
// account gains money:
|
||||
$accountTransaction->amount = app('steam')->positive($openingBalance);
|
||||
$accountTransaction->transaction_currency_id = $currency->id;
|
||||
|
@@ -63,9 +63,6 @@ trait JournalServiceTrait
|
||||
// some debug logging:
|
||||
Log::debug(sprintf('Now in getAccount(%s)', $direction), $data);
|
||||
|
||||
// final result:
|
||||
$result = null;
|
||||
|
||||
// expected type of source account, in order of preference
|
||||
/** @var array $array */
|
||||
$array = config('firefly.expected_source_types');
|
||||
@@ -76,12 +73,17 @@ trait JournalServiceTrait
|
||||
$message = 'Based on the fact that the transaction is a %s, the %s account should be in: %s. Direction is %s.';
|
||||
Log::debug(sprintf($message, $transactionType, $direction, implode(', ', $expectedTypes[$transactionType] ?? ['UNKNOWN']), $direction));
|
||||
|
||||
Log::debug('Now searching by ID');
|
||||
$result = $this->findAccountById($data, $expectedTypes[$transactionType]);
|
||||
$result = $this->findAccountByName($result, $data, $expectedTypes[$transactionType]);
|
||||
Log::debug('If nothing is found, searching by IBAN');
|
||||
$result = $this->findAccountByIban($result, $data, $expectedTypes[$transactionType]);
|
||||
Log::debug('If nothing is found, searching by number');
|
||||
$result = $this->findAccountByNumber($result, $data, $expectedTypes[$transactionType]);
|
||||
Log::debug('If nothing is found, searching by name');
|
||||
$result = $this->findAccountByName($result, $data, $expectedTypes[$transactionType]);
|
||||
Log::debug('If nothing is found, create it.');
|
||||
$result = $this->createAccount($result, $data, $expectedTypes[$transactionType][0]);
|
||||
|
||||
Log::debug('If cant be created, return cash account.');
|
||||
return $this->getCashAccount($result, $data, $expectedTypes[$transactionType]);
|
||||
}
|
||||
|
||||
@@ -118,7 +120,6 @@ trait JournalServiceTrait
|
||||
{
|
||||
// second attempt, find by name.
|
||||
if (null === $account && null !== $data['name']) {
|
||||
Log::debug('Found nothing by account ID.');
|
||||
// find by preferred type.
|
||||
$source = $this->accountRepository->findByName($data['name'], [$types[0]]);
|
||||
// or any expected type.
|
||||
@@ -204,7 +205,7 @@ trait JournalServiceTrait
|
||||
if (null !== $account) {
|
||||
Log::debug(
|
||||
sprintf(
|
||||
'Was also given %s account #%d ("%s") so will simply return that.',
|
||||
'Was given %s account #%d ("%s") so will simply return that.',
|
||||
$account->accountType->type, $account->id, $account->name
|
||||
|
||||
)
|
||||
|
@@ -136,11 +136,12 @@ trait RecurringTransactionTrait
|
||||
$validator = app(AccountValidator::class);
|
||||
$validator->setUser($recurrence->user);
|
||||
$validator->setTransactionType($recurrence->transactionType->type);
|
||||
if (!$validator->validateSource($source->id, null, null)) {
|
||||
|
||||
if (!$validator->validateSource(['id' => $source->id])) {
|
||||
throw new FireflyException(sprintf('Source invalid: %s', $validator->sourceError));
|
||||
}
|
||||
|
||||
if (!$validator->validateDestination($destination->id, null, null)) {
|
||||
if (!$validator->validateDestination(['id' => $destination->id])) {
|
||||
throw new FireflyException(sprintf('Destination invalid: %s', $validator->destError));
|
||||
}
|
||||
if (array_key_exists('foreign_amount', $array) && '' === (string)$array['foreign_amount']) {
|
||||
|
@@ -135,7 +135,7 @@ class AccountUpdateService
|
||||
$account->active = $data['active'];
|
||||
}
|
||||
if (array_key_exists('iban', $data)) {
|
||||
$account->iban = $data['iban'];
|
||||
$account->iban = app('steam')->filterSpaces((string)$data['iban']);
|
||||
}
|
||||
|
||||
// set liability, but account must already be a liability.
|
||||
@@ -292,7 +292,6 @@ class AccountUpdateService
|
||||
*/
|
||||
private function updateOpeningBalance(Account $account, array $data): void
|
||||
{
|
||||
|
||||
// has valid initial balance (IB) data?
|
||||
$type = $account->accountType;
|
||||
if (in_array($type->type, $this->canHaveOpeningBalance, true)) {
|
||||
@@ -326,7 +325,7 @@ class AccountUpdateService
|
||||
if ($this->validOBData($data) && !$this->isEmptyOBData($data)) {
|
||||
$openingBalance = $data['opening_balance'];
|
||||
$openingBalanceDate = $data['opening_balance_date'];
|
||||
if ('credit' === $data['liability_direction']) {
|
||||
if ('credit' === $direction) {
|
||||
$this->updateCreditTransaction($account, $openingBalance, $openingBalanceDate);
|
||||
}
|
||||
}
|
||||
|
@@ -79,12 +79,22 @@ class GroupUpdateService
|
||||
|
||||
$existing = $transactionGroup->transactionJournals->pluck('id')->toArray();
|
||||
$updated = $this->updateTransactions($transactionGroup, $transactions);
|
||||
$result = array_diff($existing, $updated);
|
||||
Log::debug('Array of updated IDs: ', $updated);
|
||||
|
||||
if (0 === count($updated)) {
|
||||
Log::error('There were no transactions updated or created. Will not delete anything.');
|
||||
$transactionGroup->refresh();
|
||||
app('preferences')->mark();
|
||||
return $transactionGroup;
|
||||
}
|
||||
|
||||
$result = array_diff($existing, $updated);
|
||||
Log::debug('Result of DIFF: ', $result);
|
||||
if (count($result) > 0) {
|
||||
/** @var string $deletedId */
|
||||
foreach ($result as $deletedId) {
|
||||
/** @var TransactionJournal $journal */
|
||||
$journal = $transactionGroup->transactionJournals()->find((int)$deletedId);
|
||||
$journal = $transactionGroup->transactionJournals()->find((int) $deletedId);
|
||||
/** @var JournalDestroyService $service */
|
||||
$service = app(JournalDestroyService::class);
|
||||
$service->destroy($journal);
|
||||
@@ -109,6 +119,9 @@ class GroupUpdateService
|
||||
if (empty($data)) {
|
||||
return;
|
||||
}
|
||||
if (1 === count($data) && array_key_exists('transaction_journal_id', $data)) {
|
||||
return;
|
||||
}
|
||||
/** @var JournalUpdateService $updateService */
|
||||
$updateService = app(JournalUpdateService::class);
|
||||
$updateService->setTransactionGroup($transactionGroup);
|
||||
@@ -127,6 +140,7 @@ class GroupUpdateService
|
||||
*/
|
||||
private function updateTransactions(TransactionGroup $transactionGroup, array $transactions): array
|
||||
{
|
||||
// updated or created transaction journals:
|
||||
$updated = [];
|
||||
/**
|
||||
* @var int $index
|
||||
@@ -134,7 +148,7 @@ class GroupUpdateService
|
||||
*/
|
||||
foreach ($transactions as $index => $transaction) {
|
||||
Log::debug(sprintf('Now at #%d of %d', ($index + 1), count($transactions)), $transaction);
|
||||
$journalId = (int)($transaction['transaction_journal_id'] ?? 0);
|
||||
$journalId = (int) ($transaction['transaction_journal_id'] ?? 0);
|
||||
/** @var TransactionJournal|null $journal */
|
||||
$journal = $transactionGroup->transactionJournals()->find($journalId);
|
||||
if (null === $journal) {
|
||||
@@ -151,8 +165,14 @@ class GroupUpdateService
|
||||
}
|
||||
}
|
||||
Log::debug('Call createTransactionJournal');
|
||||
$this->createTransactionJournal($transactionGroup, $transaction);
|
||||
$newJournal = $this->createTransactionJournal($transactionGroup, $transaction);
|
||||
Log::debug('Done calling createTransactionJournal');
|
||||
if (null !== $newJournal) {
|
||||
$updated[] = $newJournal->id;
|
||||
}
|
||||
if (null === $newJournal) {
|
||||
Log::error('createTransactionJournal returned NULL, indicating something went wrong.');
|
||||
}
|
||||
}
|
||||
if (null !== $journal) {
|
||||
Log::debug('Call updateTransactionJournal');
|
||||
@@ -169,12 +189,13 @@ class GroupUpdateService
|
||||
* @param TransactionGroup $transactionGroup
|
||||
* @param array $data
|
||||
*
|
||||
* @return TransactionJournal|null
|
||||
*
|
||||
* @throws FireflyException
|
||||
* @throws DuplicateTransactionException
|
||||
*/
|
||||
private function createTransactionJournal(TransactionGroup $transactionGroup, array $data): void
|
||||
private function createTransactionJournal(TransactionGroup $transactionGroup, array $data): ?TransactionJournal
|
||||
{
|
||||
|
||||
$submission = [
|
||||
'transactions' => [
|
||||
$data,
|
||||
@@ -195,6 +216,11 @@ class GroupUpdateService
|
||||
$transactionGroup->transactionJournals()->save($journal);
|
||||
}
|
||||
);
|
||||
if (0 === $collection->count()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $collection->first();
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -84,7 +84,7 @@ class JournalUpdateService
|
||||
$this->currencyRepository = app(CurrencyRepositoryInterface::class);
|
||||
$this->metaString = ['sepa_cc', 'sepa_ct_op', 'sepa_ct_id', 'sepa_db', 'sepa_country', 'sepa_ep', 'sepa_ci', 'sepa_batch_id',
|
||||
'recurrence_id',
|
||||
'internal_reference', 'bunq_payment_id', 'external_id', 'external_uri'];
|
||||
'internal_reference', 'bunq_payment_id', 'external_id', 'external_url'];
|
||||
$this->metaDate = ['interest_date', 'book_date', 'process_date', 'due_date', 'payment_date', 'invoice_date',];
|
||||
}
|
||||
|
||||
@@ -215,7 +215,7 @@ class JournalUpdateService
|
||||
$validator->setTransactionType($expectedType);
|
||||
$validator->setUser($this->transactionJournal->user);
|
||||
|
||||
$result = $validator->validateSource($sourceId, $sourceName, null);
|
||||
$result = $validator->validateSource(['id' =>$sourceId]);
|
||||
Log::debug(sprintf('hasValidSourceAccount(%d, "%s") will return %s', $sourceId, $sourceName, var_export($result, true)));
|
||||
|
||||
// See reference nr. 95
|
||||
@@ -309,7 +309,7 @@ class JournalUpdateService
|
||||
$validator->setTransactionType($expectedType);
|
||||
$validator->setUser($this->transactionJournal->user);
|
||||
$validator->source = $this->getValidSourceAccount();
|
||||
$result = $validator->validateDestination($destId, $destName, null);
|
||||
$result = $validator->validateDestination(['id' => $destId]);
|
||||
Log::debug(sprintf('hasValidDestinationAccount(%d, "%s") will return %s', $destId, $destName, var_export($result, true)));
|
||||
|
||||
// See reference nr. 96
|
||||
|
@@ -23,6 +23,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\Services\Webhook;
|
||||
|
||||
use FireflyIII\Exceptions\FireflyException;
|
||||
use FireflyIII\Helpers\Webhook\SignatureGeneratorInterface;
|
||||
use FireflyIII\Models\WebhookAttempt;
|
||||
use FireflyIII\Models\WebhookMessage;
|
||||
@@ -55,7 +56,24 @@ class StandardWebhookSender implements WebhookSenderInterface
|
||||
// have the signature generator generate a signature. If it fails, the error thrown will
|
||||
// end up in send() to be caught.
|
||||
$signatureGenerator = app(SignatureGeneratorInterface::class);
|
||||
$signature = $signatureGenerator->generate($this->message);
|
||||
|
||||
try {
|
||||
$signature = $signatureGenerator->generate($this->message);
|
||||
} catch(FireflyException $e) {
|
||||
Log::error('Did not send message because of a Firefly III Exception.');
|
||||
Log::error($e->getMessage());
|
||||
Log::error($e->getTraceAsString());
|
||||
$attempt = new WebhookAttempt;
|
||||
$attempt->webhookMessage()->associate($this->message);
|
||||
$attempt->status_code = 0;
|
||||
$attempt->logs = sprintf('Exception: %s', $e->getMessage());
|
||||
$attempt->save();
|
||||
$this->message->errored = true;
|
||||
$this->message->sent = false;
|
||||
$this->message->save();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
Log::debug(sprintf('Trying to send webhook message #%d', $this->message->id));
|
||||
|
||||
|
@@ -709,7 +709,7 @@ class ExportDataGenerator
|
||||
$metaData['sepa_ep'],
|
||||
$metaData['sepa_ci'],
|
||||
$metaData['sepa_batch_id'],
|
||||
$metaData['external_uri'],
|
||||
$metaData['external_url'],
|
||||
|
||||
// dates
|
||||
$metaData['interest_date'],
|
||||
|
@@ -98,17 +98,17 @@ class ParseDateString
|
||||
return $this->parseRelativeDate($date);
|
||||
}
|
||||
if ('xxxx-xx-xx' === strtolower($date)) {
|
||||
throw new FireflyException(sprintf('[a]Not a recognised date format: "%s"', $date));
|
||||
throw new FireflyException(sprintf('[a] Not a recognised date format: "%s"', $date));
|
||||
}
|
||||
// can't do a partial year:
|
||||
$substrCount = substr_count(substr($date, 0, 4), 'x', 0);
|
||||
if (10 === strlen($date) && $substrCount > 0 && $substrCount < 4) {
|
||||
throw new FireflyException(sprintf('[b]Not a recognised date format: "%s"', $date));
|
||||
throw new FireflyException(sprintf('[b] Not a recognised date format: "%s"', $date));
|
||||
}
|
||||
|
||||
// maybe a date range
|
||||
if (10 === strlen($date) && (str_contains($date, 'xx') || str_contains($date, 'xxxx'))) {
|
||||
Log::debug(sprintf('[c]Detected a date range ("%s"), return a fake date.', $date));
|
||||
Log::debug(sprintf('[c] Detected a date range ("%s"), return a fake date.', $date));
|
||||
// very lazy way to parse the date without parsing it, because this specific function
|
||||
// cant handle date ranges.
|
||||
return new Carbon('1984-09-17');
|
||||
@@ -211,34 +211,39 @@ class ParseDateString
|
||||
|
||||
/**
|
||||
* @param string $date
|
||||
* @param Carbon $journalDate
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function parseRange(string $date, Carbon $journalDate): array
|
||||
public function parseRange(string $date): array
|
||||
{
|
||||
// several types of range can be submitted
|
||||
$result = [
|
||||
'exact' => new Carbon('1984-09-17'),
|
||||
];
|
||||
switch (true) {
|
||||
default:
|
||||
break;
|
||||
case $this->isDayRange($date):
|
||||
return $this->parseDayRange($date, $journalDate);
|
||||
$result = $this->parseDayRange($date);
|
||||
break;
|
||||
case $this->isMonthRange($date):
|
||||
return $this->parseMonthRange($date, $journalDate);
|
||||
$result = $this->parseMonthRange($date);
|
||||
break;
|
||||
case $this->isYearRange($date):
|
||||
return $this->parseYearRange($date, $journalDate);
|
||||
$result = $this->parseYearRange($date);
|
||||
break;
|
||||
case $this->isMonthDayRange($date):
|
||||
return $this->parseMonthDayRange($date, $journalDate);
|
||||
$result = $this->parseMonthDayRange($date);
|
||||
break;
|
||||
case $this->isDayYearRange($date):
|
||||
return $this->parseDayYearRange($date, $journalDate);
|
||||
$result = $this->parseDayYearRange($date);
|
||||
break;
|
||||
case $this->isMonthYearRange($date):
|
||||
return $this->parseMonthYearRange($date, $journalDate);
|
||||
$result = $this->parseMonthYearRange($date);
|
||||
break;
|
||||
}
|
||||
|
||||
return [
|
||||
'start' => new Carbon('1984-09-17'),
|
||||
'end' => new Carbon('1984-09-17'),
|
||||
];
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -261,23 +266,18 @@ class ParseDateString
|
||||
}
|
||||
|
||||
/**
|
||||
* format of string is xxxx-xx-DD
|
||||
*
|
||||
* @param string $date
|
||||
* @param Carbon $journalDate
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function parseDayRange(string $date, Carbon $journalDate): array
|
||||
protected function parseDayRange(string $date): array
|
||||
{
|
||||
// format of string is xxxx-xx-DD
|
||||
$validDate = str_replace(['xxxx'], [$journalDate->year], $date);
|
||||
$validDate = str_replace(['xx'], [$journalDate->format('m')], $validDate);
|
||||
Log::debug(sprintf('parseDayRange: Parsed "%s" into "%s"', $date, $validDate));
|
||||
$start = Carbon::createFromFormat('Y-m-d', $validDate)->startOfDay();
|
||||
$end = Carbon::createFromFormat('Y-m-d', $validDate)->endOfDay();
|
||||
$parts = explode('-', $date);
|
||||
|
||||
return [
|
||||
'start' => $start,
|
||||
'end' => $end,
|
||||
'day' => $parts[2],
|
||||
];
|
||||
}
|
||||
|
||||
@@ -301,29 +301,19 @@ class ParseDateString
|
||||
}
|
||||
|
||||
/**
|
||||
* format of string is xxxx-MM-xx
|
||||
*
|
||||
* @param string $date
|
||||
* @param Carbon $journalDate
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function parseMonthRange(string $date, Carbon $journalDate): array
|
||||
protected function parseMonthRange(string $date): array
|
||||
{
|
||||
// because 31 would turn February into March unexpectedly and the exact day is irrelevant here.
|
||||
$day = $journalDate->format('d');
|
||||
if ((int)$day > 28) {
|
||||
$day = '28';
|
||||
}
|
||||
|
||||
// format of string is xxxx-MM-xx
|
||||
$validDate = str_replace(['xxxx'], [$journalDate->year], $date);
|
||||
$validDate = str_replace(['xx'], [$day], $validDate);
|
||||
Log::debug(sprintf('parseMonthRange: Parsed "%s" into "%s"', $date, $validDate));
|
||||
$start = Carbon::createFromFormat('Y-m-d', $validDate)->startOfMonth();
|
||||
$end = Carbon::createFromFormat('Y-m-d', $validDate)->endOfMonth();
|
||||
Log::debug(sprintf('parseMonthRange: Parsed "%s".', $date));
|
||||
$parts = explode('-', $date);
|
||||
|
||||
return [
|
||||
'start' => $start,
|
||||
'end' => $end,
|
||||
'month' => $parts[1],
|
||||
];
|
||||
}
|
||||
|
||||
@@ -347,24 +337,19 @@ class ParseDateString
|
||||
}
|
||||
|
||||
/**
|
||||
* format of string is YYYY-xx-xx
|
||||
*
|
||||
* @param string $date
|
||||
* @param Carbon $journalDate
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function parseYearRange(string $date, Carbon $journalDate): array
|
||||
protected function parseYearRange(string $date): array
|
||||
{
|
||||
// format of string is YYYY-xx-xx
|
||||
// kind of a convulted way of replacing variables but I'm lazy.
|
||||
$validDate = str_replace(['xx-xx'], [sprintf('%s-xx', $journalDate->format('m'))], $date);
|
||||
$validDate = str_replace(['xx'], [$journalDate->format('d')], $validDate);
|
||||
Log::debug(sprintf('parseYearRange: Parsed "%s" into "%s"', $date, $validDate));
|
||||
$start = Carbon::createFromFormat('Y-m-d', $validDate)->startOfYear();
|
||||
$end = Carbon::createFromFormat('Y-m-d', $validDate)->endOfYear();
|
||||
Log::debug(sprintf('parseYearRange: Parsed "%s"', $date));
|
||||
$parts = explode('-', $date);
|
||||
|
||||
return [
|
||||
'start' => $start,
|
||||
'end' => $end,
|
||||
'year' => $parts[0],
|
||||
];
|
||||
}
|
||||
|
||||
@@ -388,23 +373,20 @@ class ParseDateString
|
||||
}
|
||||
|
||||
/**
|
||||
* format of string is xxxx-MM-DD
|
||||
*
|
||||
* @param string $date
|
||||
* @param Carbon $journalDate
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function parseMonthDayRange(string $date, Carbon $journalDate): array
|
||||
private function parseMonthDayRange(string $date): array
|
||||
{
|
||||
// Any year.
|
||||
// format of string is xxxx-MM-DD
|
||||
$validDate = str_replace(['xxxx'], [$journalDate->year], $date);
|
||||
Log::debug(sprintf('parseMonthDayRange: Parsed "%s" into "%s"', $date, $validDate));
|
||||
$start = Carbon::createFromFormat('Y-m-d', $validDate)->startOfDay();
|
||||
$end = Carbon::createFromFormat('Y-m-d', $validDate)->endOfDay();
|
||||
Log::debug(sprintf('parseMonthDayRange: Parsed "%s".', $date));
|
||||
$parts = explode('-', $date);
|
||||
|
||||
return [
|
||||
'start' => $start,
|
||||
'end' => $end,
|
||||
'month' => $parts[1],
|
||||
'day' => $parts[2],
|
||||
];
|
||||
}
|
||||
|
||||
@@ -428,23 +410,20 @@ class ParseDateString
|
||||
}
|
||||
|
||||
/**
|
||||
* format of string is YYYY-xx-DD
|
||||
*
|
||||
* @param string $date
|
||||
* @param Carbon $journalDate
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function parseDayYearRange(string $date, Carbon $journalDate): array
|
||||
private function parseDayYearRange(string $date): array
|
||||
{
|
||||
// Any year.
|
||||
// format of string is YYYY-xx-DD
|
||||
$validDate = str_replace(['xx'], [$journalDate->format('m')], $date);
|
||||
Log::debug(sprintf('parseDayYearRange: Parsed "%s" into "%s"', $date, $validDate));
|
||||
$start = Carbon::createFromFormat('Y-m-d', $validDate)->startOfDay();
|
||||
$end = Carbon::createFromFormat('Y-m-d', $validDate)->endOfDay();
|
||||
Log::debug(sprintf('parseDayYearRange: Parsed "%s".', $date));
|
||||
$parts = explode('-', $date);
|
||||
|
||||
return [
|
||||
'start' => $start,
|
||||
'end' => $end,
|
||||
'year' => $parts[0],
|
||||
'day' => $parts[2],
|
||||
];
|
||||
}
|
||||
|
||||
@@ -468,28 +447,20 @@ class ParseDateString
|
||||
}
|
||||
|
||||
/**
|
||||
* format of string is YYYY-MM-xx
|
||||
*
|
||||
* @param string $date
|
||||
* @param Carbon $journalDate
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function parseMonthYearRange(string $date, Carbon $journalDate): array
|
||||
protected function parseMonthYearRange(string $date): array
|
||||
{
|
||||
// because 31 would turn February into March unexpectedly and the exact day is irrelevant here.
|
||||
$day = $journalDate->format('d');
|
||||
if ((int)$day > 28) {
|
||||
$day = '28';
|
||||
}
|
||||
|
||||
// format of string is YYYY-MM-xx
|
||||
$validDate = str_replace(['xx'], [$day], $date);
|
||||
Log::debug(sprintf('parseMonthYearRange: Parsed "%s" into "%s"', $date, $validDate));
|
||||
$start = Carbon::createFromFormat('Y-m-d', $validDate)->startOfMonth();
|
||||
$end = Carbon::createFromFormat('Y-m-d', $validDate)->endOfMonth();
|
||||
Log::debug(sprintf('parseMonthYearRange: Parsed "%s".', $date));
|
||||
$parts = explode('-', $date);
|
||||
|
||||
return [
|
||||
'start' => $start,
|
||||
'end' => $end,
|
||||
'year' => $parts[0],
|
||||
'month' => $parts[1],
|
||||
];
|
||||
}
|
||||
|
||||
|
@@ -187,7 +187,7 @@ trait ConvertsDataTypes
|
||||
*
|
||||
* @return Carbon|null
|
||||
*/
|
||||
protected function date(string $field): ?Carbon
|
||||
protected function getCarbonDate(string $field): ?Carbon
|
||||
{
|
||||
$result = null;
|
||||
try {
|
||||
|
@@ -606,48 +606,15 @@ class OperatorQuerySearch implements SearchInterface
|
||||
//
|
||||
case 'date_is':
|
||||
$range = $this->parseDateRange($value);
|
||||
Log::debug(
|
||||
sprintf(
|
||||
'Set "%s" using collector with value "%s" (%s - %s)', $operator, $value, $range['start']->format('Y-m-d'),
|
||||
$range['end']->format('Y-m-d')
|
||||
)
|
||||
);
|
||||
$this->collector->setRange($range['start'], $range['end']);
|
||||
|
||||
// add to operators manually:
|
||||
$this->operators->push(['type' => 'date_before', 'value' => $range['start']->format('Y-m-d'),]);
|
||||
$this->operators->push(['type' => 'date_after', 'value' => $range['end']->format('Y-m-d'),]);
|
||||
|
||||
$this->setExactDateParams($range);
|
||||
return false;
|
||||
case 'date_before':
|
||||
Log::debug(sprintf('Value for date_before is "%s"', $value));
|
||||
$range = $this->parseDateRange($value);
|
||||
Log::debug(
|
||||
sprintf(
|
||||
'Set "%s" using collector with value "%s" (%s - %s)', $operator, $value, $range['start']->format('Y-m-d'),
|
||||
$range['end']->format('Y-m-d')
|
||||
)
|
||||
);
|
||||
|
||||
// add to operators manually:
|
||||
$this->operators->push(['type' => 'date_before', 'value' => $range['start']->format('Y-m-d'),]);
|
||||
$this->collector->setBefore($range['start']);
|
||||
|
||||
$this->setDateBeforeParams($range);
|
||||
return false;
|
||||
case 'date_after':
|
||||
Log::debug(sprintf('Value for date_after is "%s"', $value));
|
||||
$range = $this->parseDateRange($value);
|
||||
Log::debug(
|
||||
sprintf(
|
||||
'Set "%s" using collector with value "%s" (%s - %s)', $operator, $value, $range['start']->format('Y-m-d'),
|
||||
$range['end']->format('Y-m-d')
|
||||
)
|
||||
);
|
||||
|
||||
// add to operators manually:
|
||||
$this->operators->push(['type' => 'date_after', 'value' => $range['end']->format('Y-m-d'),]);
|
||||
$this->collector->setAfter($range['end']);
|
||||
|
||||
$this->setDateAfterParams($range);
|
||||
return false;
|
||||
case 'created_on':
|
||||
Log::debug(sprintf('Set "%s" using collector with value "%s"', $operator, $value));
|
||||
@@ -660,6 +627,15 @@ class OperatorQuerySearch implements SearchInterface
|
||||
$this->collector->setUpdatedAt($updatedAt);
|
||||
break;
|
||||
//
|
||||
// external URL
|
||||
//
|
||||
case 'no_external_url':
|
||||
$this->collector->withoutExternalUrl();
|
||||
break;
|
||||
case 'any_external_url':
|
||||
$this->collector->withExternalUrl();
|
||||
break;
|
||||
//
|
||||
// other fields
|
||||
//
|
||||
case 'external_id':
|
||||
@@ -867,13 +843,12 @@ class OperatorQuerySearch implements SearchInterface
|
||||
{
|
||||
$parser = new ParseDateString;
|
||||
if ($parser->isDateRange($value)) {
|
||||
return $parser->parseRange($value, $this->date);
|
||||
return $parser->parseRange($value);
|
||||
}
|
||||
$parsedDate = $parser->parseDate($value);
|
||||
|
||||
return [
|
||||
'start' => $parsedDate,
|
||||
'end' => $parsedDate,
|
||||
'exact' => $parsedDate,
|
||||
];
|
||||
}
|
||||
|
||||
@@ -884,4 +859,119 @@ class OperatorQuerySearch implements SearchInterface
|
||||
{
|
||||
return $this->words;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $range
|
||||
*
|
||||
* @throws FireflyException
|
||||
*/
|
||||
private function setExactDateParams(array $range): void
|
||||
{
|
||||
/**
|
||||
* @var string $key
|
||||
* @var Carbon|string $value
|
||||
*/
|
||||
foreach ($range as $key => $value) {
|
||||
switch ($key) {
|
||||
default:
|
||||
throw new FireflyException(sprintf('Cannot handle key "%s" in setExactParameters()', $key));
|
||||
case 'exact':
|
||||
Log::debug(sprintf('Set date_is_exact value "%s"', $value->format('Y-m-d')));
|
||||
$this->collector->setRange($value, $value);
|
||||
$this->operators->push(['type' => 'date_is', 'value' => $value->format('Y-m-d'),]);
|
||||
break;
|
||||
case 'year':
|
||||
Log::debug(sprintf('Set date_is_exact YEAR value "%s"', $value));
|
||||
$this->collector->yearIs($value);
|
||||
$this->operators->push(['type' => 'date_is_year', 'value' => $value,]);
|
||||
break;
|
||||
case 'month':
|
||||
Log::debug(sprintf('Set date_is_exact MONTH value "%s"', $value));
|
||||
$this->collector->monthIs($value);
|
||||
$this->operators->push(['type' => 'date_is_month', 'value' => $value,]);
|
||||
break;
|
||||
case 'day':
|
||||
Log::debug(sprintf('Set date_is_exact DAY value "%s"', $value));
|
||||
$this->collector->dayIs($value);
|
||||
$this->operators->push(['type' => 'date_is_day', 'value' => $value,]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $range
|
||||
*
|
||||
* @throws FireflyException
|
||||
*/
|
||||
private function setDateBeforeParams(array $range): void
|
||||
{
|
||||
/**
|
||||
* @var string $key
|
||||
* @var Carbon|string $value
|
||||
*/
|
||||
foreach ($range as $key => $value) {
|
||||
switch ($key) {
|
||||
default:
|
||||
throw new FireflyException(sprintf('Cannot handle key "%s" in setDateBeforeParams()', $key));
|
||||
case 'exact':
|
||||
$this->collector->setBefore($value);
|
||||
$this->operators->push(['type' => 'date_before', 'value' => $value->format('Y-m-d'),]);
|
||||
break;
|
||||
case 'year':
|
||||
Log::debug(sprintf('Set date_is_before YEAR value "%s"', $value));
|
||||
$this->collector->yearBefore($value);
|
||||
$this->operators->push(['type' => 'date_before_year', 'value' => $value,]);
|
||||
break;
|
||||
case 'month':
|
||||
Log::debug(sprintf('Set date_is_before MONTH value "%s"', $value));
|
||||
$this->collector->monthBefore($value);
|
||||
$this->operators->push(['type' => 'date_before_month', 'value' => $value,]);
|
||||
break;
|
||||
case 'day':
|
||||
Log::debug(sprintf('Set date_is_before DAY value "%s"', $value));
|
||||
$this->collector->dayBefore($value);
|
||||
$this->operators->push(['type' => 'date_before_day', 'value' => $value,]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $range
|
||||
*
|
||||
* @throws FireflyException
|
||||
*/
|
||||
private function setDateAfterParams(array $range)
|
||||
{
|
||||
/**
|
||||
* @var string $key
|
||||
* @var Carbon|string $value
|
||||
*/
|
||||
foreach ($range as $key => $value) {
|
||||
switch ($key) {
|
||||
default:
|
||||
throw new FireflyException(sprintf('Cannot handle key "%s" in setDateAfterParams()', $key));
|
||||
case 'exact':
|
||||
$this->collector->setAfter($value);
|
||||
$this->operators->push(['type' => 'date_after', 'value' => $value->format('Y-m-d'),]);
|
||||
break;
|
||||
case 'year':
|
||||
Log::debug(sprintf('Set date_is_after YEAR value "%s"', $value));
|
||||
$this->collector->yearAfter($value);
|
||||
$this->operators->push(['type' => 'date_after_year', 'value' => $value,]);
|
||||
break;
|
||||
case 'month':
|
||||
Log::debug(sprintf('Set date_is_after MONTH value "%s"', $value));
|
||||
$this->collector->monthAfter($value);
|
||||
$this->operators->push(['type' => 'date_after_month', 'value' => $value,]);
|
||||
break;
|
||||
case 'day':
|
||||
Log::debug(sprintf('Set date_is_after DAY value "%s"', $value));
|
||||
$this->collector->dayAfter($value);
|
||||
$this->operators->push(['type' => 'date_after_day', 'value' => $value,]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -540,4 +540,62 @@ class Steam
|
||||
return $amount;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $string
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function filterSpaces(string $string): string
|
||||
{
|
||||
$search = [
|
||||
"\u{0001}", // start of heading
|
||||
"\u{0002}", // start of text
|
||||
"\u{0003}", // end of text
|
||||
"\u{0004}", // end of transmission
|
||||
"\u{0005}", // enquiry
|
||||
"\u{0006}", // ACK
|
||||
"\u{0007}", // BEL
|
||||
"\u{0008}", // backspace
|
||||
"\u{000E}", // shift out
|
||||
"\u{000F}", // shift in
|
||||
"\u{0010}", // data link escape
|
||||
"\u{0011}", // DC1
|
||||
"\u{0012}", // DC2
|
||||
"\u{0013}", // DC3
|
||||
"\u{0014}", // DC4
|
||||
"\u{0015}", // NAK
|
||||
"\u{0016}", // SYN
|
||||
"\u{0017}", // ETB
|
||||
"\u{0018}", // CAN
|
||||
"\u{0019}", // EM
|
||||
"\u{001A}", // SUB
|
||||
"\u{001B}", // escape
|
||||
"\u{001C}", // file separator
|
||||
"\u{001D}", // group separator
|
||||
"\u{001E}", // record separator
|
||||
"\u{001F}", // unit separator
|
||||
"\u{007F}", // DEL
|
||||
"\u{00A0}", // non-breaking space
|
||||
"\u{1680}", // ogham space mark
|
||||
"\u{180E}", // mongolian vowel separator
|
||||
"\u{2000}", // en quad
|
||||
"\u{2001}", // em quad
|
||||
"\u{2002}", // en space
|
||||
"\u{2003}", // em space
|
||||
"\u{2004}", // three-per-em space
|
||||
"\u{2005}", // four-per-em space
|
||||
"\u{2006}", // six-per-em space
|
||||
"\u{2007}", // figure space
|
||||
"\u{2008}", // punctuation space
|
||||
"\u{2009}", // thin space
|
||||
"\u{200A}", // hair space
|
||||
"\u{200B}", // zero width space
|
||||
"\u{202F}", // narrow no-break space
|
||||
"\u{3000}", // ideographic space
|
||||
"\u{FEFF}", // zero width no -break space
|
||||
"\x20", // plain old normal space
|
||||
];
|
||||
|
||||
return str_replace($search, '', $string);
|
||||
}
|
||||
}
|
||||
|
@@ -26,7 +26,12 @@ namespace FireflyIII\Support\System;
|
||||
|
||||
use Artisan;
|
||||
use Crypt;
|
||||
use FireflyIII\Exceptions\FireflyException;
|
||||
use Illuminate\Contracts\Encryption\DecryptException;
|
||||
use Laravel\Passport\Console\KeysCommand;
|
||||
use Log;
|
||||
use Psr\Container\ContainerExceptionInterface;
|
||||
use Psr\Container\NotFoundExceptionInterface;
|
||||
|
||||
/**
|
||||
* Class OAuthKeys
|
||||
@@ -62,7 +67,23 @@ class OAuthKeys
|
||||
*/
|
||||
public static function keysInDatabase(): bool
|
||||
{
|
||||
return app('fireflyconfig')->has(self::PRIVATE_KEY) && app('fireflyconfig')->has(self::PUBLIC_KEY);
|
||||
$privateKey = '';
|
||||
$publicKey = '';
|
||||
// better check if keys are in the database:
|
||||
if (app('fireflyconfig')->has(self::PRIVATE_KEY) && app('fireflyconfig')->has(self::PUBLIC_KEY)) {
|
||||
try {
|
||||
$privateKey = (string)app('fireflyconfig')->get(self::PRIVATE_KEY)?->data;
|
||||
$publicKey = (string)app('fireflyconfig')->get(self::PUBLIC_KEY)?->data;
|
||||
} catch (ContainerExceptionInterface | NotFoundExceptionInterface | FireflyException $e) {
|
||||
Log::error(sprintf('Could not validate keysInDatabase(): %s', $e->getMessage()));
|
||||
Log::error($e->getTraceAsString());
|
||||
}
|
||||
}
|
||||
if ('' !== $privateKey && '' !== $publicKey) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -97,16 +118,30 @@ class OAuthKeys
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function restoreKeysFromDB(): void
|
||||
public static function restoreKeysFromDB(): bool
|
||||
{
|
||||
$privateContent = Crypt::decrypt(app('fireflyconfig')->get(self::PRIVATE_KEY)->data);
|
||||
$publicContent = Crypt::decrypt(app('fireflyconfig')->get(self::PUBLIC_KEY)->data);
|
||||
$privateKey = (string)app('fireflyconfig')->get(self::PRIVATE_KEY)?->data;
|
||||
$publicKey = (string)app('fireflyconfig')->get(self::PUBLIC_KEY)?->data;
|
||||
try {
|
||||
$privateContent = Crypt::decrypt($privateKey);
|
||||
$publicContent = Crypt::decrypt($publicKey);
|
||||
} catch(DecryptException $e) {
|
||||
Log::error('Could not decrypt pub/private keypair.');
|
||||
Log::error($e->getMessage());
|
||||
|
||||
// delete config vars from DB:
|
||||
app('fireflyconfig')->delete(self::PRIVATE_KEY);
|
||||
app('fireflyconfig')->delete(self::PUBLIC_KEY);
|
||||
|
||||
return false;
|
||||
}
|
||||
$private = storage_path('oauth-private.key');
|
||||
$public = storage_path('oauth-public.key');
|
||||
file_put_contents($private, $privateContent);
|
||||
file_put_contents($public, $publicContent);
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -58,7 +58,7 @@ class TransactionGroupTransformer extends AbstractTransformer
|
||||
$this->metaFields = [
|
||||
'sepa_cc', 'sepa_ct_op', 'sepa_ct_id', 'sepa_db', 'sepa_country', 'sepa_ep',
|
||||
'sepa_ci', 'sepa_batch_id', 'internal_reference', 'bunq_payment_id', 'import_hash_v2',
|
||||
'recurrence_id', 'external_id', 'original_source', 'external_uri',
|
||||
'recurrence_id', 'external_id', 'original_source', 'external_url',
|
||||
'recurrence_count', 'recurrence_total',
|
||||
];
|
||||
$this->metaDateFields = ['interest_date', 'book_date', 'process_date', 'due_date', 'payment_date', 'invoice_date'];
|
||||
@@ -189,7 +189,7 @@ class TransactionGroupTransformer extends AbstractTransformer
|
||||
'recurrence_total' => $this->integerFromArray($metaFieldData->getArrayCopy(), 'recurrence_total'),
|
||||
'recurrence_count' => $this->integerFromArray($metaFieldData->getArrayCopy(), 'recurrence_count'),
|
||||
'bunq_payment_id' => $metaFieldData['bunq_payment_id'],
|
||||
'external_uri' => $metaFieldData['external_uri'],
|
||||
'external_url' => $metaFieldData['external_url'],
|
||||
'import_hash_v2' => $metaFieldData['import_hash_v2'],
|
||||
|
||||
'sepa_cc' => $metaFieldData['sepa_cc'],
|
||||
@@ -212,6 +212,8 @@ class TransactionGroupTransformer extends AbstractTransformer
|
||||
'longitude' => $longitude,
|
||||
'latitude' => $latitude,
|
||||
'zoom_level' => $zoomLevel,
|
||||
|
||||
'has_attachments' => $this->hasAttachments((int)$row['transaction_journal_id']),
|
||||
];
|
||||
}
|
||||
|
||||
@@ -248,6 +250,16 @@ class TransactionGroupTransformer extends AbstractTransformer
|
||||
return $this->groupRepos->getLocation($journalId);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $journalId
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private function hasAttachments(int $journalId): bool
|
||||
{
|
||||
return $this->groupRepos->countAttachments($journalId) > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $array
|
||||
* @param string $key
|
||||
|
@@ -41,23 +41,24 @@ trait DepositValidation
|
||||
|
||||
/**
|
||||
* @param array $validTypes
|
||||
* @param int $accountId
|
||||
* @param string $accountName
|
||||
* @param array $data
|
||||
*
|
||||
* @return Account|null
|
||||
*/
|
||||
abstract protected function findExistingAccount(array $validTypes, int $accountId, string $accountName): ?Account;
|
||||
abstract protected function findExistingAccount(array $validTypes, array $data): ?Account;
|
||||
|
||||
/**
|
||||
* @param int|null $accountId
|
||||
* @param mixed $accountName
|
||||
* @param array $array
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function validateDepositDestination(?int $accountId, $accountName): bool
|
||||
protected function validateDepositDestination(array $array): bool
|
||||
{
|
||||
$result = null;
|
||||
Log::debug(sprintf('Now in validateDepositDestination(%d, "%s")', $accountId, $accountName));
|
||||
$result = null;
|
||||
$accountId = array_key_exists('id', $array) ? $array['id'] : null;
|
||||
$accountName = array_key_exists('name', $array) ? $array['name'] : null;
|
||||
|
||||
Log::debug('Now in validateDepositDestination', $array);
|
||||
|
||||
// source can be any of the following types.
|
||||
$validTypes = $this->combinations[$this->transactionType][$this->source->accountType->type] ?? [];
|
||||
@@ -76,7 +77,7 @@ trait DepositValidation
|
||||
|
||||
if (null === $result) {
|
||||
// otherwise try to find the account:
|
||||
$search = $this->findExistingAccount($validTypes, (int)$accountId, (string)$accountName);
|
||||
$search = $this->findExistingAccount($validTypes, $array);
|
||||
if (null === $search) {
|
||||
Log::debug('findExistingAccount() returned NULL, so the result is false.');
|
||||
$this->destError = (string)trans('validation.deposit_dest_bad_data', ['id' => $accountId, 'name' => $accountName]);
|
||||
@@ -89,20 +90,23 @@ trait DepositValidation
|
||||
}
|
||||
}
|
||||
$result = $result ?? false;
|
||||
Log::debug(sprintf('validateDepositDestination(%d, "%s") will return %s', $accountId, $accountName, var_export($result, true)));
|
||||
Log::debug(sprintf('validateDepositDestination will return %s', var_export($result, true)));
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int|null $accountId
|
||||
* @param string|null $accountName
|
||||
* @param array $array
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function validateDepositSource(?int $accountId, ?string $accountName): bool
|
||||
protected function validateDepositSource(array $array): bool
|
||||
{
|
||||
Log::debug(sprintf('Now in validateDepositSource(%d, "%s")', $accountId, $accountName));
|
||||
$accountId = array_key_exists('id', $array) ? $array['id'] : null;
|
||||
$accountName = array_key_exists('name', $array) ? $array['name'] : null;
|
||||
$accountIban = array_key_exists('iban', $array) ? $array['iban'] : null;
|
||||
$accountNumber = array_key_exists('number', $array) ? $array['number'] : null;
|
||||
Log::debug('Now in validateDepositSource', $array);
|
||||
$result = null;
|
||||
// source can be any of the following types.
|
||||
$validTypes = array_keys($this->combinations[$this->transactionType]);
|
||||
@@ -114,12 +118,32 @@ trait DepositValidation
|
||||
$result = false;
|
||||
}
|
||||
|
||||
// if the user submits an ID only but that ID is not of the correct type,
|
||||
// if the user submits an ID, but that ID is not of the correct type,
|
||||
// return false.
|
||||
if (null !== $accountId && null === $accountName) {
|
||||
if (null !== $accountId) {
|
||||
$search = $this->accountRepository->find($accountId);
|
||||
if (null !== $search && !in_array($search->accountType->type, $validTypes, true)) {
|
||||
Log::debug(sprintf('User submitted only an ID (#%d), which is a "%s", so this is not a valid source.', $accountId, $search->accountType->type));
|
||||
Log::debug(sprintf('User submitted an ID (#%d), which is a "%s", so this is not a valid source.', $accountId, $search->accountType->type));
|
||||
$result = false;
|
||||
}
|
||||
}
|
||||
|
||||
// if user submits an IBAN:
|
||||
if (null !== $accountIban) {
|
||||
$search = $this->accountRepository->findByIbanNull($accountIban, $validTypes);
|
||||
if (null !== $search && !in_array($search->accountType->type, $validTypes, true)) {
|
||||
Log::debug(sprintf('User submitted IBAN ("%s"), which is a "%s", so this is not a valid source.', $accountIban, $search->accountType->type));
|
||||
$result = false;
|
||||
}
|
||||
}
|
||||
|
||||
// if user submits a number:
|
||||
if (null !== $accountNumber) {
|
||||
$search = $this->accountRepository->findByAccountNumber($accountNumber, $validTypes);
|
||||
if (null !== $search && !in_array($search->accountType->type, $validTypes, true)) {
|
||||
Log::debug(
|
||||
sprintf('User submitted number ("%s"), which is a "%s", so this is not a valid source.', $accountNumber, $search->accountType->type)
|
||||
);
|
||||
$result = false;
|
||||
}
|
||||
}
|
||||
|
@@ -36,14 +36,15 @@ trait LiabilityValidation
|
||||
{
|
||||
|
||||
/**
|
||||
* @param int|null $accountId
|
||||
* @param array $array
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function validateLCDestination(?int $accountId): bool
|
||||
protected function validateLCDestination(array $array): bool
|
||||
{
|
||||
Log::debug(sprintf('Now in validateLCDestination(%d)', $accountId));
|
||||
Log::debug('Now in validateLCDestination', $array);
|
||||
$result = null;
|
||||
$accountId = array_key_exists('id', $array) ? $array['id'] : null;
|
||||
$validTypes = config('firefly.valid_liabilities');
|
||||
|
||||
if (null === $accountId) {
|
||||
@@ -74,14 +75,15 @@ trait LiabilityValidation
|
||||
/**
|
||||
* Source of an liability credit must be a liability.
|
||||
*
|
||||
* @param string|null $accountName
|
||||
* @param array $array
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function validateLCSource(?string $accountName): bool
|
||||
protected function validateLCSource(array $array): bool
|
||||
{
|
||||
$accountName = array_key_exists('name', $array) ? $array['name'] : null;
|
||||
$result = true;
|
||||
Log::debug(sprintf('Now in validateLCDestination("%s")', $accountName));
|
||||
Log::debug('Now in validateLCDestination', $array);
|
||||
if ('' === $accountName || null === $accountName) {
|
||||
$result = false;
|
||||
}
|
||||
|
@@ -41,15 +41,16 @@ trait OBValidation
|
||||
abstract protected function canCreateTypes(array $accountTypes): bool;
|
||||
|
||||
/**
|
||||
* @param int|null $accountId
|
||||
* @param mixed $accountName
|
||||
* @param array $array
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function validateOBDestination(?int $accountId, $accountName): bool
|
||||
protected function validateOBDestination(array $array): bool
|
||||
{
|
||||
$result = null;
|
||||
Log::debug(sprintf('Now in validateOBDestination(%d, "%s")', $accountId, $accountName));
|
||||
$result = null;
|
||||
$accountId = array_key_exists('id', $array) ? $array['id'] : null;
|
||||
$accountName = array_key_exists('name', $array) ? $array['name'] : null;
|
||||
Log::debug('Now in validateOBDestination', $array);
|
||||
|
||||
// source can be any of the following types.
|
||||
$validTypes = $this->combinations[$this->transactionType][$this->source->accountType->type] ?? [];
|
||||
@@ -68,7 +69,7 @@ trait OBValidation
|
||||
|
||||
if (null === $result) {
|
||||
// otherwise try to find the account:
|
||||
$search = $this->findExistingAccount($validTypes, (int)$accountId, (string)$accountName);
|
||||
$search = $this->findExistingAccount($validTypes, $array);
|
||||
if (null === $search) {
|
||||
Log::debug('findExistingAccount() returned NULL, so the result is false.', $validTypes);
|
||||
$this->destError = (string)trans('validation.ob_dest_bad_data', ['id' => $accountId, 'name' => $accountName]);
|
||||
@@ -90,15 +91,15 @@ trait OBValidation
|
||||
* Source of an opening balance can either be an asset account
|
||||
* or an "initial balance account". The latter can be created.
|
||||
*
|
||||
* @param int|null $accountId
|
||||
* @param string|null $accountName
|
||||
* @param array $array
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function validateOBSource(?int $accountId, ?string $accountName): bool
|
||||
protected function validateOBSource(array $array): bool
|
||||
{
|
||||
Log::debug(sprintf('Now in validateOBSource(%d, "%s")', $accountId, $accountName));
|
||||
Log::debug(sprintf('The account name is null: %s', var_export(null === $accountName, true)));
|
||||
$accountId = array_key_exists('id', $array) ? $array['id'] : null;
|
||||
$accountName = array_key_exists('name', $array) ? $array['name'] : null;
|
||||
Log::debug('Now in validateOBSource', $array);
|
||||
$result = null;
|
||||
// source can be any of the following types.
|
||||
$validTypes = array_keys($this->combinations[$this->transactionType]);
|
||||
|
@@ -24,6 +24,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\Validation\Account;
|
||||
|
||||
use FireflyIII\Models\Account;
|
||||
use FireflyIII\Models\AccountType;
|
||||
use Log;
|
||||
|
||||
@@ -32,15 +33,17 @@ use Log;
|
||||
*/
|
||||
trait ReconciliationValidation
|
||||
{
|
||||
|
||||
public ?Account $destination;
|
||||
public ?Account $source;
|
||||
/**
|
||||
* @param int|null $accountId
|
||||
* @param array $array
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function validateReconciliationDestination(?int $accountId): bool
|
||||
protected function validateReconciliationDestination(array $array): bool
|
||||
{
|
||||
Log::debug('Now in validateReconciliationDestination');
|
||||
$accountId = array_key_exists('id', $array) ? $array['id'] : null;
|
||||
Log::debug('Now in validateReconciliationDestination', $array);
|
||||
if (null === $accountId) {
|
||||
Log::debug('Return FALSE');
|
||||
|
||||
@@ -80,13 +83,14 @@ trait ReconciliationValidation
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int|null $accountId
|
||||
* @param array $array
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function validateReconciliationSource(?int $accountId): bool
|
||||
protected function validateReconciliationSource(array $array): bool
|
||||
{
|
||||
Log::debug('In validateReconciliationSource');
|
||||
$accountId = array_key_exists('id', $array) ? $array['id'] : null;
|
||||
Log::debug('In validateReconciliationSource', $array);
|
||||
if (null === $accountId) {
|
||||
Log::debug('Return FALSE');
|
||||
|
||||
|
@@ -40,22 +40,22 @@ trait TransferValidation
|
||||
|
||||
/**
|
||||
* @param array $validTypes
|
||||
* @param int $accountId
|
||||
* @param string $accountName
|
||||
* @param array $data
|
||||
*
|
||||
* @return Account|null
|
||||
*/
|
||||
abstract protected function findExistingAccount(array $validTypes, int $accountId, string $accountName): ?Account;
|
||||
abstract protected function findExistingAccount(array $validTypes, array $data): ?Account;
|
||||
|
||||
/**
|
||||
* @param int|null $accountId
|
||||
* @param mixed $accountName
|
||||
* @param array $array
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function validateTransferDestination(?int $accountId, $accountName): bool
|
||||
protected function validateTransferDestination(array $array): bool
|
||||
{
|
||||
Log::debug(sprintf('Now in validateTransferDestination(%d, "%s")', $accountId, $accountName));
|
||||
$accountId = array_key_exists('id', $array) ? $array['id'] : null;
|
||||
$accountName = array_key_exists('name', $array) ? $array['name'] : null;
|
||||
Log::debug('Now in validateTransferDestination', $array);
|
||||
// source can be any of the following types.
|
||||
$validTypes = $this->combinations[$this->transactionType][$this->source->accountType->type] ?? [];
|
||||
if (null === $accountId && null === $accountName && false === $this->canCreateTypes($validTypes)) {
|
||||
@@ -68,7 +68,7 @@ trait TransferValidation
|
||||
}
|
||||
|
||||
// or try to find the account:
|
||||
$search = $this->findExistingAccount($validTypes, (int)$accountId, (string)$accountName);
|
||||
$search = $this->findExistingAccount($validTypes,$array);
|
||||
if (null === $search) {
|
||||
$this->destError = (string)trans('validation.transfer_dest_bad_data', ['id' => $accountId, 'name' => $accountName]);
|
||||
|
||||
@@ -88,14 +88,15 @@ trait TransferValidation
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int|null $accountId
|
||||
* @param string|null $accountName
|
||||
* @param array $array
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function validateTransferSource(?int $accountId, ?string $accountName): bool
|
||||
protected function validateTransferSource(array $array): bool
|
||||
{
|
||||
Log::debug(sprintf('Now in validateTransferSource(%d, "%s")', $accountId, $accountName));
|
||||
$accountId = array_key_exists('id', $array) ? $array['id'] : null;
|
||||
$accountName = array_key_exists('name', $array) ? $array['name'] : null;
|
||||
Log::debug('Now in validateTransferSource', $array);
|
||||
// source can be any of the following types.
|
||||
$validTypes = array_keys($this->combinations[$this->transactionType]);
|
||||
if (null === $accountId && null === $accountName && false === $this->canCreateTypes($validTypes)) {
|
||||
@@ -108,7 +109,7 @@ trait TransferValidation
|
||||
}
|
||||
|
||||
// otherwise try to find the account:
|
||||
$search = $this->findExistingAccount($validTypes, (int)$accountId, (string)$accountName);
|
||||
$search = $this->findExistingAccount($validTypes, $array);
|
||||
if (null === $search) {
|
||||
$this->sourceError = (string)trans('validation.transfer_source_bad_data', ['id' => $accountId, 'name' => $accountName]);
|
||||
Log::warning('Not a valid source, cant find it.', $validTypes);
|
||||
|
@@ -41,35 +41,35 @@ trait WithdrawalValidation
|
||||
|
||||
/**
|
||||
* @param array $validTypes
|
||||
* @param int $accountId
|
||||
* @param string $accountName
|
||||
* @param array $data
|
||||
*
|
||||
* @return Account|null
|
||||
*/
|
||||
abstract protected function findExistingAccount(array $validTypes, int $accountId, string $accountName): ?Account;
|
||||
abstract protected function findExistingAccount(array $validTypes, array $data): ?Account;
|
||||
|
||||
/**
|
||||
* @param int|null $accountId
|
||||
* @param string|null $accountName
|
||||
* @param array $array
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function validateGenericSource(?int $accountId, ?string $accountName): bool
|
||||
protected function validateGenericSource(array $array): bool
|
||||
{
|
||||
Log::debug(sprintf('Now in validateGenericSource(%d, "%s")', $accountId, $accountName));
|
||||
$accountId = array_key_exists('id', $array) ? $array['id'] : null;
|
||||
$accountName = array_key_exists('name', $array) ? $array['name'] : null;
|
||||
Log::debug('Now in validateGenericSource', $array);
|
||||
// source can be any of the following types.
|
||||
$validTypes = [AccountType::ASSET, AccountType::REVENUE, AccountType::LOAN, AccountType::DEBT, AccountType::MORTGAGE];
|
||||
if (null === $accountId && null === $accountName && false === $this->canCreateTypes($validTypes)) {
|
||||
// if both values are NULL we return TRUE
|
||||
// because we assume the user doesnt want to submit / change anything.
|
||||
$this->sourceError = (string)trans('validation.withdrawal_source_need_data');
|
||||
Log::warning('Not a valid source. Need more data.');
|
||||
Log::warning('[a] Not a valid source. Need more data.');
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// otherwise try to find the account:
|
||||
$search = $this->findExistingAccount($validTypes, (int)$accountId, (string)$accountName);
|
||||
$search = $this->findExistingAccount($validTypes, $array);
|
||||
if (null === $search) {
|
||||
$this->sourceError = (string)trans('validation.withdrawal_source_bad_data', ['id' => $accountId, 'name' => $accountName]);
|
||||
Log::warning('Not a valid source. Cant find it.', $validTypes);
|
||||
@@ -83,14 +83,15 @@ trait WithdrawalValidation
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int|null $accountId
|
||||
* @param string|null $accountName
|
||||
* @param array $array
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function validateWithdrawalDestination(?int $accountId, ?string $accountName): bool
|
||||
protected function validateWithdrawalDestination(array $array): bool
|
||||
{
|
||||
Log::debug(sprintf('Now in validateWithdrawalDestination(%d, "%s")', $accountId, $accountName));
|
||||
$accountId = array_key_exists('id', $array) ? $array['id'] : null;
|
||||
$accountName = array_key_exists('name', $array) ? $array['name'] : null;
|
||||
Log::debug('Now in validateWithdrawalDestination()', $array);
|
||||
// source can be any of the following types.
|
||||
$validTypes = $this->combinations[$this->transactionType][$this->source->accountType->type] ?? [];
|
||||
if (null === $accountId && null === $accountName && false === $this->canCreateTypes($validTypes)) {
|
||||
@@ -120,27 +121,29 @@ trait WithdrawalValidation
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int|null $accountId
|
||||
* @param string|null $accountName
|
||||
* @param array $array
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function validateWithdrawalSource(?int $accountId, ?string $accountName): bool
|
||||
protected function validateWithdrawalSource(array $array): bool
|
||||
{
|
||||
Log::debug(sprintf('Now in validateWithdrawalSource(%d, "%s")', $accountId, $accountName));
|
||||
$accountId = array_key_exists('id', $array) ? $array['id'] : null;
|
||||
$accountName = array_key_exists('name', $array) ? $array['name'] : null;
|
||||
|
||||
Log::debug('Now in validateWithdrawalSource', $array);
|
||||
// source can be any of the following types.
|
||||
$validTypes = array_keys($this->combinations[$this->transactionType]);
|
||||
if (null === $accountId && null === $accountName && false === $this->canCreateTypes($validTypes)) {
|
||||
// if both values are NULL we return false,
|
||||
// because the source of a withdrawal can't be created.
|
||||
$this->sourceError = (string)trans('validation.withdrawal_source_need_data');
|
||||
Log::warning('Not a valid source. Need more data.');
|
||||
Log::warning('[b] Not a valid source. Need more data.');
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// otherwise try to find the account:
|
||||
$search = $this->findExistingAccount($validTypes, (int)$accountId, (string)$accountName);
|
||||
$search = $this->findExistingAccount($validTypes, $array);
|
||||
if (null === $search) {
|
||||
$this->sourceError = (string)trans('validation.withdrawal_source_bad_data', ['id' => $accountId, 'name' => $accountName]);
|
||||
Log::warning('Not a valid source. Cant find it.', $validTypes);
|
||||
|
@@ -95,15 +95,13 @@ class AccountValidator
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int|null $accountId
|
||||
* @param string|null $accountName
|
||||
* @param string|null $accountIban
|
||||
* @param array $array
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function validateDestination(?int $accountId, ?string $accountName, ?string $accountIban): bool
|
||||
public function validateDestination(array $array): bool
|
||||
{
|
||||
Log::debug(sprintf('Now in AccountValidator::validateDestination(%d, "%s", "%s")', $accountId, $accountName, $accountIban));
|
||||
Log::debug('Now in AccountValidator::validateDestination()', $array);
|
||||
if (null === $this->source) {
|
||||
Log::error('Source is NULL, always FALSE.');
|
||||
$this->destError = 'No source account validation has taken place yet. Please do this first or overrule the object.';
|
||||
@@ -119,22 +117,22 @@ class AccountValidator
|
||||
break;
|
||||
|
||||
case TransactionType::WITHDRAWAL:
|
||||
$result = $this->validateWithdrawalDestination($accountId, $accountName);
|
||||
$result = $this->validateWithdrawalDestination($array);
|
||||
break;
|
||||
case TransactionType::DEPOSIT:
|
||||
$result = $this->validateDepositDestination($accountId, $accountName);
|
||||
$result = $this->validateDepositDestination($array);
|
||||
break;
|
||||
case TransactionType::TRANSFER:
|
||||
$result = $this->validateTransferDestination($accountId, $accountName);
|
||||
$result = $this->validateTransferDestination($array);
|
||||
break;
|
||||
case TransactionType::OPENING_BALANCE:
|
||||
$result = $this->validateOBDestination($accountId, $accountName);
|
||||
$result = $this->validateOBDestination($array);
|
||||
break;
|
||||
case TransactionType::LIABILITY_CREDIT:
|
||||
$result = $this->validateLCDestination($accountId);
|
||||
$result = $this->validateLCDestination($array);
|
||||
break;
|
||||
case TransactionType::RECONCILIATION:
|
||||
$result = $this->validateReconciliationDestination($accountId);
|
||||
$result = $this->validateReconciliationDestination($array);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -142,39 +140,37 @@ class AccountValidator
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int|null $accountId
|
||||
* @param string|null $accountName
|
||||
* @param string|null $accountIban
|
||||
* @param array $array
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function validateSource(?int $accountId, ?string $accountName, ?string $accountIban): bool
|
||||
public function validateSource(array $array): bool
|
||||
{
|
||||
Log::debug(sprintf('Now in AccountValidator::validateSource(%d, "%s", "%s")', $accountId, $accountName, $accountIban));
|
||||
Log::debug('Now in AccountValidator::validateSource()', $array);
|
||||
switch ($this->transactionType) {
|
||||
default:
|
||||
Log::error(sprintf('AccountValidator::validateSource cannot handle "%s", so it will do a generic check.', $this->transactionType));
|
||||
$result = $this->validateGenericSource($accountId, $accountName);
|
||||
$result = $this->validateGenericSource($array);
|
||||
break;
|
||||
case TransactionType::WITHDRAWAL:
|
||||
$result = $this->validateWithdrawalSource($accountId, $accountName);
|
||||
$result = $this->validateWithdrawalSource($array);
|
||||
break;
|
||||
case TransactionType::DEPOSIT:
|
||||
$result = $this->validateDepositSource($accountId, $accountName);
|
||||
$result = $this->validateDepositSource($array);
|
||||
break;
|
||||
case TransactionType::TRANSFER:
|
||||
$result = $this->validateTransferSource($accountId, $accountName);
|
||||
$result = $this->validateTransferSource($array);
|
||||
break;
|
||||
case TransactionType::OPENING_BALANCE:
|
||||
$result = $this->validateOBSource($accountId, $accountName);
|
||||
$result = $this->validateOBSource($array);
|
||||
break;
|
||||
case TransactionType::LIABILITY_CREDIT:
|
||||
$result = $this->validateLCSource($accountName);
|
||||
$result = $this->validateLCSource($array);
|
||||
break;
|
||||
|
||||
case TransactionType::RECONCILIATION:
|
||||
Log::debug('Calling validateReconciliationSource');
|
||||
$result = $this->validateReconciliationSource($accountId);
|
||||
$result = $this->validateReconciliationSource($array);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -203,24 +199,45 @@ class AccountValidator
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $validTypes
|
||||
* @param int $accountId
|
||||
* @param string $accountName
|
||||
* @param array $validTypes
|
||||
* @param array $data
|
||||
*
|
||||
* @return Account|null
|
||||
*/
|
||||
protected function findExistingAccount(array $validTypes, int $accountId, string $accountName): ?Account
|
||||
protected function findExistingAccount(array $validTypes, array $data): ?Account
|
||||
{
|
||||
Log::debug('Now in findExistingAccount', $data);
|
||||
$accountId = array_key_exists('id', $data) ? $data['id'] : null;
|
||||
$accountIban = array_key_exists('iban', $data) ? $data['iban'] : null;
|
||||
$accountNumber = array_key_exists('number', $data) ? $data['number'] : null;
|
||||
$accountName = array_key_exists('name', $data) ? $data['name'] : null;
|
||||
|
||||
// find by ID
|
||||
if ($accountId > 0) {
|
||||
if (null !== $accountId && $accountId > 0) {
|
||||
$first = $this->accountRepository->find($accountId);
|
||||
if ((null !== $first) && in_array($first->accountType->type, $validTypes, true)) {
|
||||
return $first;
|
||||
}
|
||||
}
|
||||
|
||||
// find by iban
|
||||
if (null !== $accountIban && '' !== (string) $accountIban) {
|
||||
$first = $this->accountRepository->findByIbanNull($accountIban, $validTypes);
|
||||
if ((null !== $first) && in_array($first->accountType->type, $validTypes, true)) {
|
||||
return $first;
|
||||
}
|
||||
}
|
||||
|
||||
// find by number
|
||||
if (null !== $accountNumber && '' !== (string) $accountNumber) {
|
||||
$first = $this->accountRepository->findByAccountNumber($accountNumber, $validTypes);
|
||||
if ((null !== $first) && in_array($first->accountType->type, $validTypes, true)) {
|
||||
return $first;
|
||||
}
|
||||
}
|
||||
|
||||
// find by name:
|
||||
if ('' !== $accountName) {
|
||||
if ('' !== (string) $accountName) {
|
||||
return $this->accountRepository->findByName($accountName, $validTypes);
|
||||
}
|
||||
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user