mirror of
https://github.com/firefly-iii/firefly-iii.git
synced 2025-10-22 03:53:37 +00:00
Compare commits
214 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
6044cffef3 | ||
|
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 | ||
|
7afe9fac0a | ||
|
e6a2c10a19 | ||
|
cc9867bcc4 | ||
|
ba7ae19533 | ||
|
47fa9e3956 | ||
|
0b739b0e3f | ||
|
221e912645 | ||
|
81f4e92231 | ||
|
4fef85d5d0 | ||
|
04a6b09861 | ||
|
e31842d768 | ||
|
e99efae839 | ||
|
36f91aa11b | ||
|
140658c1a3 | ||
|
38be52d12f | ||
|
a7fd8069c2 | ||
|
2df57bf684 | ||
|
4a6cac96f6 | ||
|
0e6eb89cea | ||
|
0c883259d9 | ||
|
13bd98b842 | ||
|
c641046aa5 | ||
|
05bddbb40e | ||
|
4de6d10e27 | ||
|
253ed7b2bd | ||
|
0d38741de7 | ||
|
b5cef50dc7 | ||
|
16ee3df31f | ||
|
341478db73 | ||
|
ef1bf7c3e6 | ||
|
ee5b64904d | ||
|
0ff3b95f89 | ||
|
b70c904ec3 | ||
|
3e3465fb56 | ||
|
9e37a107ee | ||
|
4de2512b34 | ||
|
90d4545a1f | ||
|
8561f3e177 | ||
|
51b5b3a846 | ||
|
1401282aa0 | ||
|
8b81526e54 | ||
|
a16b6cb2b8 | ||
|
7876030737 | ||
|
402d5748e4 | ||
|
280975b81a | ||
|
6f8778d87f | ||
|
fbf7578fb0 | ||
|
7dd560a2e3 | ||
|
0b2e1bc8e2 | ||
|
baea455fde | ||
|
eb303157ca | ||
|
733b0d183f | ||
|
66233b7b00 | ||
|
46a820fda5 | ||
|
0244989b94 | ||
|
990c369cab | ||
|
af501952c2 | ||
|
d7ba71b1c0 | ||
|
b8df070d93 | ||
|
c55c4e0e89 | ||
|
4249336320 | ||
|
903b9bfd08 | ||
|
e32a4a84a8 | ||
|
b42d8d1e30 | ||
|
e9e9a359c8 | ||
|
b3f424fa28 | ||
|
ba740322f4 | ||
|
898a517ae9 | ||
|
c2c8c42ef3 | ||
|
4d9c4a415d | ||
|
f4fe930430 | ||
|
04dfeaf488 | ||
|
797064a119 | ||
|
4d8d9cb87d | ||
|
d1ae2cffcb | ||
|
102412cdb9 | ||
|
f9ef5868cc | ||
|
d9b2112b19 | ||
|
5edcd97c78 | ||
|
9227a3039e | ||
|
f97521c118 | ||
|
119870fe9a | ||
|
f4942e32ed | ||
|
0ef5c10749 | ||
|
d37fc311db | ||
|
7b05b78cf7 | ||
|
576a429426 | ||
|
47ef0c3a0b | ||
|
66303f614b | ||
|
98358f7578 | ||
|
8486b846f2 | ||
|
6cb35137cb | ||
|
f6182412a0 | ||
|
a67c87101d | ||
|
84b32514df |
17
.env.example
17
.env.example
@@ -177,6 +177,12 @@ MAP_DEFAULT_ZOOM=6
|
||||
# https://docs.firefly-iii.org/advanced-installation/authentication
|
||||
AUTHENTICATION_GUARD=web
|
||||
|
||||
#
|
||||
# Your LDAP server may speak a dialect. You can choose between 'OpenLDAP' and 'ActiveDirectory'
|
||||
# Anything else defaults to 'ActiveDirectory'
|
||||
#
|
||||
LDAP_DIALECT=OpenLDAP
|
||||
|
||||
#
|
||||
# LDAP connection settings:
|
||||
#
|
||||
@@ -193,14 +199,13 @@ LDAP_PASSWORD=super_secret
|
||||
LDAP_AUTH_FIELD=uid
|
||||
|
||||
#
|
||||
# If you wish to only authenticate users from a specific group, use the
|
||||
# group filter. Leave empty or remove if not in use.
|
||||
# If you wish to only authenticate users from a specific group, use the base DN above.
|
||||
#
|
||||
# Example: cn=Administrators,dc=local,dc=com
|
||||
# If you require extra/special filters please use the LDAP_EXTRA_FILTER with a valid DN.
|
||||
#
|
||||
# The group filter will only be applied after the user is authenticated.
|
||||
# The extra filter will only be applied after the user is authenticated.
|
||||
#
|
||||
LDAP_GROUP_FILTER=
|
||||
LDAP_EXTRA_FILTER=
|
||||
|
||||
#
|
||||
# Remote user guard settings
|
||||
@@ -297,6 +302,8 @@ FIREFLY_III_LAYOUT=v1
|
||||
# It won't work. It doesn't do ANYTHING. Don't believe the lies you read online. I'm not joking.
|
||||
# This configuration value WILL NOT HELP.
|
||||
#
|
||||
# Notable exception to this rule is Synology, which, according to some users, will use APP_URL to rewrite stuff.
|
||||
#
|
||||
# This variable is ONLY used in some of the emails Firefly III sends around. Nowhere else.
|
||||
# So when configuring anything WEB related this variable doesn't do anything. Nothing
|
||||
#
|
||||
|
7
.github/.mergify.yml
vendored
7
.github/.mergify.yml
vendored
@@ -1,7 +0,0 @@
|
||||
pull_request_rules:
|
||||
- name: PR on main is never approved.
|
||||
conditions:
|
||||
- base=main
|
||||
actions:
|
||||
close:
|
||||
message: Please reopen this PR on the `develop` branch. Thank you.
|
13
.github/mergify.yml
vendored
Normal file
13
.github/mergify.yml
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
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
|
||||
actions:
|
||||
close:
|
||||
message: Please do not open PR's on the `main` branch, but on the `develop` branch only. Thank you!
|
4
.github/stale.yml
vendored
4
.github/stale.yml
vendored
@@ -1,11 +1,11 @@
|
||||
# Configuration for probot-stale - https://github.com/probot/stale
|
||||
|
||||
# Number of days of inactivity before an Issue or Pull Request becomes stale
|
||||
daysUntilStale: 7
|
||||
daysUntilStale: 14
|
||||
|
||||
# Number of days of inactivity before a stale Issue or Pull Request is closed.
|
||||
# Set to false to disable. If disabled, issues still need to be closed manually, but will remain marked as stale.
|
||||
daysUntilClose: 7
|
||||
daysUntilClose: 14
|
||||
|
||||
# Issues or Pull Requests with these labels will never be considered stale. Set to `[]` to disable
|
||||
# - "[Status] Maybe Later"
|
||||
|
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 }}
|
@@ -1,58 +0,0 @@
|
||||
---
|
||||
build:
|
||||
nodes:
|
||||
analysis:
|
||||
project_setup:
|
||||
override: true
|
||||
tests:
|
||||
override:
|
||||
- php-scrutinizer-run
|
||||
checks:
|
||||
javascript: true
|
||||
php:
|
||||
align_assignments: true
|
||||
avoid_fixme_comments: true
|
||||
avoid_multiple_statements_on_same_line: true
|
||||
avoid_perl_style_comments: true
|
||||
avoid_todo_comments: true
|
||||
duplication: false
|
||||
encourage_single_quotes: true
|
||||
newline_at_end_of_file: true
|
||||
no_goto: true
|
||||
no_long_variable_names:
|
||||
maximum: "20"
|
||||
no_short_method_names:
|
||||
minimum: "3"
|
||||
no_short_variable_names:
|
||||
minimum: "3"
|
||||
optional_parameters_at_the_end: true
|
||||
parameter_doc_comments: true
|
||||
remove_extra_empty_lines: true
|
||||
return_doc_comment_if_not_inferrable: true
|
||||
return_doc_comments: true
|
||||
uppercase_constants: true
|
||||
use_self_instead_of_fqcn: true
|
||||
coding_style:
|
||||
php:
|
||||
spaces:
|
||||
around_operators:
|
||||
concatenation: true
|
||||
other:
|
||||
after_type_cast: false
|
||||
filter:
|
||||
excluded_paths:
|
||||
- database/migrations/*
|
||||
- bootstrap/*
|
||||
- config/*
|
||||
- docker/*
|
||||
- public/js/lib/*
|
||||
- public/lib/adminlte/js/*
|
||||
- public/lib/bootstrap/js/*
|
||||
- resources/*
|
||||
- routes/*
|
||||
- storage/*
|
||||
paths:
|
||||
- app/*
|
||||
- public/js/ff/*
|
||||
tools:
|
||||
external_code_coverage: false
|
20
.travis.yml
20
.travis.yml
@@ -1,20 +0,0 @@
|
||||
language: php
|
||||
php:
|
||||
- '7.4'
|
||||
dist: xenial
|
||||
os: linux
|
||||
cache:
|
||||
directories:
|
||||
- "/home/travis/.config"
|
||||
- "/home/travis/build/firefly-iii/firefly-iii/vendor"
|
||||
|
||||
branches:
|
||||
only:
|
||||
- develop
|
||||
|
||||
before_script:
|
||||
- phpenv config-rm xdebug.ini || return 0
|
||||
|
||||
script:
|
||||
- "./.ci/phpstan.sh"
|
||||
- "./.ci/phpunit.sh"
|
@@ -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);
|
||||
}
|
||||
|
@@ -211,7 +211,7 @@ class StoreRequest extends FormRequest
|
||||
// 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_id' => ['mustExist:categories,id', new BelongsUser,'nullable'],
|
||||
'transactions.*.category_name' => 'between:1,255|nullable',
|
||||
'transactions.*.bill_id' => ['numeric', 'nullable', 'mustExist:bills,id', new BelongsUser],
|
||||
'transactions.*.bill_name' => ['between:1,255', 'nullable', new BelongsUser],
|
||||
|
@@ -154,6 +154,7 @@ class ExportData extends Command
|
||||
/**
|
||||
* @return array
|
||||
* @throws FireflyException
|
||||
* @throws Exception
|
||||
*/
|
||||
private function parseOptions(): array
|
||||
{
|
||||
@@ -201,12 +202,17 @@ class ExportData extends Command
|
||||
$error = true;
|
||||
}
|
||||
}
|
||||
if(null === $this->option($field)) {
|
||||
Log::info(sprintf('No date given in field "%s"', $field));
|
||||
$error = true;
|
||||
}
|
||||
|
||||
if (true === $error && 'start' === $field) {
|
||||
$journal = $this->journalRepository->firstNull();
|
||||
$date = null === $journal ? Carbon::now()->subYear() : $journal->date;
|
||||
$date->startOfDay();
|
||||
}
|
||||
|
||||
if (true === $error && 'end' === $field) {
|
||||
$date = today(config('app.timezone'));
|
||||
$date->endOfDay();
|
||||
|
@@ -143,11 +143,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!');
|
||||
}
|
||||
}
|
||||
|
44
app/Events/ActuallyLoggedIn.php
Normal file
44
app/Events/ActuallyLoggedIn.php
Normal file
@@ -0,0 +1,44 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
/*
|
||||
* ActuallyLoggedIn.php
|
||||
* Copyright (c) 2021 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/>.
|
||||
*/
|
||||
|
||||
namespace FireflyIII\Events;
|
||||
|
||||
use FireflyIII\User;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
|
||||
/**
|
||||
* Class ActuallyLoggedIn
|
||||
*/
|
||||
class ActuallyLoggedIn extends Event
|
||||
{
|
||||
use SerializesModels;
|
||||
|
||||
public User $user;
|
||||
|
||||
/**
|
||||
* @param User $user
|
||||
*/
|
||||
public function __construct(User $user)
|
||||
{
|
||||
$this->user = $user;
|
||||
}
|
||||
}
|
@@ -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));
|
||||
|
@@ -25,6 +25,7 @@ namespace FireflyIII\Handlers\Events;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use Exception;
|
||||
use FireflyIII\Events\ActuallyLoggedIn;
|
||||
use FireflyIII\Events\DetectedNewIPAddress;
|
||||
use FireflyIII\Events\RegisteredUser;
|
||||
use FireflyIII\Events\RequestedNewPassword;
|
||||
@@ -116,9 +117,24 @@ class UserEventHandler
|
||||
*/
|
||||
public function createGroupMembership(RegisteredUser $event): bool
|
||||
{
|
||||
$user = $event->user;
|
||||
$user = $event->user;
|
||||
$groupExists = true;
|
||||
$groupTitle = $user->email;
|
||||
$index = 1;
|
||||
|
||||
// create a new group.
|
||||
$group = UserGroup::create(['title' => $user->email]);
|
||||
while (true === $groupExists) {
|
||||
$groupExists = UserGroup::where('title', $groupTitle)->count() > 0;
|
||||
if(false === $groupExists) {
|
||||
$group = UserGroup::create(['title' => $groupTitle]);
|
||||
break;
|
||||
}
|
||||
$groupTitle = sprintf('%s-%d', $user->email, $index);
|
||||
$index++;
|
||||
if($index > 99) {
|
||||
throw new FireflyException('Email address can no longer be used for registrations.');
|
||||
}
|
||||
}
|
||||
$role = UserRole::where('title', UserRole::OWNER)->first();
|
||||
if (null === $role) {
|
||||
throw new FireflyException('The user role is unexpectedly empty. Did you run all migrations?');
|
||||
@@ -317,12 +333,11 @@ class UserEventHandler
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Login $event
|
||||
*
|
||||
* @throws FireflyException
|
||||
* @param ActuallyLoggedIn $event
|
||||
*/
|
||||
public function storeUserIPAddress(Login $event): void
|
||||
public function storeUserIPAddress(ActuallyLoggedIn $event): void
|
||||
{
|
||||
Log::debug('Now in storeUserIPAddress');
|
||||
/** @var User $user */
|
||||
$user = $event->user;
|
||||
/** @var array $preference */
|
||||
|
@@ -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);
|
||||
|
@@ -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.
|
||||
*
|
||||
|
@@ -565,4 +565,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;
|
||||
|
||||
}
|
||||
|
@@ -79,7 +79,7 @@ class NetWorth implements NetWorthInterface
|
||||
|
||||
$netWorth = [];
|
||||
$result = [];
|
||||
Log::debug(sprintf('Now in getNetWorthByCurrency(%s)', $date->format('Y-m-d')));
|
||||
//Log::debug(sprintf('Now in getNetWorthByCurrency(%s)', $date->format('Y-m-d')));
|
||||
|
||||
// get default currency
|
||||
$default = app('amount')->getDefaultCurrencyByUser($this->user);
|
||||
@@ -90,16 +90,16 @@ class NetWorth implements NetWorthInterface
|
||||
// get the preferred currency for this account
|
||||
/** @var Account $account */
|
||||
foreach ($accounts as $account) {
|
||||
Log::debug(sprintf('Now at account #%d: "%s"', $account->id, $account->name));
|
||||
//Log::debug(sprintf('Now at account #%d: "%s"', $account->id, $account->name));
|
||||
$currencyId = (int)$this->accountRepository->getMetaValue($account, 'currency_id');
|
||||
$currencyId = 0 === $currencyId ? $default->id : $currencyId;
|
||||
|
||||
Log::debug(sprintf('Currency ID is #%d', $currencyId));
|
||||
//Log::debug(sprintf('Currency ID is #%d', $currencyId));
|
||||
|
||||
// balance in array:
|
||||
$balance = $balances[$account->id] ?? '0';
|
||||
|
||||
Log::debug(sprintf('Balance is %s', $balance));
|
||||
//Log::debug(sprintf('Balance is %s', $balance));
|
||||
|
||||
// always subtract virtual balance.
|
||||
$virtualBalance = (string)$account->virtual_balance;
|
||||
@@ -107,14 +107,14 @@ class NetWorth implements NetWorthInterface
|
||||
$balance = bcsub($balance, $virtualBalance);
|
||||
}
|
||||
|
||||
Log::debug(sprintf('Balance corrected to %s because of virtual balance (%s)', $balance, $virtualBalance));
|
||||
//Log::debug(sprintf('Balance corrected to %s because of virtual balance (%s)', $balance, $virtualBalance));
|
||||
|
||||
if (!array_key_exists($currencyId, $netWorth)) {
|
||||
$netWorth[$currencyId] = '0';
|
||||
}
|
||||
$netWorth[$currencyId] = bcadd($balance, $netWorth[$currencyId]);
|
||||
|
||||
Log::debug(sprintf('Total net worth for currency #%d is %s', $currencyId, $netWorth[$currencyId]));
|
||||
//Log::debug(sprintf('Total net worth for currency #%d is %s', $currencyId, $netWorth[$currencyId]));
|
||||
}
|
||||
ksort($netWorth);
|
||||
|
||||
|
@@ -209,10 +209,10 @@ class PopupReport implements PopupReportInterface
|
||||
$collector = app(GroupCollectorInterface::class);
|
||||
|
||||
// set report accounts + the request accounts:
|
||||
$set = $attributes['accounts'] ?? new Collection;
|
||||
$set->push($account);
|
||||
//$set = $attributes['accounts'] ?? new Collection;
|
||||
//$set->push($account);
|
||||
|
||||
$collector->setBothAccounts($set)
|
||||
$collector->setDestinationAccounts(new Collection([$account]))
|
||||
->setRange($attributes['startDate'], $attributes['endDate'])
|
||||
->withAccountInformation()
|
||||
->withBudgetInformation()
|
||||
@@ -222,7 +222,6 @@ class PopupReport implements PopupReportInterface
|
||||
if (null !== $currency) {
|
||||
$collector->setCurrency($currency);
|
||||
}
|
||||
|
||||
return $collector->getExtractedJournals();
|
||||
}
|
||||
|
||||
|
@@ -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'),
|
||||
|
@@ -101,7 +101,7 @@ class IndexController extends Controller
|
||||
|
||||
$accounts->each(
|
||||
function (Account $account) use ($activities, $startBalances, $endBalances) {
|
||||
$account->lastActivityDate = $this->isInArray($activities, $account->id);
|
||||
$account->lastActivityDate = $this->isInArrayDate($activities, $account->id);
|
||||
$account->startBalance = $this->isInArray($startBalances, $account->id);
|
||||
$account->endBalance = $this->isInArray($endBalances, $account->id);
|
||||
$account->difference = bcsub($account->endBalance, $account->startBalance);
|
||||
@@ -163,7 +163,7 @@ class IndexController extends Controller
|
||||
$accounts->each(
|
||||
function (Account $account) use ($activities, $startBalances, $endBalances) {
|
||||
// See reference nr. 68
|
||||
$account->lastActivityDate = $this->isInArray($activities, $account->id);
|
||||
$account->lastActivityDate = $this->isInArrayDate($activities, $account->id);
|
||||
$account->startBalance = $this->isInArray($startBalances, $account->id);
|
||||
$account->endBalance = $this->isInArray($endBalances, $account->id);
|
||||
$account->difference = bcsub($account->endBalance, $account->startBalance);
|
||||
|
@@ -25,6 +25,7 @@ namespace FireflyIII\Http\Controllers\Auth;
|
||||
use Adldap;
|
||||
use Cookie;
|
||||
use DB;
|
||||
use FireflyIII\Events\ActuallyLoggedIn;
|
||||
use FireflyIII\Exceptions\FireflyException;
|
||||
use FireflyIII\Http\Controllers\Controller;
|
||||
use FireflyIII\Providers\RouteServiceProvider;
|
||||
@@ -119,6 +120,10 @@ class LoginController extends Controller
|
||||
|
||||
// if you just logged in, it can't be that you have a valid 2FA cookie.
|
||||
|
||||
// send a custom login event because laravel will also fire a login event if a "remember me"-cookie
|
||||
// restores the event.
|
||||
event(new ActuallyLoggedIn($this->guard()->user()));
|
||||
|
||||
return $this->sendLoginResponse($request);
|
||||
}
|
||||
Log::warning('Login attempt failed.');
|
||||
|
@@ -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").
|
||||
|
@@ -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 : '',
|
||||
|
@@ -59,7 +59,6 @@ class ReportController extends Controller
|
||||
'category-entry' => $this->categoryEntry($attributes),
|
||||
'budget-entry' => $this->budgetEntry($attributes),
|
||||
};
|
||||
|
||||
return response()->json(['html' => $html]);
|
||||
}
|
||||
}
|
||||
|
@@ -648,10 +648,9 @@ class CategoryController extends Controller
|
||||
* @param Carbon $start
|
||||
* @param Carbon $end
|
||||
*
|
||||
* @return mixed|string
|
||||
* @throws JsonException
|
||||
* @return string
|
||||
*/
|
||||
public function operations(Collection $accounts, Carbon $start, Carbon $end)
|
||||
public function operations(Collection $accounts, Carbon $start, Carbon $end): string
|
||||
{
|
||||
// chart properties for cache:
|
||||
$cache = new CacheProperties;
|
||||
@@ -673,7 +672,7 @@ class CategoryController extends Controller
|
||||
|
||||
|
||||
try {
|
||||
$result = prefixView('reports.partials.categories', compact('report'))->render();
|
||||
$result = (string)prefixView('reports.partials.categories', compact('report'))->render();
|
||||
$cache->store($result);
|
||||
} catch (Throwable $e) { // @phpstan-ignore-line
|
||||
Log::error(sprintf('Could not render category::expenses: %s', $e->getMessage()));
|
||||
|
@@ -34,6 +34,7 @@ use FireflyIII\Support\Http\Controllers\ModelInformation;
|
||||
use FireflyIII\Support\Http\Controllers\RuleManagement;
|
||||
use FireflyIII\Support\Search\SearchInterface;
|
||||
use Illuminate\Contracts\View\Factory;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Http\RedirectResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Routing\Redirector;
|
||||
@@ -237,15 +238,17 @@ class CreateController extends Controller
|
||||
/**
|
||||
* @param Rule $rule
|
||||
*
|
||||
* @return RedirectResponse
|
||||
* @return JsonResponse
|
||||
*/
|
||||
public function duplicate(Rule $rule): RedirectResponse
|
||||
public function duplicate(Request $request): JsonResponse
|
||||
{
|
||||
$newRule = $this->ruleRepos->duplicate($rule);
|
||||
$ruleId = (int)$request->get('id');
|
||||
$rule = $this->ruleRepos->find($ruleId);
|
||||
if (null !== $rule) {
|
||||
$this->ruleRepos->duplicate($rule);
|
||||
}
|
||||
|
||||
session()->flash('success', trans('firefly.duplicated_rule', ['title' => $rule->title, 'newTitle' => $newRule->title]));
|
||||
|
||||
return redirect(route('rules.index'));
|
||||
return new JsonResponse(['OK']);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -28,6 +28,7 @@ use FireflyIII\Http\Requests\RuleGroupFormRequest;
|
||||
use FireflyIII\Models\RuleGroup;
|
||||
use FireflyIII\Repositories\RuleGroup\RuleGroupRepositoryInterface;
|
||||
use Illuminate\Contracts\View\Factory;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Http\RedirectResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Routing\Redirector;
|
||||
@@ -62,24 +63,38 @@ class EditController extends Controller
|
||||
}
|
||||
|
||||
/**
|
||||
* Move a rule group down.
|
||||
* Move a rule group in either direction.
|
||||
*
|
||||
* @param RuleGroup $ruleGroup
|
||||
* @param Request $request
|
||||
*
|
||||
* @return RedirectResponse|Redirector
|
||||
* @return JsonResponse
|
||||
*/
|
||||
public function down(RuleGroup $ruleGroup)
|
||||
public function moveGroup(Request $request): JsonResponse
|
||||
{
|
||||
$maxOrder = $this->repository->maxOrder();
|
||||
$order = (int)$ruleGroup->order;
|
||||
if ($order < $maxOrder) {
|
||||
$newOrder = $order + 1;
|
||||
$this->repository->setOrder($ruleGroup, $newOrder);
|
||||
$groupId = (int)$request->get('id');
|
||||
$ruleGroup= $this->repository->find($groupId);
|
||||
if(null !== $ruleGroup) {
|
||||
$direction = $request->get('direction');
|
||||
if('down' === $direction) {
|
||||
$maxOrder = $this->repository->maxOrder();
|
||||
$order = (int)$ruleGroup->order;
|
||||
if ($order < $maxOrder) {
|
||||
$newOrder = $order + 1;
|
||||
$this->repository->setOrder($ruleGroup, $newOrder);
|
||||
}
|
||||
}
|
||||
if('up' === $direction) {
|
||||
$order = (int)$ruleGroup->order;
|
||||
if ($order > 1) {
|
||||
$newOrder = $order - 1;
|
||||
$this->repository->setOrder($ruleGroup, $newOrder);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return redirect(route('rules.index'));
|
||||
return new JsonResponse(['OK']);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Edit a rule group.
|
||||
*
|
||||
@@ -106,25 +121,6 @@ class EditController extends Controller
|
||||
return prefixView('rules.rule-group.edit', compact('ruleGroup', 'subTitle'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Move the rule group up.
|
||||
*
|
||||
* @param RuleGroup $ruleGroup
|
||||
*
|
||||
* @return RedirectResponse|Redirector
|
||||
*
|
||||
*/
|
||||
public function up(RuleGroup $ruleGroup)
|
||||
{
|
||||
$order = (int)$ruleGroup->order;
|
||||
if ($order > 1) {
|
||||
$newOrder = $order - 1;
|
||||
$this->repository->setOrder($ruleGroup, $newOrder);
|
||||
}
|
||||
|
||||
return redirect(route('rules.index'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the rule group.
|
||||
*
|
||||
|
@@ -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']);
|
||||
}
|
||||
|
@@ -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));
|
||||
}
|
||||
}
|
||||
|
@@ -32,9 +32,6 @@ use Illuminate\Contracts\Config\Repository;
|
||||
*/
|
||||
class TrustProxies extends Middleware
|
||||
{
|
||||
/** @var int The headers to check. */
|
||||
//protected $headers = Request::HEADER_X_FORWARDED_ALL;
|
||||
|
||||
/**
|
||||
* TrustProxies constructor.
|
||||
*
|
||||
@@ -42,11 +39,7 @@ class TrustProxies extends Middleware
|
||||
*/
|
||||
public function __construct(Repository $config)
|
||||
{
|
||||
$trustedProxies = (string)config('firefly.trusted_proxies');
|
||||
$this->proxies = explode(',', $trustedProxies);
|
||||
if ('**' === $trustedProxies) {
|
||||
$this->proxies = '**';
|
||||
}
|
||||
$this->proxies = (string)config('firefly.trusted_proxies');
|
||||
parent::__construct($config);
|
||||
}
|
||||
}
|
||||
|
@@ -69,7 +69,7 @@ class BillStoreRequest extends FormRequest
|
||||
'transaction_currency_id' => 'required|exists:transaction_currencies,id',
|
||||
'date' => 'required|date',
|
||||
'repeat_freq' => 'required|in:weekly,monthly,quarterly,half-year,yearly',
|
||||
'skip' => 'required|between:0,31',
|
||||
'skip' => 'required|integer|gte:0|lte:31',
|
||||
'active' => 'boolean',
|
||||
];
|
||||
}
|
||||
|
@@ -73,7 +73,7 @@ class BillUpdateRequest extends FormRequest
|
||||
'transaction_currency_id' => 'required|exists:transaction_currencies,id',
|
||||
'date' => 'required|date',
|
||||
'repeat_freq' => 'required|in:weekly,monthly,quarterly,half-year,yearly',
|
||||
'skip' => 'required|between:0,31',
|
||||
'skip' => 'required|integer|gte:0|lte:31',
|
||||
'active' => 'boolean',
|
||||
];
|
||||
}
|
||||
|
@@ -63,7 +63,7 @@ class BudgetFormStoreRequest extends FormRequest
|
||||
return [
|
||||
'name' => 'required|between:1,100|uniqueObjectForUser:budgets,name',
|
||||
'active' => 'numeric|between:0,1',
|
||||
'auto_budget_type' => 'numeric|between:0,2',
|
||||
'auto_budget_type' => 'numeric|integer|gte:0|lte:2',
|
||||
'auto_budget_currency_id' => 'exists:transaction_currencies,id',
|
||||
'auto_budget_amount' => 'min:0|max:1000000000|required_if:auto_budget_type,1|required_if:auto_budget_type,2',
|
||||
'auto_budget_period' => 'in:daily,weekly,monthly,quarterly,half_year,yearly',
|
||||
|
@@ -73,7 +73,7 @@ class BudgetFormUpdateRequest extends FormRequest
|
||||
return [
|
||||
'name' => $nameRule,
|
||||
'active' => 'numeric|between:0,1',
|
||||
'auto_budget_type' => 'numeric|between:0,2',
|
||||
'auto_budget_type' => 'numeric|integer|gte:0|lte:31',
|
||||
'auto_budget_currency_id' => 'exists:transaction_currencies,id',
|
||||
'auto_budget_amount' => 'min:0|max:1000000000|required_if:auto_budget_type,1|required_if:auto_budget_type,2',
|
||||
'auto_budget_period' => 'in:daily,weekly,monthly,quarterly,half_year,yearly',
|
||||
|
@@ -192,7 +192,7 @@ class RecurrenceFormRequest extends FormRequest
|
||||
'title' => 'required|between:1,255|uniqueObjectForUser:recurrences,title',
|
||||
'first_date' => 'required|date|after:' . $today->format('Y-m-d'),
|
||||
'repetition_type' => ['required', new ValidRecurrenceRepetitionValue, new ValidRecurrenceRepetitionType, 'between:1,20'],
|
||||
'skip' => 'required|numeric|between:0,31',
|
||||
'skip' => 'required|numeric|integer|gte:0|lte:31',
|
||||
|
||||
// optional for recurrence:
|
||||
'recurring_description' => 'between:0,65000',
|
||||
@@ -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');
|
||||
|
@@ -431,7 +431,7 @@ class CreateRecurringTransactions implements ShouldQueue
|
||||
'user' => $recurrence->user_id,
|
||||
'currency_id' => (int)$transaction->transaction_currency_id,
|
||||
'currency_code' => null,
|
||||
'description' => $recurrence->recurrenceTransactions()->first()->description,
|
||||
'description' => $transactions->first()->description,
|
||||
'amount' => $transaction->amount,
|
||||
'budget_id' => $this->repository->getBudget($transaction),
|
||||
'budget_name' => null,
|
||||
@@ -452,7 +452,7 @@ class CreateRecurringTransactions implements ShouldQueue
|
||||
'tags' => $this->repository->getTags($transaction),
|
||||
'piggy_bank_id' => $this->repository->getPiggyBank($transaction),
|
||||
'piggy_bank_name' => null,
|
||||
'bill_id' => null,
|
||||
'bill_id' => $this->repository->getBillId($transaction),
|
||||
'bill_name' => null,
|
||||
'recurrence_total' => $total,
|
||||
'recurrence_count' => $count,
|
||||
|
@@ -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();
|
||||
}
|
||||
}
|
||||
|
@@ -4,7 +4,8 @@ declare(strict_types=1);
|
||||
namespace FireflyIII\Ldap\Rules;
|
||||
|
||||
use LdapRecord\Laravel\Auth\Rule;
|
||||
use LdapRecord\Models\ActiveDirectory\Group;
|
||||
use LdapRecord\Models\Attributes\DistinguishedName;
|
||||
use LdapRecord\Query\ObjectNotFoundException;
|
||||
use Log;
|
||||
|
||||
/**
|
||||
@@ -16,22 +17,51 @@ class UserDefinedRule extends Rule
|
||||
* Check if the rule passes validation.
|
||||
*
|
||||
* @return bool
|
||||
* @throws ObjectNotFoundException
|
||||
*/
|
||||
public function isValid()
|
||||
{
|
||||
// LDAP_GROUP_FILTER
|
||||
$groupFilter = config('ldap.group_filter');
|
||||
Log::debug(sprintf('UserDefinedRule with group filter "%s"', $groupFilter));
|
||||
if (null !== $groupFilter && '' !== (string)$groupFilter) {
|
||||
Log::debug('Group filter is not empty, will now apply it.');
|
||||
$administrators = Group::find($groupFilter);
|
||||
$result = $this->user->groups()->recursive()->exists($administrators);
|
||||
Log::debug(sprintf('Search result is %s.', var_export($result, true)));
|
||||
$extraFilter = config('ldap.extra_filter');
|
||||
Log::debug(sprintf('UserDefinedRule with extra filter "%s"', $extraFilter));
|
||||
|
||||
return $result;
|
||||
if (empty($extraFilter)) {
|
||||
Log::debug('Extra filter is empty, return true.');
|
||||
|
||||
return true;
|
||||
}
|
||||
Log::debug('Group filter is empty or NULL, so will return true.');
|
||||
Log::debug('Extra filter is not empty, continue.');
|
||||
|
||||
return true;
|
||||
// group class:
|
||||
// use ;
|
||||
$openLDAP = class_exists(\LdapRecord\Models\OpenLDAP\Group::class) ? \LdapRecord\Models\OpenLDAP\Group::class : '';
|
||||
$activeDirectory = class_exists(\LdapRecord\Models\ActiveDirectory\Group::class) ? \LdapRecord\Models\ActiveDirectory\Group::class : '';
|
||||
$groupClass = config('ldap.dialect') === 'OpenLDAP' ? $openLDAP : $activeDirectory;
|
||||
|
||||
Log::debug(sprintf('Will use dialect group class "%s"', $groupClass));
|
||||
|
||||
|
||||
// We've been given an invalid group filter. We will assume the
|
||||
// developer is using some group ANR attribute, and attempt
|
||||
// to check the user's membership with the resulting group.
|
||||
if (!DistinguishedName::isValid($extraFilter)) {
|
||||
Log::debug('UserDefinedRule: Is not valid DN');
|
||||
|
||||
return $this->user->groups()->recursive()->exists($groupClass::findByAnrOrFail($extraFilter));
|
||||
}
|
||||
|
||||
$head = strtolower(DistinguishedName::make($extraFilter)->head());
|
||||
Log::debug(sprintf('UserDefinedRule: Head is "%s"', $head));
|
||||
// If the head of the DN we've been given is an OU, we will assume
|
||||
// the developer is looking to filter users based on hierarchy.
|
||||
// Otherwise, we'll attempt locating a group by the given
|
||||
// group filter and checking the users group membership.
|
||||
if ('ou' === $head) {
|
||||
Log::debug('UserDefinedRule: Will return if user is a descendant of.');
|
||||
|
||||
return $this->user->isDescendantOf($extraFilter);
|
||||
}
|
||||
Log::debug('UserDefinedRule: Will return if user exists in group.');
|
||||
|
||||
return $this->user->groups()->recursive()->exists($groupClass::findOrFail($extraFilter));
|
||||
}
|
||||
}
|
||||
|
28
app/Ldap/Scopes/UserDefinedScope.php
Normal file
28
app/Ldap/Scopes/UserDefinedScope.php
Normal file
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\Ldap\Scopes;
|
||||
|
||||
use LdapRecord\Models\Model;
|
||||
use LdapRecord\Models\Scope;
|
||||
use LdapRecord\Query\Model\Builder;
|
||||
use Log;
|
||||
|
||||
|
||||
/**
|
||||
* Class UserDefinedScope
|
||||
*/
|
||||
class UserDefinedScope implements Scope
|
||||
{
|
||||
/**
|
||||
* Apply the scope to the given query.
|
||||
*
|
||||
* @param Builder $query
|
||||
* @param Model $model
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function apply(Builder $query, Model $model)
|
||||
{
|
||||
}
|
||||
}
|
@@ -23,6 +23,7 @@ declare(strict_types=1);
|
||||
namespace FireflyIII\Providers;
|
||||
|
||||
use Exception;
|
||||
use FireflyIII\Events\ActuallyLoggedIn;
|
||||
use FireflyIII\Events\AdminRequestedTestMessage;
|
||||
use FireflyIII\Events\DestroyedTransactionGroup;
|
||||
use FireflyIII\Events\DetectedNewIPAddress;
|
||||
@@ -74,6 +75,8 @@ class EventServiceProvider extends ServiceProvider
|
||||
Login::class => [
|
||||
'FireflyIII\Handlers\Events\UserEventHandler@checkSingleUserIsAdmin',
|
||||
'FireflyIII\Handlers\Events\UserEventHandler@demoUserBackToEnglish',
|
||||
],
|
||||
ActuallyLoggedIn::class => [
|
||||
'FireflyIII\Handlers\Events\UserEventHandler@storeUserIPAddress',
|
||||
],
|
||||
DetectedNewIPAddress::class => [
|
||||
|
@@ -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;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -251,7 +251,7 @@ class BillRepository implements BillRepositoryInterface
|
||||
$journalIds = $set->pluck('id')->toArray();
|
||||
$amount = (string)Transaction::whereIn('transaction_journal_id', $journalIds)->where('amount', '<', 0)->sum('amount');
|
||||
$sum = bcadd($sum, $amount);
|
||||
Log::debug(sprintf('Total > 0, so add to sum %f, which becomes %f', $amount, $sum));
|
||||
//Log::debug(sprintf('Total > 0, so add to sum %f, which becomes %f', $amount, $sum));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -281,7 +281,7 @@ class BillRepository implements BillRepositoryInterface
|
||||
$amount = (string)Transaction::whereIn('transaction_journal_id', $journalIds)->where('amount', '<', 0)->sum('amount');
|
||||
$return[$currencyId] = $return[$currencyId] ?? '0';
|
||||
$return[$currencyId] = bcadd($amount, $return[$currencyId]);
|
||||
Log::debug(sprintf('Total > 0, so add to sum %f, which becomes %f (currency %d)', $amount, $return[$currencyId], $currencyId));
|
||||
//Log::debug(sprintf('Total > 0, so add to sum %f, which becomes %f (currency %d)', $amount, $return[$currencyId], $currencyId));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -302,18 +302,18 @@ class BillRepository implements BillRepositoryInterface
|
||||
$sum = '0';
|
||||
/** @var Bill $bill */
|
||||
foreach ($bills as $bill) {
|
||||
Log::debug(sprintf('Now at bill #%d (%s)', $bill->id, $bill->name));
|
||||
//Log::debug(sprintf('Now at bill #%d (%s)', $bill->id, $bill->name));
|
||||
$dates = $this->getPayDatesInRange($bill, $start, $end);
|
||||
$count = $bill->transactionJournals()->after($start)->before($end)->count();
|
||||
$total = $dates->count() - $count;
|
||||
|
||||
Log::debug(sprintf('Dates = %d, journalCount = %d, total = %d', $dates->count(), $count, $total));
|
||||
//Log::debug(sprintf('Dates = %d, journalCount = %d, total = %d', $dates->count(), $count, $total));
|
||||
|
||||
if ($total > 0) {
|
||||
$average = bcdiv(bcadd($bill->amount_max, $bill->amount_min), '2');
|
||||
$multi = bcmul($average, (string)$total);
|
||||
$sum = bcadd($sum, $multi);
|
||||
Log::debug(sprintf('Total > 0, so add to sum %f, which becomes %f', $multi, $sum));
|
||||
//Log::debug(sprintf('Total > 0, so add to sum %f, which becomes %f', $multi, $sum));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -334,20 +334,20 @@ class BillRepository implements BillRepositoryInterface
|
||||
$return = [];
|
||||
/** @var Bill $bill */
|
||||
foreach ($bills as $bill) {
|
||||
Log::debug(sprintf('Now at bill #%d (%s)', $bill->id, $bill->name));
|
||||
//Log::debug(sprintf('Now at bill #%d (%s)', $bill->id, $bill->name));
|
||||
$dates = $this->getPayDatesInRange($bill, $start, $end);
|
||||
$count = $bill->transactionJournals()->after($start)->before($end)->count();
|
||||
$total = $dates->count() - $count;
|
||||
$currencyId = (int)$bill->transaction_currency_id;
|
||||
|
||||
Log::debug(sprintf('Dates = %d, journalCount = %d, total = %d', $dates->count(), $count, $total));
|
||||
//Log::debug(sprintf('Dates = %d, journalCount = %d, total = %d', $dates->count(), $count, $total));
|
||||
|
||||
if ($total > 0) {
|
||||
$average = bcdiv(bcadd($bill->amount_max, $bill->amount_min), '2');
|
||||
$multi = bcmul($average, (string)$total);
|
||||
$return[$currencyId] = $return[$currencyId] ?? '0';
|
||||
$return[$currencyId] = bcadd($return[$currencyId], $multi);
|
||||
Log::debug(sprintf('Total > 0, so add to sum %f, which becomes %f (for currency %d)', $multi, $return[$currencyId], $currencyId));
|
||||
//Log::debug(sprintf('Total > 0, so add to sum %f, which becomes %f (for currency %d)', $multi, $return[$currencyId], $currencyId));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -481,7 +481,7 @@ class BillRepository implements BillRepositoryInterface
|
||||
//Log::debug(sprintf('First currentstart is %s', $currentStart->format('Y-m-d')));
|
||||
|
||||
while ($currentStart <= $end) {
|
||||
Log::debug(sprintf('Currentstart is now %s.', $currentStart->format('Y-m-d')));
|
||||
//Log::debug(sprintf('Currentstart is now %s.', $currentStart->format('Y-m-d')));
|
||||
$nextExpectedMatch = $this->nextDateMatch($bill, $currentStart);
|
||||
//Log::debug(sprintf('Next Date match after %s is %s', $currentStart->format('Y-m-d'), $nextExpectedMatch->format('Y-m-d')));
|
||||
if ($nextExpectedMatch > $end) {// If nextExpectedMatch is after end, we continue
|
||||
|
@@ -35,8 +35,7 @@ use Illuminate\Support\Collection;
|
||||
*/
|
||||
class OperationsRepository implements OperationsRepositoryInterface
|
||||
{
|
||||
/** @var User */
|
||||
private $user;
|
||||
private User $user;
|
||||
|
||||
/**
|
||||
* This method returns a list of all the withdrawal transaction journals (as arrays) set in that period
|
||||
@@ -340,4 +339,136 @@ class OperationsRepository implements OperationsRepositoryInterface
|
||||
{
|
||||
return $this->user->categories()->get();
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function listTransferredIn(Carbon $start, Carbon $end, Collection $accounts, ?Collection $categories = null): array
|
||||
{
|
||||
/** @var GroupCollectorInterface $collector */
|
||||
$collector = app(GroupCollectorInterface::class);
|
||||
$collector->setUser($this->user)->setRange($start, $end)->setTypes([TransactionType::TRANSFER])
|
||||
->setDestinationAccounts($accounts)->excludeSourceAccounts($accounts);
|
||||
if (null !== $categories && $categories->count() > 0) {
|
||||
$collector->setCategories($categories);
|
||||
}
|
||||
if (null === $categories || (null !== $categories && 0 === $categories->count())) {
|
||||
$collector->setCategories($this->getCategories());
|
||||
}
|
||||
$collector->withCategoryInformation()->withAccountInformation()->withBudgetInformation();
|
||||
$journals = $collector->getExtractedJournals();
|
||||
$array = [];
|
||||
|
||||
foreach ($journals as $journal) {
|
||||
$currencyId = (int)$journal['currency_id'];
|
||||
$categoryId = (int)$journal['category_id'];
|
||||
$categoryName = (string)$journal['category_name'];
|
||||
|
||||
// catch "no category" entries.
|
||||
if (0 === $categoryId) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// info about the currency:
|
||||
$array[$currencyId] = $array[$currencyId] ?? [
|
||||
'categories' => [],
|
||||
'currency_id' => $currencyId,
|
||||
'currency_name' => $journal['currency_name'],
|
||||
'currency_symbol' => $journal['currency_symbol'],
|
||||
'currency_code' => $journal['currency_code'],
|
||||
'currency_decimal_places' => $journal['currency_decimal_places'],
|
||||
];
|
||||
|
||||
// info about the categories:
|
||||
$array[$currencyId]['categories'][$categoryId] = $array[$currencyId]['categories'][$categoryId] ?? [
|
||||
'id' => $categoryId,
|
||||
'name' => $categoryName,
|
||||
'transaction_journals' => [],
|
||||
];
|
||||
|
||||
// add journal to array:
|
||||
// only a subset of the fields.
|
||||
$journalId = (int)$journal['transaction_journal_id'];
|
||||
$array[$currencyId]['categories'][$categoryId]['transaction_journals'][$journalId] = [
|
||||
'amount' => app('steam')->positive($journal['amount']),
|
||||
'date' => $journal['date'],
|
||||
'source_account_id' => $journal['source_account_id'],
|
||||
'category_name' => $journal['category_name'],
|
||||
'source_account_name' => $journal['source_account_name'],
|
||||
'destination_account_id' => $journal['destination_account_id'],
|
||||
'destination_account_name' => $journal['destination_account_name'],
|
||||
'description' => $journal['description'],
|
||||
'transaction_group_id' => $journal['transaction_group_id'],
|
||||
];
|
||||
|
||||
}
|
||||
|
||||
return $array;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function listTransferredOut(Carbon $start, Carbon $end, Collection $accounts, ?Collection $categories = null): array
|
||||
{
|
||||
/** @var GroupCollectorInterface $collector */
|
||||
$collector = app(GroupCollectorInterface::class);
|
||||
$collector->setUser($this->user)->setRange($start, $end)->setTypes([TransactionType::TRANSFER])
|
||||
->setSourceAccounts($accounts)->excludeDestinationAccounts($accounts);
|
||||
if (null !== $categories && $categories->count() > 0) {
|
||||
$collector->setCategories($categories);
|
||||
}
|
||||
if (null === $categories || (null !== $categories && 0 === $categories->count())) {
|
||||
$collector->setCategories($this->getCategories());
|
||||
}
|
||||
$collector->withCategoryInformation()->withAccountInformation()->withBudgetInformation();
|
||||
$journals = $collector->getExtractedJournals();
|
||||
$array = [];
|
||||
|
||||
foreach ($journals as $journal) {
|
||||
$currencyId = (int)$journal['currency_id'];
|
||||
$categoryId = (int)$journal['category_id'];
|
||||
$categoryName = (string)$journal['category_name'];
|
||||
|
||||
// catch "no category" entries.
|
||||
if (0 === $categoryId) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// info about the currency:
|
||||
$array[$currencyId] = $array[$currencyId] ?? [
|
||||
'categories' => [],
|
||||
'currency_id' => $currencyId,
|
||||
'currency_name' => $journal['currency_name'],
|
||||
'currency_symbol' => $journal['currency_symbol'],
|
||||
'currency_code' => $journal['currency_code'],
|
||||
'currency_decimal_places' => $journal['currency_decimal_places'],
|
||||
];
|
||||
|
||||
// info about the categories:
|
||||
$array[$currencyId]['categories'][$categoryId] = $array[$currencyId]['categories'][$categoryId] ?? [
|
||||
'id' => $categoryId,
|
||||
'name' => $categoryName,
|
||||
'transaction_journals' => [],
|
||||
];
|
||||
|
||||
// add journal to array:
|
||||
// only a subset of the fields.
|
||||
$journalId = (int)$journal['transaction_journal_id'];
|
||||
$array[$currencyId]['categories'][$categoryId]['transaction_journals'][$journalId] = [
|
||||
'amount' => app('steam')->negative($journal['amount']),
|
||||
'date' => $journal['date'],
|
||||
'source_account_id' => $journal['source_account_id'],
|
||||
'category_name' => $journal['category_name'],
|
||||
'source_account_name' => $journal['source_account_name'],
|
||||
'destination_account_id' => $journal['destination_account_id'],
|
||||
'destination_account_name' => $journal['destination_account_name'],
|
||||
'description' => $journal['description'],
|
||||
'transaction_group_id' => $journal['transaction_group_id'],
|
||||
];
|
||||
|
||||
}
|
||||
|
||||
return $array;
|
||||
}
|
||||
}
|
||||
|
@@ -47,6 +47,36 @@ interface OperationsRepositoryInterface
|
||||
*/
|
||||
public function listExpenses(Carbon $start, Carbon $end, ?Collection $accounts = null, ?Collection $categories = null): array;
|
||||
|
||||
/**
|
||||
* This method returns a list of all the transfer transaction journals (as arrays) set in that period
|
||||
* which have the specified category set to them, transferred INTO the listed accounts.
|
||||
* It excludes any transfers between the listed accounts.
|
||||
* It's grouped per currency, with as few details in the array as possible. Amounts are always negative.
|
||||
*
|
||||
* @param Carbon $start
|
||||
* @param Carbon $end
|
||||
* @param Collection $accounts
|
||||
* @param Collection|null $categories
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function listTransferredIn(Carbon $start, Carbon $end, Collection $accounts, ?Collection $categories = null): array;
|
||||
|
||||
/**
|
||||
* This method returns a list of all the transfer transaction journals (as arrays) set in that period
|
||||
* which have the specified category set to them, transferred FROM the listed accounts.
|
||||
* It excludes any transfers between the listed accounts.
|
||||
* It's grouped per currency, with as few details in the array as possible. Amounts are always negative.
|
||||
*
|
||||
* @param Carbon $start
|
||||
* @param Carbon $end
|
||||
* @param Collection $accounts
|
||||
* @param Collection|null $categories
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function listTransferredOut(Carbon $start, Carbon $end, Collection $accounts, ?Collection $categories = null): array;
|
||||
|
||||
/**
|
||||
* This method returns a list of all the deposit transaction journals (as arrays) set in that period
|
||||
* which have the specified category set to them. It's grouped per currency, with as few details in the array
|
||||
|
@@ -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
|
||||
|
@@ -613,4 +613,20 @@ class RecurringRepository implements RecurringRepositoryInterface
|
||||
|
||||
return $filtered;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function getBillId(RecurrenceTransaction $recTransaction): ?int
|
||||
{
|
||||
$return = null;
|
||||
/** @var RecurrenceTransactionMeta $meta */
|
||||
foreach ($recTransaction->recurrenceTransactionMeta as $meta) {
|
||||
if ('bill_id' === $meta->name) {
|
||||
$return = (int)$meta->value;
|
||||
}
|
||||
}
|
||||
|
||||
return $return;
|
||||
}
|
||||
}
|
||||
|
@@ -82,6 +82,15 @@ interface RecurringRepositoryInterface
|
||||
*/
|
||||
public function getCategoryId(RecurrenceTransaction $recTransaction): ?int;
|
||||
|
||||
/**
|
||||
* Get the category from a recurring transaction transaction.
|
||||
*
|
||||
* @param RecurrenceTransaction $recTransaction
|
||||
*
|
||||
* @return null|int
|
||||
*/
|
||||
public function getBillId(RecurrenceTransaction $recTransaction): ?int;
|
||||
|
||||
/**
|
||||
* Get the category from a recurring transaction transaction.
|
||||
*
|
||||
|
@@ -329,10 +329,8 @@ class RuleGroupRepository implements RuleGroupRepositoryInterface
|
||||
*/
|
||||
public function resetOrder(): bool
|
||||
{
|
||||
$this->user->ruleGroups()->where('active', false)->update(['order' => 0]);
|
||||
$set = $this->user
|
||||
->ruleGroups()
|
||||
->where('active', true)
|
||||
->whereNull('deleted_at')
|
||||
->orderBy('order', 'ASC')
|
||||
->orderBy('title', 'DESC')
|
||||
@@ -363,7 +361,6 @@ class RuleGroupRepository implements RuleGroupRepositoryInterface
|
||||
{
|
||||
$set = $ruleGroup->rules()
|
||||
->orderBy('order', 'ASC')
|
||||
->where('active', true)
|
||||
->orderBy('title', 'DESC')
|
||||
->orderBy('updated_at', 'DESC')
|
||||
->get(['rules.*']);
|
||||
|
@@ -484,4 +484,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.
|
||||
*
|
||||
|
@@ -26,6 +26,7 @@ use Exception;
|
||||
use FireflyIII\Exceptions\FireflyException;
|
||||
use FireflyIII\Models\BudgetLimit;
|
||||
use FireflyIII\Models\Role;
|
||||
use FireflyIII\Models\UserGroup;
|
||||
use FireflyIII\User;
|
||||
use Illuminate\Database\QueryException;
|
||||
use Illuminate\Support\Collection;
|
||||
@@ -164,7 +165,10 @@ class UserRepository implements UserRepositoryInterface
|
||||
public function destroy(User $user): bool
|
||||
{
|
||||
Log::debug(sprintf('Calling delete() on user %d', $user->id));
|
||||
|
||||
$user->groupMemberships()->delete();
|
||||
$user->delete();
|
||||
$this->deleteEmptyGroups();
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -396,4 +400,20 @@ class UserRepository implements UserRepositoryInterface
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function deleteEmptyGroups(): void
|
||||
{
|
||||
$groups = UserGroup::get();
|
||||
/** @var UserGroup $group */
|
||||
foreach ($groups as $group) {
|
||||
$count = $group->groupMemberships()->count();
|
||||
if (0 === $count) {
|
||||
Log::info(sprintf('Deleted empty group #%d ("%s")', $group->id, $group->title));
|
||||
$group->delete();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -39,6 +39,11 @@ interface UserRepositoryInterface
|
||||
*/
|
||||
public function all(): Collection;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public function deleteEmptyGroups(): void;
|
||||
|
||||
/**
|
||||
* Gives a user a role.
|
||||
*
|
||||
|
@@ -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;
|
||||
|
@@ -530,6 +530,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 +548,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 +558,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;
|
||||
|
@@ -77,9 +77,9 @@ trait JournalServiceTrait
|
||||
Log::debug(sprintf($message, $transactionType, $direction, implode(', ', $expectedTypes[$transactionType] ?? ['UNKNOWN']), $direction));
|
||||
|
||||
$result = $this->findAccountById($data, $expectedTypes[$transactionType]);
|
||||
$result = $this->findAccountByName($result, $data, $expectedTypes[$transactionType]);
|
||||
$result = $this->findAccountByIban($result, $data, $expectedTypes[$transactionType]);
|
||||
$result = $this->findAccountByNumber($result, $data, $expectedTypes[$transactionType]);
|
||||
$result = $this->findAccountByName($result, $data, $expectedTypes[$transactionType]);
|
||||
$result = $this->createAccount($result, $data, $expectedTypes[$transactionType][0]);
|
||||
|
||||
return $this->getCashAccount($result, $data, $expectedTypes[$transactionType]);
|
||||
|
@@ -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']) {
|
||||
|
@@ -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)) {
|
||||
|
@@ -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));
|
||||
|
||||
|
@@ -23,6 +23,8 @@ declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\Support\Http\Controllers;
|
||||
|
||||
use Carbon\Carbon;
|
||||
|
||||
/**
|
||||
* Trait BasicDataSupport
|
||||
*
|
||||
@@ -30,15 +32,28 @@ namespace FireflyIII\Support\Http\Controllers;
|
||||
trait BasicDataSupport
|
||||
{
|
||||
/**
|
||||
* Find the ID in a given array. Return '0' of not there (amount).
|
||||
* Find the ID in a given array. Return '0' if not there (amount).
|
||||
*
|
||||
* @param array $array
|
||||
* @param int $entryId
|
||||
*
|
||||
* @return null|mixed
|
||||
*/
|
||||
protected function isInArray(array $array, int $entryId) // helper for data (math, calculations)
|
||||
protected function isInArray(array $array, int $entryId)
|
||||
{
|
||||
return $array[$entryId] ?? '0';
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the ID in a given array. Return null if not there (amount).
|
||||
*
|
||||
* @param array $array
|
||||
* @param int $entryId
|
||||
*
|
||||
* @return null|Carbon
|
||||
*/
|
||||
protected function isInArrayDate(array $array, int $entryId): ?Carbon
|
||||
{
|
||||
return $array[$entryId] ?? null;
|
||||
}
|
||||
}
|
||||
|
@@ -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],
|
||||
];
|
||||
}
|
||||
|
||||
|
@@ -249,7 +249,7 @@ class BudgetReportGenerator
|
||||
'budget_limits' => [],
|
||||
];
|
||||
|
||||
$noBudget = $this->nbRepository->sumExpenses($this->start, $this->end);
|
||||
$noBudget = $this->nbRepository->sumExpenses($this->start, $this->end, $this->accounts);
|
||||
foreach ($noBudget as $noBudgetEntry) {
|
||||
|
||||
// currency information:
|
||||
|
@@ -66,6 +66,11 @@ class CategoryReportGenerator
|
||||
{
|
||||
$earnedWith = $this->opsRepository->listIncome($this->start, $this->end, $this->accounts);
|
||||
$spentWith = $this->opsRepository->listExpenses($this->start, $this->end, $this->accounts);
|
||||
|
||||
// also transferred out and transferred into these accounts in this category:
|
||||
$transferredIn = $this->opsRepository->listTransferredIn($this->start, $this->end, $this->accounts);
|
||||
$transferredOut = $this->opsRepository->listTransferredOut($this->start, $this->end, $this->accounts);
|
||||
|
||||
$earnedWithout = $this->noCatRepository->listIncome($this->start, $this->end, $this->accounts);
|
||||
$spentWithout = $this->noCatRepository->listExpenses($this->start, $this->end, $this->accounts);
|
||||
|
||||
@@ -75,7 +80,7 @@ class CategoryReportGenerator
|
||||
];
|
||||
|
||||
// needs four for-each loops.
|
||||
foreach ([$earnedWith, $spentWith, $earnedWithout, $spentWithout] as $data) {
|
||||
foreach ([$earnedWith, $spentWith, $earnedWithout, $spentWithout, $transferredIn, $transferredOut] as $data) {
|
||||
$this->processOpsArray($data);
|
||||
}
|
||||
}
|
||||
|
@@ -156,7 +156,7 @@ class OperatorQuerySearch implements SearchInterface
|
||||
$parser = new QueryParser();
|
||||
try {
|
||||
$query1 = $parser->parse($query);
|
||||
} catch (TypeError|LogicException $e) {
|
||||
} catch (TypeError | LogicException $e) {
|
||||
Log::error($e->getMessage());
|
||||
Log::error(sprintf('Could not parse search: "%s".', $query));
|
||||
throw new FireflyException('Invalid search value. See the logs.', 0, $e);
|
||||
@@ -245,6 +245,7 @@ class OperatorQuerySearch implements SearchInterface
|
||||
private function handleSearchNode(Node $searchNode): void
|
||||
{
|
||||
$class = get_class($searchNode);
|
||||
Log::debug(sprintf('Now in handleSearchNode(%s)', $class));
|
||||
switch ($class) {
|
||||
default:
|
||||
Log::error(sprintf('Cannot handle node %s', $class));
|
||||
@@ -252,7 +253,7 @@ class OperatorQuerySearch implements SearchInterface
|
||||
case Subquery::class:
|
||||
// loop all notes in subquery:
|
||||
foreach ($searchNode->getNodes() as $subNode) { // @phpstan-ignore-line
|
||||
$this->handleSearchNode($subNode); // lets hope its not too recursive!
|
||||
$this->handleSearchNode($subNode); // let's hope it's not too recursive!
|
||||
}
|
||||
break;
|
||||
case Word::class:
|
||||
@@ -282,13 +283,15 @@ class OperatorQuerySearch implements SearchInterface
|
||||
'value' => (string)$value,
|
||||
]
|
||||
);
|
||||
} else {
|
||||
Log::debug(sprintf('Added operator type "%s"', $operator));
|
||||
}
|
||||
if (!in_array($operator, $this->validOperators, true)) {
|
||||
Log::debug(sprintf('Added INVALID operator type "%s"', $operator));
|
||||
$this->invalidOperators[] = [
|
||||
'type' => $operator,
|
||||
'value' => (string)$value,
|
||||
];
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -302,7 +305,7 @@ class OperatorQuerySearch implements SearchInterface
|
||||
*/
|
||||
private function updateCollector(string $operator, string $value): bool
|
||||
{
|
||||
Log::debug(sprintf('updateCollector("%s", "%s")', $operator, $value));
|
||||
Log::debug(sprintf('Now in updateCollector("%s", "%s")', $operator, $value));
|
||||
|
||||
// check if alias, replace if necessary:
|
||||
$operator = self::getRootOperator($operator);
|
||||
@@ -603,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));
|
||||
@@ -864,13 +834,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,
|
||||
];
|
||||
}
|
||||
|
||||
@@ -881,4 +850,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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -406,7 +406,7 @@ class Steam
|
||||
->get(['transactions.account_id', DB::raw('MAX(transaction_journals.date) AS max_date')]); // @phpstan-ignore-line
|
||||
|
||||
foreach ($set as $entry) {
|
||||
$date = new Carbon($entry->max_date, 'UTC');
|
||||
$date = new Carbon($entry->max_date, config('app.timezone'));
|
||||
$date->setTimezone(config('app.timezone'));
|
||||
$list[(int)$entry->account_id] = $date;
|
||||
}
|
||||
|
@@ -26,7 +26,11 @@ namespace FireflyIII\Support\System;
|
||||
|
||||
use Artisan;
|
||||
use Crypt;
|
||||
use FireflyIII\Exceptions\FireflyException;
|
||||
use Laravel\Passport\Console\KeysCommand;
|
||||
use Log;
|
||||
use Psr\Container\ContainerExceptionInterface;
|
||||
use Psr\Container\NotFoundExceptionInterface;
|
||||
|
||||
/**
|
||||
* Class OAuthKeys
|
||||
@@ -62,7 +66,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;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -101,8 +121,10 @@ class OAuthKeys
|
||||
*/
|
||||
public static function restoreKeysFromDB(): void
|
||||
{
|
||||
$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;
|
||||
$privateContent = Crypt::decrypt($privateKey);
|
||||
$publicContent = Crypt::decrypt($publicKey);
|
||||
$private = storage_path('oauth-private.key');
|
||||
$public = storage_path('oauth-public.key');
|
||||
file_put_contents($private, $privateContent);
|
||||
|
@@ -49,6 +49,7 @@ class General extends AbstractExtension
|
||||
$this->mimeIcon(),
|
||||
$this->markdown(),
|
||||
$this->floatval(),
|
||||
$this->phpHostName(),
|
||||
];
|
||||
}
|
||||
|
||||
@@ -91,6 +92,24 @@ class General extends AbstractExtension
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Show URL host name
|
||||
*
|
||||
* @return TwigFilter
|
||||
*/
|
||||
protected function phpHostName(): TwigFilter
|
||||
{
|
||||
return new TwigFilter(
|
||||
'phphost',
|
||||
static function (string $string): string {
|
||||
$proto = (string)parse_url($string, PHP_URL_SCHEME);
|
||||
$host = (string)parse_url($string, PHP_URL_HOST);
|
||||
|
||||
return e(sprintf('%s://%s', $proto, $host));
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to convert 1024 to 1kb etc.
|
||||
*
|
||||
|
@@ -60,9 +60,11 @@ class DeleteTransaction implements ActionInterface
|
||||
|
||||
// trigger delete factory:
|
||||
$journal = TransactionJournal::find($journal['transaction_group_id']);
|
||||
/** @var JournalDestroyService $service */
|
||||
$service = app(JournalDestroyService::class);
|
||||
$service->destroy($journal);
|
||||
if (null !== $journal) {
|
||||
/** @var JournalDestroyService $service */
|
||||
$service = app(JournalDestroyService::class);
|
||||
$service->destroy($journal);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@@ -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
|
||||
@@ -351,7 +363,7 @@ class TransactionGroupTransformer extends AbstractTransformer
|
||||
$bill = $this->getBill($journal->bill);
|
||||
|
||||
if (null !== $foreignAmount && null !== $foreignCurrency) {
|
||||
$foreignAmount = number_format((float)$foreignAmount, $foreignCurrency->decimal_places ?? 0, '.', '');
|
||||
$foreignAmount = number_format((float)$foreignAmount, $foreignCurrency['decimal_places'] ?? 0, '.', '');
|
||||
}
|
||||
|
||||
$longitude = null;
|
||||
|
@@ -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)) {
|
||||
@@ -67,8 +67,8 @@ trait TransferValidation
|
||||
return false;
|
||||
}
|
||||
|
||||
// otherwise try to find the account:
|
||||
$search = $this->findExistingAccount($validTypes, (int)$accountId, (string)$accountName);
|
||||
// or try to find the account:
|
||||
$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,22 +41,22 @@ 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)) {
|
||||
@@ -69,7 +69,7 @@ trait WithdrawalValidation
|
||||
}
|
||||
|
||||
// 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,14 +121,16 @@ 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)) {
|
||||
@@ -140,7 +143,7 @@ trait WithdrawalValidation
|
||||
}
|
||||
|
||||
// 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,22 +199,43 @@ 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 && '' !== $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 && '' !== $accountNumber) {
|
||||
$first = $this->accountRepository->findByAccountNumber($accountNumber, $validTypes);
|
||||
if ((null !== $first) && in_array($first->accountType->type, $validTypes, true)) {
|
||||
return $first;
|
||||
}
|
||||
}
|
||||
|
||||
// find by name:
|
||||
if ('' !== $accountName) {
|
||||
return $this->accountRepository->findByName($accountName, $validTypes);
|
||||
|
@@ -41,15 +41,13 @@ trait ValidatesBulkTransactionQuery
|
||||
protected function validateTransactionQuery(Validator $validator): void
|
||||
{
|
||||
$data = $validator->getData();
|
||||
// assumption is all validation has already taken place
|
||||
// and the query key exists.
|
||||
// assumption is all validation has already taken place and the query key exists.
|
||||
$json = json_decode($data['query'], true, 8, JSON_THROW_ON_ERROR);
|
||||
|
||||
if (array_key_exists('account_id', $json['where'])
|
||||
&& array_key_exists('account_id', $json['update'])
|
||||
) {
|
||||
// find both accounts
|
||||
// must be same type.
|
||||
// find both accounts, must be same type.
|
||||
// already validated: belongs to this user.
|
||||
$repository = app(AccountRepositoryInterface::class);
|
||||
$source = $repository->find((int)$json['where']['account_id']);
|
||||
|
@@ -105,7 +105,7 @@ trait RecurrenceValidation
|
||||
// validate destination account
|
||||
$destinationId = array_key_exists('destination_id', $transaction) ? (int)$transaction['destination_id'] : null;
|
||||
$destinationName = $transaction['destination_name'] ?? null;
|
||||
$validDestination = $accountValidator->validateDestination($destinationId, $destinationName, null);
|
||||
$validDestination = $accountValidator->validateDestination(['id' => $destinationId, 'name' => $destinationName,]);
|
||||
// do something with result:
|
||||
if (false === $validDestination) {
|
||||
$validator->errors()->add(sprintf('transactions.%d.destination_id', $index), $accountValidator->destError);
|
||||
|
@@ -42,7 +42,7 @@ trait TransactionValidation
|
||||
*/
|
||||
public function validateAccountInformation(Validator $validator): void
|
||||
{
|
||||
Log::debug('Now in validateAccountInformation()');
|
||||
Log::debug('Now in validateAccountInformation (TransactionValidation) ()');
|
||||
$transactions = $this->getTransactionsArray($validator);
|
||||
$data = $validator->getData();
|
||||
|
||||
@@ -100,10 +100,17 @@ trait TransactionValidation
|
||||
$accountValidator->setTransactionType($transactionType);
|
||||
|
||||
// validate source account.
|
||||
$sourceId = array_key_exists('source_id', $transaction) ? (int)$transaction['source_id'] : null;
|
||||
$sourceName = array_key_exists('source_name', $transaction) ? (string)$transaction['source_name'] : null;
|
||||
$sourceIban = array_key_exists('source_iban', $transaction) ? (string)$transaction['source_iban'] : null;
|
||||
$validSource = $accountValidator->validateSource($sourceId, $sourceName, $sourceIban);
|
||||
$sourceId = array_key_exists('source_id', $transaction) ? (int)$transaction['source_id'] : null;
|
||||
$sourceName = array_key_exists('source_name', $transaction) ? (string)$transaction['source_name'] : null;
|
||||
$sourceIban = array_key_exists('source_iban', $transaction) ? (string)$transaction['source_iban'] : null;
|
||||
$sourceNumber = array_key_exists('source_number', $transaction) ? (string)$transaction['source_number'] : null;
|
||||
$array = [
|
||||
'id' => $sourceId,
|
||||
'name' => $sourceName,
|
||||
'iban' => $sourceIban,
|
||||
'number' => $sourceNumber,
|
||||
];
|
||||
$validSource = $accountValidator->validateSource($array);
|
||||
|
||||
// do something with result:
|
||||
if (false === $validSource) {
|
||||
@@ -113,10 +120,17 @@ trait TransactionValidation
|
||||
return;
|
||||
}
|
||||
// validate destination account
|
||||
$destinationId = array_key_exists('destination_id', $transaction) ? (int)$transaction['destination_id'] : null;
|
||||
$destinationName = array_key_exists('destination_name', $transaction) ? (string)$transaction['destination_name'] : null;
|
||||
$destinationIban = array_key_exists('destination_iban', $transaction) ? (string)$transaction['destination_iban'] : null;
|
||||
$validDestination = $accountValidator->validateDestination($destinationId, $destinationName, $destinationIban);
|
||||
$destinationId = array_key_exists('destination_id', $transaction) ? (int)$transaction['destination_id'] : null;
|
||||
$destinationName = array_key_exists('destination_name', $transaction) ? (string)$transaction['destination_name'] : null;
|
||||
$destinationIban = array_key_exists('destination_iban', $transaction) ? (string)$transaction['destination_iban'] : null;
|
||||
$destinationNumber = array_key_exists('destination_number', $transaction) ? (string)$transaction['destination_number'] : null;
|
||||
$array = [
|
||||
'id' => $destinationId,
|
||||
'name' => $destinationName,
|
||||
'iban' => $destinationIban,
|
||||
'number' => $destinationNumber,
|
||||
];
|
||||
$validDestination = $accountValidator->validateDestination($array);
|
||||
// do something with result:
|
||||
if (false === $validDestination) {
|
||||
$validator->errors()->add(sprintf('transactions.%d.destination_id', $index), $accountValidator->destError);
|
||||
@@ -176,7 +190,7 @@ trait TransactionValidation
|
||||
Log::debug('Will try to validate source account information.');
|
||||
$sourceId = (int)($transaction['source_id'] ?? 0);
|
||||
$sourceName = $transaction['source_name'] ?? null;
|
||||
$validSource = $accountValidator->validateSource($sourceId, $sourceName, null);
|
||||
$validSource = $accountValidator->validateSource(['id' => $sourceId, 'name' => $sourceName]);
|
||||
|
||||
// do something with result:
|
||||
if (false === $validSource) {
|
||||
@@ -204,7 +218,8 @@ trait TransactionValidation
|
||||
}
|
||||
$destinationId = (int)($transaction['destination_id'] ?? 0);
|
||||
$destinationName = $transaction['destination_name'] ?? null;
|
||||
$validDestination = $accountValidator->validateDestination($destinationId, $destinationName, null);
|
||||
$array = ['id' => $destinationId, 'name' => $destinationName,];
|
||||
$validDestination = $accountValidator->validateDestination($array);
|
||||
// do something with result:
|
||||
if (false === $validDestination) {
|
||||
Log::warning('Looks like the destination account is not valid so complain to the user about it.');
|
||||
|
67
changelog.md
67
changelog.md
@@ -2,7 +2,72 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
This project adheres to [Semantic Versioning](http://semver.org/).
|
||||
|
||||
## 5.6.2 - 2021-10-xx
|
||||
## 5.6.8 - 2021-12-19
|
||||
|
||||
### Fixed
|
||||
- Account validation was broken in some edge cases, making it impossible to edit transactions.
|
||||
|
||||
## 5.6.7 - 2021-12-19
|
||||
|
||||
### Changed
|
||||
- Rewrote part of the transaction engine to improve account search.
|
||||
|
||||
### Fixed
|
||||
- Rule engine could not deal with dates in some edge cases, thanks @daften for the inspiration!
|
||||
- Several annoying layout v2 issues fixed.
|
||||
|
||||
## 5.6.6 - 2021-12-10
|
||||
|
||||
### Fixed
|
||||
- Cache is flushed after API action to delete a transaction.
|
||||
- Stricter validation of OAuth key existence.
|
||||
- [Issue 5371](https://github.com/firefly-iii/firefly-iii/issues/5371), thanks to @weimdall fixes budget issue.
|
||||
- Various v2 layout issues fixed: [issue 5375](https://github.com/firefly-iii/firefly-iii/issues/5375) [issue 5279](https://github.com/firefly-iii/firefly-iii/issues/5279) [issue 5108](https://github.com/firefly-iii/firefly-iii/issues/5108) [issue 5352](https://github.com/firefly-iii/firefly-iii/issues/5352) [issue 5335](https://github.com/firefly-iii/firefly-iii/issues/5335) [issue 5327](https://github.com/firefly-iii/firefly-iii/issues/5327) [issue 5296](https://github.com/firefly-iii/firefly-iii/issues/5296), thanks for reporting!
|
||||
|
||||
### API
|
||||
- Add field `has_attachments` to transaction splits
|
||||
|
||||
### Security
|
||||
- CSRF issue found by the good people of huntr.dev (@418sec)
|
||||
|
||||
## 5.6.5 - 2021-11-25
|
||||
|
||||
### Fixed
|
||||
- [Issue 5231](https://github.com/firefly-iii/firefly-iii/issues/5231) Various LDAP related fixes which I hope will allow you to use LDAP again.
|
||||
- [Issue 5288](https://github.com/firefly-iii/firefly-iii/issues/5288) Would generate new keys when not necessary.
|
||||
- [Issue 5245](https://github.com/firefly-iii/firefly-iii/issues/5245) Fix various weirdly formatted amounts.
|
||||
- [Issue 5240](https://github.com/firefly-iii/firefly-iii/issues/5240) Redirect issue when setting budget amounts.
|
||||
- [Issue 5325](https://github.com/firefly-iii/firefly-iii/issues/5325) Opening balance was always positive.
|
||||
|
||||
### Security
|
||||
- Various minor CSRF
|
||||
|
||||
## 5.6.4 - 2021-11-13
|
||||
|
||||
### Fixed
|
||||
- [Issue 5276](https://github.com/firefly-iii/firefly-iii/issues/5276) Sloppy release, this fixes an issue with logging out.
|
||||
|
||||
## 5.6.3 - 2021-11-12
|
||||
|
||||
### Changed
|
||||
- [Issue 5133](https://github.com/firefly-iii/firefly-iii/issues/5133) Several possible fixes for LDAP filter.
|
||||
|
||||
### Fixed
|
||||
- [Issue 5116](https://github.com/firefly-iii/firefly-iii/issues/5116) Fix missing icon
|
||||
- [Issue 5173](https://github.com/firefly-iii/firefly-iii/issues/5173) Firefly III would email too often about logins
|
||||
- [Issue 5178](https://github.com/firefly-iii/firefly-iii/issues/5178) Fix export parameter
|
||||
- [Issue 5179](https://github.com/firefly-iii/firefly-iii/issues/5179) Possible fix for issue with empty date strings
|
||||
- [Issue 5196](https://github.com/firefly-iii/firefly-iii/issues/5196) Fix issue with foreign amount formatting
|
||||
- [Issue 5200](https://github.com/firefly-iii/firefly-iii/issues/5200) Link bill to recurring transaction
|
||||
- [Issue 5218](https://github.com/firefly-iii/firefly-iii/issues/5218) Error in search
|
||||
- [Issue 5226](https://github.com/firefly-iii/firefly-iii/issues/5226) Could submit negative "skip" values
|
||||
- [Issue 5229](https://github.com/firefly-iii/firefly-iii/issues/5229) Fix serviceworker registration
|
||||
|
||||
|
||||
### Security
|
||||
- Logout is now POST and other minor CSRF issues.
|
||||
|
||||
## 5.6.2 - 2021-10-09
|
||||
|
||||
### Added
|
||||
- `/health` will return `200 OK` if Firefly III is up and running, thanks @ajgon!
|
||||
|
@@ -91,25 +91,25 @@
|
||||
"doctrine/dbal": "3.*",
|
||||
"fideloper/proxy": "4.*",
|
||||
"gdbots/query-parser": "^2.0",
|
||||
"guzzlehttp/guzzle": "^7.2",
|
||||
"guzzlehttp/guzzle": "^7.4",
|
||||
"jc5/google2fa-laravel": "2.0.6",
|
||||
"jc5/recovery": "^2",
|
||||
"laravel/framework": "^8.51",
|
||||
"laravel/framework": "^8.74",
|
||||
"laravel/passport": "10.*",
|
||||
"laravel/ui": "^3.0",
|
||||
"laravel/ui": "^3.4",
|
||||
"laravelcollective/html": "6.*",
|
||||
"league/commonmark": "2.*",
|
||||
"league/csv": "^9.6",
|
||||
"league/csv": "^9.7",
|
||||
"league/fractal": "0.*",
|
||||
"pragmarx/google2fa": "^8.0",
|
||||
"predis/predis": "^1.1",
|
||||
"psr/log": "<3",
|
||||
"ramsey/uuid": "^4.2",
|
||||
"rcrowe/twigbridge": "^0.12.1",
|
||||
"spatie/data-transfer-object": "^3.1",
|
||||
"psr/log": "<2"
|
||||
"spatie/data-transfer-object": "^3.1"
|
||||
},
|
||||
"require-dev": {
|
||||
"barryvdh/laravel-debugbar": "^3.3",
|
||||
"barryvdh/laravel-debugbar": "^3.6",
|
||||
"barryvdh/laravel-ide-helper": "2.*",
|
||||
"filp/whoops": "2.*",
|
||||
"fakerphp/faker": "1.*",
|
||||
|
1407
composer.lock
generated
1407
composer.lock
generated
File diff suppressed because it is too large
Load Diff
@@ -24,6 +24,19 @@ declare(strict_types=1);
|
||||
use FireflyIII\Ldap\AttributeHandler;
|
||||
use FireflyIII\Ldap\Rules\UserDefinedRule;
|
||||
|
||||
# select ldap model based on configuration option
|
||||
switch(env('LDAP_DIALECT')) {
|
||||
case 'ActiveDirectory':
|
||||
$ldapModel = class_exists(LdapRecord\Models\ActiveDirectory\User::class) ? LdapRecord\Models\ActiveDirectory\User::class : '';
|
||||
break;
|
||||
case 'FreeIPA':
|
||||
$ldapModel = class_exists(LdapRecord\Models\FreeIPA\User::class) ? LdapRecord\Models\FreeIPA\User::class : '';
|
||||
break;
|
||||
default:
|
||||
# default to openLDAP
|
||||
$ldapModel = class_exists(LdapRecord\Models\OpenLDAP\User::class) ? LdapRecord\Models\OpenLDAP\User::class : '';
|
||||
}
|
||||
|
||||
return [
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
@@ -108,8 +121,7 @@ return [
|
||||
|
||||
'ldap' => [
|
||||
'driver' => 'ldap',
|
||||
//'model' => LdapRecord\Models\ActiveDirectory\User::class,
|
||||
'model' => LdapRecord\Models\OpenLDAP\User::class,
|
||||
'model' => $ldapModel,
|
||||
'rules' => [
|
||||
UserDefinedRule::class,
|
||||
],
|
||||
|
@@ -101,8 +101,8 @@ return [
|
||||
'webhooks' => true,
|
||||
'handle_debts' => true,
|
||||
],
|
||||
'version' => '5.6.2',
|
||||
'api_version' => '1.5.4',
|
||||
'version' => '5.6.8',
|
||||
'api_version' => '1.5.5',
|
||||
'db_version' => 18,
|
||||
|
||||
// generic settings
|
||||
|
@@ -36,8 +36,8 @@ return [
|
||||
*/
|
||||
|
||||
'default' => env('LDAP_CONNECTION', 'default'),
|
||||
|
||||
'group_filter' => env('LDAP_GROUP_FILTER'),
|
||||
'extra_filter' => env('LDAP_EXTRA_FILTER'),
|
||||
'dialect' => env('LDAP_DIALECT'),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
|
@@ -11,19 +11,19 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"axios": "^0.21",
|
||||
"date-fns": "^2.21.1",
|
||||
"date-fns": "^2.27.0",
|
||||
"laravel-mix": "^6",
|
||||
"lodash": "^4.17.21",
|
||||
"lodash.clonedeep": "^4.5.0",
|
||||
"postcss": "^8.1.14",
|
||||
"postcss": "^8.4.5",
|
||||
"resolve-url-loader": "^4.0.0",
|
||||
"sass": "^1.39.2",
|
||||
"sass-loader": "^12.0.0",
|
||||
"vue-i18n": "^8.24.2",
|
||||
"sass": "^1.45.0",
|
||||
"sass-loader": "^12.2.0",
|
||||
"vue-i18n": "^8.26.7",
|
||||
"vue-loader": "^15",
|
||||
"vue-template-compiler": "^2.6.12",
|
||||
"vuex": "^3.6.2",
|
||||
"webpack": "^5.52.1"
|
||||
"webpack": "^5.64.4"
|
||||
},
|
||||
"dependencies": {
|
||||
"@fortawesome/fontawesome-free": "^5.15.3",
|
||||
@@ -32,7 +32,7 @@
|
||||
"axios-cache-adapter": "^2.7.3",
|
||||
"bootstrap": "^4.6.0",
|
||||
"bootstrap-vue": "^2.21.2",
|
||||
"chart.js": "^3.4.0",
|
||||
"chart.js": "^3.6.2",
|
||||
"icheck-bootstrap": "^3.0.1",
|
||||
"jquery-ui": "^1.12.1",
|
||||
"leaflet": "^1.7.1",
|
||||
@@ -40,7 +40,7 @@
|
||||
"localforage-memoryStorageDriver": "^0.9.2",
|
||||
"overlayscrollbars": "^1.13.1",
|
||||
"sortablejs": "^1.14.0",
|
||||
"uiv": "^1.3.1",
|
||||
"uiv": "^1.4.1",
|
||||
"v-calendar": "^2.3.2",
|
||||
"vue-typeahead-bootstrap": "^2.8.0",
|
||||
"vue2-leaflet": "^2.7.1"
|
||||
|
@@ -97,7 +97,7 @@
|
||||
|
||||
<GenericLocation :disabled="submitting" v-model="location" :title="$t('form.location')" :errors="errors.location"
|
||||
v-on:set-field="storeField($event)"/>
|
||||
|
||||
<!-- attachments -->
|
||||
<GenericAttachments :disabled="submitting" :title="$t('form.attachments')" field-name="attachments" :errors="errors.attachments"
|
||||
v-on:selected-attachments="selectedAttachments($event)"
|
||||
v-on:selected-no-attachments="selectedNoAttachments($event)"
|
||||
@@ -275,6 +275,8 @@ export default {
|
||||
this.hasAttachments = false;
|
||||
},
|
||||
uploadedAttachments: function (e) {
|
||||
// console.log('Response to event uploaded-attachments');
|
||||
// console.log(e);
|
||||
this.finishSubmission();
|
||||
},
|
||||
submitForm: function (e) {
|
||||
|
@@ -63,7 +63,7 @@
|
||||
</div>
|
||||
<div class="card-footer">
|
||||
<button @click="deleteAccount" class="btn btn-danger float-right" v-if="!loading && !deleting && !deleted"> {{
|
||||
$t('firefly.delete_account')
|
||||
$t('form.deletePermanently')
|
||||
}}
|
||||
</button>
|
||||
</div>
|
||||
|
@@ -177,13 +177,9 @@
|
||||
import {mapGetters, mapMutations} from "vuex";
|
||||
import Sortable from "sortablejs";
|
||||
import format from "date-fns/format";
|
||||
// import {setup} from 'axios-cache-adapter';
|
||||
// import {cacheAdapterEnhancer} from 'axios-extensions';
|
||||
import {configureAxios} from "../../shared/forageStore";
|
||||
|
||||
|
||||
// get all and cache, dont keep the table busy.
|
||||
|
||||
export default {
|
||||
name: "Index",
|
||||
props: {
|
||||
|
@@ -22,45 +22,20 @@
|
||||
<div>
|
||||
<div class="row">
|
||||
<div class="col-lg-12 col-md-6 col-sm-12 col-xs-12">
|
||||
<!-- Custom Tabs -->
|
||||
<div class="card">
|
||||
<div class="card-header d-flex p-0">
|
||||
<h3 class="card-title p-3">Tabs</h3>
|
||||
<ul class="nav nav-pills ml-auto p-2">
|
||||
<li class="nav-item"><a class="nav-link active" href="#main_chart" data-toggle="tab">Chart</a></li>
|
||||
<li class="nav-item"><a class="nav-link" href="#budgets" data-toggle="tab">Budgets</a></li>
|
||||
<li class="nav-item"><a class="nav-link" href="#categories" data-toggle="tab">Categories</a></li>
|
||||
</ul>
|
||||
</div><!-- /.card-header -->
|
||||
<div class="card-body">
|
||||
<div class="tab-content">
|
||||
<div class="tab-pane active" id="main_chart">
|
||||
1: main chart
|
||||
</div>
|
||||
<!-- /.tab-pane -->
|
||||
<div class="tab-pane" id="budgets">
|
||||
2: tree map from/to budget
|
||||
</div>
|
||||
<!-- /.tab-pane -->
|
||||
<div class="tab-pane" id="categories">
|
||||
2: tree map from/to cat
|
||||
</div>
|
||||
<!-- /.tab-pane -->
|
||||
</div>
|
||||
<!-- /.tab-content -->
|
||||
</div><!-- /.card-body -->
|
||||
</div>
|
||||
<!-- ./card -->
|
||||
<!-- Custom Tabs will be put here (see file history). -->
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<TransactionListLarge
|
||||
:entries="rawTransactions"
|
||||
:isEmpty="isEmpty"
|
||||
:page="currentPage"
|
||||
ref="list"
|
||||
:total="total"
|
||||
:per-page="perPage"
|
||||
:sort-desc="sortDesc"
|
||||
v-on:jump-page="jumpToPage($event)"
|
||||
v-on:refreshed-cache-key="refreshedKey"
|
||||
/>
|
||||
<div class="row">
|
||||
<div class="col-lg-12 col-md-6 col-sm-12 col-xs-12">
|
||||
@@ -81,8 +56,8 @@
|
||||
|
||||
<script>
|
||||
import TransactionListLarge from "../transactions/TransactionListLarge";
|
||||
import format from "date-fns/format";
|
||||
import {mapGetters} from "vuex";
|
||||
import format from "date-fns/format";
|
||||
import {configureAxios} from "../../shared/forageStore";
|
||||
|
||||
export default {
|
||||
@@ -106,7 +81,8 @@ export default {
|
||||
perPage: 51,
|
||||
locale: 'en-US',
|
||||
api: null,
|
||||
nameLoading:false
|
||||
nameLoading: false,
|
||||
isEmpty: false
|
||||
}
|
||||
},
|
||||
created() {
|
||||
@@ -132,42 +108,55 @@ export default {
|
||||
.then(response => {
|
||||
let start = new Intl.DateTimeFormat(this.locale, {year: 'numeric', month: 'long', day: 'numeric'}).format(this.start);
|
||||
let end = new Intl.DateTimeFormat(this.locale, {year: 'numeric', month: 'long', day: 'numeric'}).format(this.end);
|
||||
document.getElementById('page-subTitle').innerText = this.$t('firefly.journals_in_period_for_account_js', {start: start, end: end, title: response.data.data.attributes.name});
|
||||
document.getElementById('page-subTitle').innerText = this.$t('firefly.journals_in_period_for_account_js', {
|
||||
start: start,
|
||||
end: end,
|
||||
title: response.data.data.attributes.name
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
}
|
||||
},
|
||||
refreshedKey: function () {
|
||||
this.loading = false;
|
||||
this.getTransactions();
|
||||
this.updatePageTitle();
|
||||
},
|
||||
getTransactions: function () {
|
||||
// console.log('getTransactions()');
|
||||
if (this.showReady && !this.loading) {
|
||||
|
||||
|
||||
// console.log('Show ready, not loading, go for download');
|
||||
this.loading = true;
|
||||
this.rawTransactions = [];
|
||||
configureAxios().then(async (api) => {
|
||||
// console.log('Now getTransactions() x Start');
|
||||
let startStr = format(this.start, 'y-MM-dd');
|
||||
let endStr = format(this.end, 'y-MM-dd');
|
||||
this.rawTransactions = [];
|
||||
|
||||
let url = './api/v1/accounts/' + this.accountId + '/transactions?page=1&limit=' + this.perPage + '&start=' + startStr + '&end=' + endStr;
|
||||
|
||||
let url = './api/v1/accounts/' + this.accountId + '/transactions?page=' + this.currentPage + '&limit=' + this.perPage + '&start=' + startStr + '&end=' + endStr + '&cache=' + this.cacheKey;
|
||||
|
||||
api.get(url)
|
||||
.then(response => {
|
||||
// console.log('Now getTransactions() DONE!');
|
||||
this.total = parseInt(response.data.meta.pagination.total);
|
||||
if (0 === this.total) {
|
||||
this.isEmpty = true;
|
||||
}
|
||||
// let transactions = response.data.data;
|
||||
// console.log('Have downloaded ' + transactions.length + ' transactions');
|
||||
// console.log(response.data);
|
||||
this.rawTransactions = response.data.data;
|
||||
this.loading = false;
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
},
|
||||
jumpToPage: function (event) {
|
||||
// console.log('noticed a change!');
|
||||
// console.log('noticed a change in account/show!');
|
||||
this.currentPage = event.page;
|
||||
this.downloadTransactionList(event.page);
|
||||
this.getTransactions();
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
|
@@ -23,93 +23,131 @@
|
||||
<Alert :message="errorMessage" type="danger"/>
|
||||
<Alert :message="successMessage" type="success"/>
|
||||
<form @submit="submitForm" autocomplete="off">
|
||||
<div class="row">
|
||||
<div class="col-xl-6 col-lg-6 col-md-12 col-sm-12 col-xs-12">
|
||||
<div class="card card-primary">
|
||||
<div class="card-header">
|
||||
<h3 class="card-title">
|
||||
{{ $t('firefly.mandatoryFields') }}
|
||||
</h3>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<GenericTextInput :disabled="submitting" v-model="name" field-name="name" :errors="errors.name" :title="$t('form.name')"
|
||||
v-on:set-field="storeField($event)"/>
|
||||
<GenericCurrency :disabled="submitting" v-model="currency_id" :errors="errors.currency_id" v-on:set-field="storeField($event)"/>
|
||||
|
||||
<GenericTextInput :disabled="submitting" field-type="number" field-step="any" v-model="amount_min"
|
||||
field-name="amount_min" :errors="errors.amount_min" :title="$t('form.amount_min')" v-on:set-field="storeField($event)"/>
|
||||
|
||||
<GenericTextInput :disabled="submitting" field-type="number" field-step="any" v-model="amount_max"
|
||||
field-name="amount_max" :errors="errors.amount_max" :title="$t('form.amount_max')" v-on:set-field="storeField($event)"/>
|
||||
|
||||
<GenericTextInput :disabled="submitting" field-type="date" v-model="date" field-name="date"
|
||||
:errors="errors.date" :title="$t('form.startdate')" v-on:set-field="storeField($event)"/>
|
||||
<GenericTextInput :disabled="submitting" field-type="date" v-model="end_date" field-name="end_date"
|
||||
:errors="errors.end_date" :title="$t('form.end_date')" v-on:set-field="storeField($event)"/>
|
||||
<GenericTextInput :disabled="submitting" field-type="date" v-model="extension_date" field-name="extension_date"
|
||||
:errors="errors.extension_date" :title="$t('form.extension_date')" v-on:set-field="storeField($event)"/>
|
||||
|
||||
<RepeatFrequencyPeriod :disabled="submitting" v-model="repeat_freq" :errors="errors.repeat_freq"
|
||||
v-on:set-field="storeField($event)"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-xl-6 col-lg-6 col-md-12 col-sm-12 col-xs-12">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h3 class="card-title">
|
||||
{{ $t('firefly.optionalFields') }}
|
||||
</h3>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<GenericTextarea :disabled="submitting" field-name="notes" :title="$t('form.notes')" v-model="notes" :errors="errors.notes"
|
||||
v-on:set-field="storeField($event)"/>
|
||||
|
||||
<GenericAttachments :disabled="submitting" :title="$t('form.attachments')" field-name="attachments" :errors="errors.attachments"
|
||||
v-on:selected-attachments="selectedAttachments($event)"
|
||||
v-on:selected-no-attachments="selectedNoAttachments($event)"
|
||||
v-on:uploaded-attachments="uploadedAttachments($event)"
|
||||
:upload-trigger="uploadTrigger"
|
||||
:upload-object-type="uploadObjectType"
|
||||
:upload-object-id="uploadObjectId"
|
||||
/>
|
||||
|
||||
<GenericTextInput :disabled="submitting" v-model="skip" field-name="skip" :errors="errors.skip" :title="$t('form.skip')"
|
||||
v-on:set-field="storeField($event)"/>
|
||||
|
||||
<GenericGroup :disabled="submitting" v-model="group_title" field-name="group_title" :errors="errors.group_title" :title="$t('form.object_group')"
|
||||
v-on:set-field="storeField($event)"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col">
|
||||
<!-- tabs -->
|
||||
<ul class="nav nav-tabs ml-auto p-2" id="subscriptionTabs">
|
||||
<li>
|
||||
<li class="nav-item"><a class="nav-link active" href="#subscription" data-toggle="pill">
|
||||
Subscription
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<li class="nav-item"><a class="nav-link" href="#rule" data-toggle="pill">
|
||||
Rule
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-xl-6 col-lg-6 col-md-12 col-sm-12 col-xs-12 offset-xl-6 offset-lg-6">
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<div class="row">
|
||||
<div class="col-lg-6 offset-lg-6">
|
||||
<button :disabled=submitting type="button" @click="submitForm" class="btn btn-success btn-block">{{
|
||||
$t('firefly.store_new_bill')
|
||||
}}
|
||||
</button>
|
||||
<div class="form-check">
|
||||
<input id="createAnother" v-model="createAnother" class="form-check-input" type="checkbox">
|
||||
<label class="form-check-label" for="createAnother">
|
||||
<span class="small">{{ $t('firefly.create_another') }}</span>
|
||||
</label>
|
||||
</div>
|
||||
<div class="form-check">
|
||||
<input id="resetFormAfter" v-model="resetFormAfter" :disabled="!createAnother" class="form-check-input" type="checkbox">
|
||||
<label class="form-check-label" for="resetFormAfter">
|
||||
<span class="small">{{ $t('firefly.reset_after') }}</span>
|
||||
</label>
|
||||
<div class="tab-content" id="subscriptionTabContent">
|
||||
<div class="tab-pane show active" id="subscription" role="tabpanel" aria-labelledby="subscription-tab">
|
||||
<div class="row">
|
||||
<div class="col-xl-6 col-lg-6 col-md-12 col-sm-12 col-xs-12">
|
||||
<div class="card card-primary">
|
||||
<div class="card-header">
|
||||
<h3 class="card-title">
|
||||
{{ $t('firefly.mandatoryFields') }}
|
||||
</h3>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<GenericTextInput :disabled="submitting" v-model="name" field-name="name" :errors="errors.name" :title="$t('form.name')"
|
||||
v-on:set-field="storeField($event)"/>
|
||||
<GenericCurrency :disabled="submitting" v-model="currency_id" :errors="errors.currency_id" v-on:set-field="storeField($event)"/>
|
||||
|
||||
<GenericTextInput :disabled="submitting" field-type="number" field-step="any" v-model="amount_min"
|
||||
field-name="amount_min" :errors="errors.amount_min" :title="$t('form.amount_min')" v-on:set-field="storeField($event)"/>
|
||||
|
||||
<GenericTextInput :disabled="submitting" field-type="number" field-step="any" v-model="amount_max"
|
||||
field-name="amount_max" :errors="errors.amount_max" :title="$t('form.amount_max')" v-on:set-field="storeField($event)"/>
|
||||
|
||||
<GenericTextInput :disabled="submitting" field-type="date" v-model="date" field-name="date"
|
||||
:errors="errors.date" :title="$t('form.startdate')" v-on:set-field="storeField($event)"/>
|
||||
<GenericTextInput :disabled="submitting" field-type="date" v-model="end_date" field-name="end_date"
|
||||
:errors="errors.end_date" :title="$t('form.end_date')" v-on:set-field="storeField($event)"/>
|
||||
<GenericTextInput :disabled="submitting" field-type="date" v-model="extension_date" field-name="extension_date"
|
||||
:errors="errors.extension_date" :title="$t('form.extension_date')" v-on:set-field="storeField($event)"/>
|
||||
|
||||
<RepeatFrequencyPeriod :disabled="submitting" v-model="repeat_freq" :errors="errors.repeat_freq"
|
||||
v-on:set-field="storeField($event)"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-xl-6 col-lg-6 col-md-12 col-sm-12 col-xs-12">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h3 class="card-title">
|
||||
{{ $t('firefly.optionalFields') }}
|
||||
</h3>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<GenericTextarea :disabled="submitting" field-name="notes" :title="$t('form.notes')" v-model="notes" :errors="errors.notes"
|
||||
v-on:set-field="storeField($event)"/>
|
||||
|
||||
<GenericAttachments :disabled="submitting" :title="$t('form.attachments')" field-name="attachments" :errors="errors.attachments"
|
||||
v-on:selected-attachments="selectedAttachments($event)"
|
||||
v-on:selected-no-attachments="selectedNoAttachments($event)"
|
||||
v-on:uploaded-attachments="uploadedAttachments($event)"
|
||||
:upload-trigger="uploadTrigger"
|
||||
:upload-object-type="uploadObjectType"
|
||||
:upload-object-id="uploadObjectId"
|
||||
/>
|
||||
|
||||
<GenericTextInput :disabled="submitting" v-model="skip" field-name="skip" :errors="errors.skip" :title="$t('form.skip')"
|
||||
v-on:set-field="storeField($event)"/>
|
||||
|
||||
<GenericGroup :disabled="submitting" v-model="group_title" field-name="group_title" :errors="errors.group_title"
|
||||
:title="$t('form.object_group')"
|
||||
v-on:set-field="storeField($event)"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-xl-6 col-lg-6 col-md-12 col-sm-12 col-xs-12 offset-xl-6 offset-lg-6">
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<div class="row">
|
||||
<div class="col-lg-6 offset-lg-6">
|
||||
<button :disabled=submitting type="button" @click="submitForm" class="btn btn-success btn-block">{{
|
||||
$t('firefly.store_new_bill')
|
||||
}}
|
||||
</button>
|
||||
<div class="form-check">
|
||||
<input id="createAnother" v-model="createAnother" class="form-check-input" type="checkbox">
|
||||
<label class="form-check-label" for="createAnother">
|
||||
<span class="small">{{ $t('firefly.create_another') }}</span>
|
||||
</label>
|
||||
</div>
|
||||
<div class="form-check">
|
||||
<input id="resetFormAfter" v-model="resetFormAfter" :disabled="!createAnother" class="form-check-input" type="checkbox">
|
||||
<label class="form-check-label" for="resetFormAfter">
|
||||
<span class="small">{{ $t('firefly.reset_after') }}</span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="tab-pane show" id="rule" role="tabpanel" aria-labelledby="rule-tab">
|
||||
<div class="row">
|
||||
<div class="col-xl-6 col-lg-6 col-md-12 col-sm-12 col-xs-12">
|
||||
<div class="card card-primary">
|
||||
<div class="card-header">
|
||||
<h3 class="card-title">
|
||||
Title
|
||||
</h3>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<p>
|
||||
In the future here you can set rule info for the new subscription.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
@@ -226,7 +264,7 @@ export default {
|
||||
this.uploadObjectId = this.returnedId;
|
||||
this.uploadTrigger = true;
|
||||
}
|
||||
if(!this.hasAttachments) {
|
||||
if (!this.hasAttachments) {
|
||||
this.finishSubmission();
|
||||
}
|
||||
})
|
||||
@@ -236,10 +274,10 @@ export default {
|
||||
// display errors!
|
||||
});
|
||||
},
|
||||
uploadedAttachments: function(e) {
|
||||
uploadedAttachments: function (e) {
|
||||
this.finishSubmission();
|
||||
},
|
||||
finishSubmission: function() {
|
||||
finishSubmission: function () {
|
||||
this.successMessage = this.$t('firefly.stored_new_bill_js', {ID: this.returnedId, name: this.returnedTitle});
|
||||
// stay here is false?
|
||||
if (false === this.createAnother) {
|
||||
@@ -294,6 +332,3 @@ export default {
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user