Merge branch 'release/6.0.0-alpha.2'

This commit is contained in:
James Cole 2023-02-04 08:24:24 +01:00
commit 3a0b3f46a5
396 changed files with 8437 additions and 7603 deletions

View File

@ -226,30 +226,30 @@
},
{
"name": "doctrine/annotations",
"version": "1.14.2",
"version": "2.0.1",
"source": {
"type": "git",
"url": "https://github.com/doctrine/annotations.git",
"reference": "ad785217c1e9555a7d6c6c8c9f406395a5e2882b"
"reference": "e157ef3f3124bbf6fe7ce0ffd109e8a8ef284e7f"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/doctrine/annotations/zipball/ad785217c1e9555a7d6c6c8c9f406395a5e2882b",
"reference": "ad785217c1e9555a7d6c6c8c9f406395a5e2882b",
"url": "https://api.github.com/repos/doctrine/annotations/zipball/e157ef3f3124bbf6fe7ce0ffd109e8a8ef284e7f",
"reference": "e157ef3f3124bbf6fe7ce0ffd109e8a8ef284e7f",
"shasum": ""
},
"require": {
"doctrine/lexer": "^1 || ^2",
"doctrine/lexer": "^2 || ^3",
"ext-tokenizer": "*",
"php": "^7.1 || ^8.0",
"php": "^7.2 || ^8.0",
"psr/cache": "^1 || ^2 || ^3"
},
"require-dev": {
"doctrine/cache": "^1.11 || ^2.0",
"doctrine/coding-standard": "^9 || ^10",
"phpstan/phpstan": "~1.4.10 || ^1.8.0",
"doctrine/cache": "^2.0",
"doctrine/coding-standard": "^10",
"phpstan/phpstan": "^1.8.0",
"phpunit/phpunit": "^7.5 || ^8.5 || ^9.5",
"symfony/cache": "^4.4 || ^5.4 || ^6",
"symfony/cache": "^5.4 || ^6",
"vimeo/psalm": "^4.10"
},
"suggest": {
@ -296,77 +296,33 @@
],
"support": {
"issues": "https://github.com/doctrine/annotations/issues",
"source": "https://github.com/doctrine/annotations/tree/1.14.2"
"source": "https://github.com/doctrine/annotations/tree/2.0.1"
},
"time": "2022-12-15T06:48:22+00:00"
},
{
"name": "doctrine/deprecations",
"version": "v1.0.0",
"source": {
"type": "git",
"url": "https://github.com/doctrine/deprecations.git",
"reference": "0e2a4f1f8cdfc7a92ec3b01c9334898c806b30de"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/doctrine/deprecations/zipball/0e2a4f1f8cdfc7a92ec3b01c9334898c806b30de",
"reference": "0e2a4f1f8cdfc7a92ec3b01c9334898c806b30de",
"shasum": ""
},
"require": {
"php": "^7.1|^8.0"
},
"require-dev": {
"doctrine/coding-standard": "^9",
"phpunit/phpunit": "^7.5|^8.5|^9.5",
"psr/log": "^1|^2|^3"
},
"suggest": {
"psr/log": "Allows logging deprecations via PSR-3 logger implementation"
},
"type": "library",
"autoload": {
"psr-4": {
"Doctrine\\Deprecations\\": "lib/Doctrine/Deprecations"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"description": "A small layer on top of trigger_error(E_USER_DEPRECATED) or PSR-3 logging with options to disable all deprecations or selectively for packages.",
"homepage": "https://www.doctrine-project.org/",
"support": {
"issues": "https://github.com/doctrine/deprecations/issues",
"source": "https://github.com/doctrine/deprecations/tree/v1.0.0"
},
"time": "2022-05-02T15:47:09+00:00"
"time": "2023-02-02T22:02:53+00:00"
},
{
"name": "doctrine/lexer",
"version": "2.1.0",
"version": "3.0.0",
"source": {
"type": "git",
"url": "https://github.com/doctrine/lexer.git",
"reference": "39ab8fcf5a51ce4b85ca97c7a7d033eb12831124"
"reference": "84a527db05647743d50373e0ec53a152f2cde568"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/doctrine/lexer/zipball/39ab8fcf5a51ce4b85ca97c7a7d033eb12831124",
"reference": "39ab8fcf5a51ce4b85ca97c7a7d033eb12831124",
"url": "https://api.github.com/repos/doctrine/lexer/zipball/84a527db05647743d50373e0ec53a152f2cde568",
"reference": "84a527db05647743d50373e0ec53a152f2cde568",
"shasum": ""
},
"require": {
"doctrine/deprecations": "^1.0",
"php": "^7.1 || ^8.0"
"php": "^8.1"
},
"require-dev": {
"doctrine/coding-standard": "^9 || ^10",
"phpstan/phpstan": "^1.3",
"phpunit/phpunit": "^7.5 || ^8.5 || ^9.5",
"doctrine/coding-standard": "^10",
"phpstan/phpstan": "^1.9",
"phpunit/phpunit": "^9.5",
"psalm/plugin-phpunit": "^0.18.3",
"vimeo/psalm": "^4.11 || ^5.0"
"vimeo/psalm": "^5.0"
},
"type": "library",
"autoload": {
@ -403,7 +359,7 @@
],
"support": {
"issues": "https://github.com/doctrine/lexer/issues",
"source": "https://github.com/doctrine/lexer/tree/2.1.0"
"source": "https://github.com/doctrine/lexer/tree/3.0.0"
},
"funding": [
{
@ -419,26 +375,27 @@
"type": "tidelift"
}
],
"time": "2022-12-14T08:49:07+00:00"
"time": "2022-12-15T16:57:16+00:00"
},
{
"name": "friendsofphp/php-cs-fixer",
"version": "v3.13.2",
"version": "v3.14.3",
"source": {
"type": "git",
"url": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer.git",
"reference": "3952f08a81bd3b1b15e11c3de0b6bf037faa8496"
"reference": "b418036b95b4936a33fe906245d3044395935e73"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/PHP-CS-Fixer/PHP-CS-Fixer/zipball/3952f08a81bd3b1b15e11c3de0b6bf037faa8496",
"reference": "3952f08a81bd3b1b15e11c3de0b6bf037faa8496",
"url": "https://api.github.com/repos/PHP-CS-Fixer/PHP-CS-Fixer/zipball/b418036b95b4936a33fe906245d3044395935e73",
"reference": "b418036b95b4936a33fe906245d3044395935e73",
"shasum": ""
},
"require": {
"composer/semver": "^3.2",
"composer/semver": "^3.3",
"composer/xdebug-handler": "^3.0.3",
"doctrine/annotations": "^1.13",
"doctrine/annotations": "^2",
"doctrine/lexer": "^2 || ^3",
"ext-json": "*",
"ext-tokenizer": "*",
"php": "^7.4 || ^8.0",
@ -448,26 +405,26 @@
"symfony/filesystem": "^5.4 || ^6.0",
"symfony/finder": "^5.4 || ^6.0",
"symfony/options-resolver": "^5.4 || ^6.0",
"symfony/polyfill-mbstring": "^1.23",
"symfony/polyfill-php80": "^1.25",
"symfony/polyfill-php81": "^1.25",
"symfony/polyfill-mbstring": "^1.27",
"symfony/polyfill-php80": "^1.27",
"symfony/polyfill-php81": "^1.27",
"symfony/process": "^5.4 || ^6.0",
"symfony/stopwatch": "^5.4 || ^6.0"
},
"require-dev": {
"justinrainbow/json-schema": "^5.2",
"keradus/cli-executor": "^2.0",
"mikey179/vfsstream": "^1.6.10",
"php-coveralls/php-coveralls": "^2.5.2",
"mikey179/vfsstream": "^1.6.11",
"php-coveralls/php-coveralls": "^2.5.3",
"php-cs-fixer/accessible-object": "^1.1",
"php-cs-fixer/phpunit-constraint-isidenticalstring": "^1.2",
"php-cs-fixer/phpunit-constraint-xmlmatchesxsd": "^1.2.1",
"phpspec/prophecy": "^1.15",
"phpspec/prophecy": "^1.16",
"phpspec/prophecy-phpunit": "^2.0",
"phpunit/phpunit": "^9.5",
"phpunitgoodpractices/polyfill": "^1.6",
"phpunitgoodpractices/traits": "^1.9.2",
"symfony/phpunit-bridge": "^6.0",
"symfony/phpunit-bridge": "^6.2.3",
"symfony/yaml": "^5.4 || ^6.0"
},
"suggest": {
@ -500,7 +457,7 @@
"description": "A tool to automatically fix PHP code style",
"support": {
"issues": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/issues",
"source": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/tree/v3.13.2"
"source": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/tree/v3.14.3"
},
"funding": [
{
@ -508,7 +465,7 @@
"type": "github"
}
],
"time": "2023-01-02T23:53:50+00:00"
"time": "2023-01-30T00:24:29+00:00"
},
{
"name": "psr/cache",
@ -780,16 +737,16 @@
},
{
"name": "symfony/console",
"version": "v6.2.3",
"version": "v6.2.5",
"source": {
"type": "git",
"url": "https://github.com/symfony/console.git",
"reference": "0f579613e771dba2dbb8211c382342a641f5da06"
"reference": "3e294254f2191762c1d137aed4b94e966965e985"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/console/zipball/0f579613e771dba2dbb8211c382342a641f5da06",
"reference": "0f579613e771dba2dbb8211c382342a641f5da06",
"url": "https://api.github.com/repos/symfony/console/zipball/3e294254f2191762c1d137aed4b94e966965e985",
"reference": "3e294254f2191762c1d137aed4b94e966965e985",
"shasum": ""
},
"require": {
@ -856,7 +813,7 @@
"terminal"
],
"support": {
"source": "https://github.com/symfony/console/tree/v6.2.3"
"source": "https://github.com/symfony/console/tree/v6.2.5"
},
"funding": [
{
@ -872,7 +829,7 @@
"type": "tidelift"
}
],
"time": "2022-12-28T14:26:22+00:00"
"time": "2023-01-01T08:38:09+00:00"
},
{
"name": "symfony/deprecation-contracts",
@ -943,16 +900,16 @@
},
{
"name": "symfony/event-dispatcher",
"version": "v6.2.2",
"version": "v6.2.5",
"source": {
"type": "git",
"url": "https://github.com/symfony/event-dispatcher.git",
"reference": "3ffeb31139b49bf6ef0bc09d1db95eac053388d1"
"reference": "f02d108b5e9fd4a6245aa73a9d2df2ec060c3e68"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/3ffeb31139b49bf6ef0bc09d1db95eac053388d1",
"reference": "3ffeb31139b49bf6ef0bc09d1db95eac053388d1",
"url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/f02d108b5e9fd4a6245aa73a9d2df2ec060c3e68",
"reference": "f02d108b5e9fd4a6245aa73a9d2df2ec060c3e68",
"shasum": ""
},
"require": {
@ -1006,7 +963,7 @@
"description": "Provides tools that allow your application components to communicate with each other by dispatching events and listening to them",
"homepage": "https://symfony.com",
"support": {
"source": "https://github.com/symfony/event-dispatcher/tree/v6.2.2"
"source": "https://github.com/symfony/event-dispatcher/tree/v6.2.5"
},
"funding": [
{
@ -1022,7 +979,7 @@
"type": "tidelift"
}
],
"time": "2022-12-14T16:11:27+00:00"
"time": "2023-01-01T08:38:09+00:00"
},
{
"name": "symfony/event-dispatcher-contracts",
@ -1105,16 +1062,16 @@
},
{
"name": "symfony/filesystem",
"version": "v6.2.0",
"version": "v6.2.5",
"source": {
"type": "git",
"url": "https://github.com/symfony/filesystem.git",
"reference": "50b2523c874605cf3d4acf7a9e2b30b6a440a016"
"reference": "e59e8a4006afd7f5654786a83b4fcb8da98f4593"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/filesystem/zipball/50b2523c874605cf3d4acf7a9e2b30b6a440a016",
"reference": "50b2523c874605cf3d4acf7a9e2b30b6a440a016",
"url": "https://api.github.com/repos/symfony/filesystem/zipball/e59e8a4006afd7f5654786a83b4fcb8da98f4593",
"reference": "e59e8a4006afd7f5654786a83b4fcb8da98f4593",
"shasum": ""
},
"require": {
@ -1148,7 +1105,7 @@
"description": "Provides basic utilities for the filesystem",
"homepage": "https://symfony.com",
"support": {
"source": "https://github.com/symfony/filesystem/tree/v6.2.0"
"source": "https://github.com/symfony/filesystem/tree/v6.2.5"
},
"funding": [
{
@ -1164,20 +1121,20 @@
"type": "tidelift"
}
],
"time": "2022-11-20T13:01:27+00:00"
"time": "2023-01-20T17:45:48+00:00"
},
{
"name": "symfony/finder",
"version": "v6.2.3",
"version": "v6.2.5",
"source": {
"type": "git",
"url": "https://github.com/symfony/finder.git",
"reference": "81eefbddfde282ee33b437ba5e13d7753211ae8e"
"reference": "c90dc446976a612e3312a97a6ec0069ab0c2099c"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/finder/zipball/81eefbddfde282ee33b437ba5e13d7753211ae8e",
"reference": "81eefbddfde282ee33b437ba5e13d7753211ae8e",
"url": "https://api.github.com/repos/symfony/finder/zipball/c90dc446976a612e3312a97a6ec0069ab0c2099c",
"reference": "c90dc446976a612e3312a97a6ec0069ab0c2099c",
"shasum": ""
},
"require": {
@ -1212,7 +1169,7 @@
"description": "Finds files and directories via an intuitive fluent interface",
"homepage": "https://symfony.com",
"support": {
"source": "https://github.com/symfony/finder/tree/v6.2.3"
"source": "https://github.com/symfony/finder/tree/v6.2.5"
},
"funding": [
{
@ -1228,20 +1185,20 @@
"type": "tidelift"
}
],
"time": "2022-12-22T17:55:15+00:00"
"time": "2023-01-20T17:45:48+00:00"
},
{
"name": "symfony/options-resolver",
"version": "v6.2.0",
"version": "v6.2.5",
"source": {
"type": "git",
"url": "https://github.com/symfony/options-resolver.git",
"reference": "d28f02acde71ff75e957082cd36e973df395f626"
"reference": "e8324d44f5af99ec2ccec849934a242f64458f86"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/options-resolver/zipball/d28f02acde71ff75e957082cd36e973df395f626",
"reference": "d28f02acde71ff75e957082cd36e973df395f626",
"url": "https://api.github.com/repos/symfony/options-resolver/zipball/e8324d44f5af99ec2ccec849934a242f64458f86",
"reference": "e8324d44f5af99ec2ccec849934a242f64458f86",
"shasum": ""
},
"require": {
@ -1279,7 +1236,7 @@
"options"
],
"support": {
"source": "https://github.com/symfony/options-resolver/tree/v6.2.0"
"source": "https://github.com/symfony/options-resolver/tree/v6.2.5"
},
"funding": [
{
@ -1295,7 +1252,7 @@
"type": "tidelift"
}
],
"time": "2022-11-02T09:08:04+00:00"
"time": "2023-01-01T08:38:09+00:00"
},
{
"name": "symfony/polyfill-ctype",
@ -1791,16 +1748,16 @@
},
{
"name": "symfony/process",
"version": "v6.2.0",
"version": "v6.2.5",
"source": {
"type": "git",
"url": "https://github.com/symfony/process.git",
"reference": "ba6e55359f8f755fe996c58a81e00eaa67a35877"
"reference": "9ead139f63dfa38c4e4a9049cc64a8b2748c83b7"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/process/zipball/ba6e55359f8f755fe996c58a81e00eaa67a35877",
"reference": "ba6e55359f8f755fe996c58a81e00eaa67a35877",
"url": "https://api.github.com/repos/symfony/process/zipball/9ead139f63dfa38c4e4a9049cc64a8b2748c83b7",
"reference": "9ead139f63dfa38c4e4a9049cc64a8b2748c83b7",
"shasum": ""
},
"require": {
@ -1832,7 +1789,7 @@
"description": "Executes commands in sub-processes",
"homepage": "https://symfony.com",
"support": {
"source": "https://github.com/symfony/process/tree/v6.2.0"
"source": "https://github.com/symfony/process/tree/v6.2.5"
},
"funding": [
{
@ -1848,7 +1805,7 @@
"type": "tidelift"
}
],
"time": "2022-11-02T09:08:04+00:00"
"time": "2023-01-01T08:38:09+00:00"
},
{
"name": "symfony/service-contracts",
@ -1937,16 +1894,16 @@
},
{
"name": "symfony/stopwatch",
"version": "v6.2.0",
"version": "v6.2.5",
"source": {
"type": "git",
"url": "https://github.com/symfony/stopwatch.git",
"reference": "266636bb8f3fbdccc302491df7b3a1b9a8c238a7"
"reference": "00b6ac156aacffc53487c930e0ab14587a6607f6"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/stopwatch/zipball/266636bb8f3fbdccc302491df7b3a1b9a8c238a7",
"reference": "266636bb8f3fbdccc302491df7b3a1b9a8c238a7",
"url": "https://api.github.com/repos/symfony/stopwatch/zipball/00b6ac156aacffc53487c930e0ab14587a6607f6",
"reference": "00b6ac156aacffc53487c930e0ab14587a6607f6",
"shasum": ""
},
"require": {
@ -1979,7 +1936,7 @@
"description": "Provides a way to profile code",
"homepage": "https://symfony.com",
"support": {
"source": "https://github.com/symfony/stopwatch/tree/v6.2.0"
"source": "https://github.com/symfony/stopwatch/tree/v6.2.5"
},
"funding": [
{
@ -1995,20 +1952,20 @@
"type": "tidelift"
}
],
"time": "2022-09-28T16:00:52+00:00"
"time": "2023-01-01T08:36:55+00:00"
},
{
"name": "symfony/string",
"version": "v6.2.2",
"version": "v6.2.5",
"source": {
"type": "git",
"url": "https://github.com/symfony/string.git",
"reference": "863219fd713fa41cbcd285a79723f94672faff4d"
"reference": "b2dac0fa27b1ac0f9c0c0b23b43977f12308d0b0"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/string/zipball/863219fd713fa41cbcd285a79723f94672faff4d",
"reference": "863219fd713fa41cbcd285a79723f94672faff4d",
"url": "https://api.github.com/repos/symfony/string/zipball/b2dac0fa27b1ac0f9c0c0b23b43977f12308d0b0",
"reference": "b2dac0fa27b1ac0f9c0c0b23b43977f12308d0b0",
"shasum": ""
},
"require": {
@ -2065,7 +2022,7 @@
"utf8"
],
"support": {
"source": "https://github.com/symfony/string/tree/v6.2.2"
"source": "https://github.com/symfony/string/tree/v6.2.5"
},
"funding": [
{
@ -2081,7 +2038,7 @@
"type": "tidelift"
}
],
"time": "2022-12-14T16:11:27+00:00"
"time": "2023-01-01T08:38:09+00:00"
}
],
"packages-dev": [],

View File

@ -158,9 +158,7 @@ MANDRILL_SECRET=
SPARKPOST_SECRET=
# Firefly III can send you the following messages.
SEND_REGISTRATION_MAIL=true
SEND_ERROR_MESSAGE=true
SEND_LOGIN_NEW_IP_WARNING=true
# These messages contain (sensitive) transaction information:
SEND_REPORT_JOURNALS=true

48
.github/mergify.yml vendored
View File

@ -6,3 +6,51 @@ pull_request_rules:
actions:
close:
message: Please do not open PR's on the `main` branch, but on the `develop` branch only. Thank you!
- name: No translations
conditions:
- or:
- -files=^resources/lang/bg_BG
- -files=^resources/lang/ca_ES
- -files=^resources/lang/cs_CZ
- -files=^resources/lang/da_DK
- -files=^resources/lang/de_DE
- -files=^resources/lang/el_GR
- -files=^resources/lang/en_GB
- -files=^resources/lang/es_ES
- -files=^resources/lang/et_EE
- -files=^resources/lang/fa_IR
- -files=^resources/lang/fi_FI
- -files=^resources/lang/fr_FR
- -files=^resources/lang/he_IL
- -files=^resources/lang/hu_HU
- -files=^resources/lang/id_ID
- -files=^resources/lang/is_IS
- -files=^resources/lang/it_IT
- -files=^resources/lang/ja_JP
- -files=^resources/lang/ko_KR
- -files=^resources/lang/lt_LT
- -files=^resources/lang/nb_NO
- -files=^resources/lang/nl_NL
- -files=^resources/lang/pl_PL
- -files=^resources/lang/pt_BR
- -files=^resources/lang/pt_PT
- -files=^resources/lang/ro_RO
- -files=^resources/lang/ru_RU
- -files=^resources/lang/si_LK
- -files=^resources/lang/sk_SK
- -files=^resources/lang/sl_SI
- -files=^resources/lang/sr_CS
- -files=^resources/lang/sv_SE
- -files=^resources/lang/th_TH
- -files=^resources/lang/tlh_AA
- -files=^resources/lang/tr_TR
- -files=^resources/lang/uk_UA
- -files=^resources/lang/vi_VN
- -files=^resources/lang/zh_CN
- -files=^resources/lang/zh_TW
actions:
comment:
message: |
Please do not submit translated strings in your PR. If you need new sentences to be translated, add them to the `en_US` language strings. New or changed translations for other languages can be submitted at https://crowdin.com/project/firefly-iii
Thank you!

View File

@ -31,10 +31,12 @@ use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
use Illuminate\Foundation\Bus\DispatchesJobs;
use Illuminate\Foundation\Validation\ValidatesRequests;
use Illuminate\Routing\Controller as BaseController;
use Illuminate\Support\Facades\Log;
use League\Fractal\Manager;
use League\Fractal\Serializer\JsonApiSerializer;
use Psr\Container\ContainerExceptionInterface;
use Psr\Container\NotFoundExceptionInterface;
use Symfony\Component\HttpFoundation\Exception\BadRequestException;
use Symfony\Component\HttpFoundation\ParameterBag;
/**
@ -95,14 +97,20 @@ abstract class Controller extends BaseController
// some date fields:
$dates = ['start', 'end', 'date'];
foreach ($dates as $field) {
$date = request()->query->get($field);
$obj = null;
try {
$date = request()->query->get($field);
} catch (BadRequestException $e) {
Log::error(sprintf('Request field "%s" contains a non-scalar value. Value set to NULL.', $field));
Log::error($e->getMessage());
$value = null;
}
$obj = null;
if (null !== $date) {
try {
$obj = Carbon::parse($date);
} catch (InvalidDateException|InvalidFormatException $e) {
// don't care
app('log')->warning(sprintf('Ignored invalid date "%s" in API controller parameter check: %s', $date, $e->getMessage()));
app('log')->warning(sprintf('Ignored invalid date "%s" in API controller parameter check: %s', substr($date, 0, 20), $e->getMessage()));
}
}
$bag->set($field, $obj);
@ -111,7 +119,13 @@ abstract class Controller extends BaseController
// integer fields:
$integers = ['limit'];
foreach ($integers as $integer) {
$value = request()->query->get($integer);
try {
$value = request()->query->get($integer);
} catch (BadRequestException $e) {
Log::error(sprintf('Request field "%s" contains a non-scalar value. Value set to NULL.', $integer));
Log::error($e->getMessage());
$value = null;
}
if (null !== $value) {
$bag->set($integer, (int)$value);
}
@ -129,7 +143,13 @@ abstract class Controller extends BaseController
private function getSortParameters(ParameterBag $bag): ParameterBag
{
$sortParameters = [];
$param = (string)request()->query->get('sort');
try {
$param = (string)request()->query->get('sort');
} catch (BadRequestException $e) {
Log::error('Request field "sort" contains a non-scalar value. Value set to NULL.');
Log::error($e->getMessage());
$param = '';
}
if ('' === $param) {
return $bag;
}

View File

@ -69,7 +69,7 @@ class DestroyController extends Controller
$this->unused = $request->boolean('unused', false);
switch ($objects) {
default:
throw new FireflyException(sprintf('This endpoint can\'t handle object "%s"', $objects));
throw new FireflyException(sprintf('200033: This endpoint can\'t handle object "%s"', $objects));
case 'budgets':
$this->destroyBudgets();
break;

View File

@ -28,8 +28,10 @@ use FireflyIII\Api\V1\Controllers\Controller;
use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Models\TransactionCurrency;
use FireflyIII\Repositories\Currency\CurrencyRepositoryInterface;
use FireflyIII\Repositories\User\UserRepositoryInterface;
use FireflyIII\User;
use Illuminate\Http\JsonResponse;
use Validator;
/**
* Class DestroyController
@ -37,6 +39,7 @@ use Illuminate\Http\JsonResponse;
class DestroyController extends Controller
{
private CurrencyRepositoryInterface $repository;
private UserRepositoryInterface $userRepository;
/**
* CurrencyRepository constructor.
@ -49,6 +52,7 @@ class DestroyController extends Controller
$this->middleware(
function ($request, $next) {
$this->repository = app(CurrencyRepositoryInterface::class);
$this->userRepository = app(UserRepositoryInterface::class);
$this->repository->setUser(auth()->user());
return $next($request);
@ -72,16 +76,20 @@ class DestroyController extends Controller
{
/** @var User $admin */
$admin = auth()->user();
$rules = ['currency_code' => 'required'];
if (!$this->userRepository->hasRole($admin, 'owner')) {
// access denied:
throw new FireflyException('200005: You need the "owner" role to do this.');
$messages = ['currency_code' => '200005: You need the "owner" role to do this.'];
Validator::make([], $rules, $messages)->validate();
}
if ($this->repository->currencyInUse($currency)) {
throw new FireflyException('200006: Currency in use.');
$messages = ['currency_code' => '200006: Currency in use.'];
Validator::make([], $rules, $messages)->validate();
}
if ($this->repository->isFallbackCurrency($currency)) {
throw new FireflyException('200026: Currency is fallback.');
$messages = ['currency_code' => '200026: Currency is fallback.'];
Validator::make([], $rules, $messages)->validate();
}
$this->repository->destroy($currency);

View File

@ -34,6 +34,7 @@ use FireflyIII\Transformers\LinkTypeTransformer;
use FireflyIII\User;
use Illuminate\Http\JsonResponse;
use League\Fractal\Resource\Item;
use Validator;
/**
* Class StoreController
@ -81,9 +82,12 @@ class StoreController extends Controller
{
/** @var User $admin */
$admin = auth()->user();
$rules = ['name' => 'required'];
if (!$this->userRepository->hasRole($admin, 'owner')) {
throw new FireflyException('200005: You need the "owner" role to do this.');
// access denied:
$messages = ['name' => '200005: You need the "owner" role to do this.'];
Validator::make([], $rules, $messages)->validate();
}
$data = $request->getAll();
// if currency ID is 0, find the currency by the code:

View File

@ -35,6 +35,7 @@ use FireflyIII\Transformers\LinkTypeTransformer;
use FireflyIII\User;
use Illuminate\Http\JsonResponse;
use League\Fractal\Resource\Item;
use Validator;
/**
* Class UpdateController
@ -87,9 +88,11 @@ class UpdateController extends Controller
/** @var User $admin */
$admin = auth()->user();
$rules = ['name' => 'required'];
if (!$this->userRepository->hasRole($admin, 'owner')) {
throw new FireflyException('200005: You need the "owner" role to do this.');
$messages = ['name' => '200005: You need the "owner" role to do this.'];
Validator::make([], $rules, $messages)->validate();
}
$data = $request->getAll();

View File

@ -32,6 +32,7 @@ use Illuminate\Http\JsonResponse;
use Log;
use Psr\Container\ContainerExceptionInterface;
use Psr\Container\NotFoundExceptionInterface;
use Validator;
/**
* Class ConfigurationController
@ -174,8 +175,10 @@ class ConfigurationController extends Controller
*/
public function update(UpdateRequest $request, string $name): JsonResponse
{
$rules = ['value' => 'required'];
if (!$this->repository->hasRole(auth()->user(), 'owner')) {
throw new FireflyException('200005: You need the "owner" role to do this.');
$messages = ['value' => '200005: You need the "owner" role to do this.'];
Validator::make([], $rules, $messages)->validate();
}
$data = $request->getAll();
$shortName = str_replace('configuration.', '', $name);

View File

@ -73,7 +73,7 @@ class AttemptController extends Controller
public function index(Webhook $webhook, WebhookMessage $message): JsonResponse
{
if ($message->webhook_id !== $webhook->id) {
throw new FireflyException('Webhook and webhook message are no match');
throw new FireflyException('200040: Webhook and webhook message are no match');
}
$manager = $this->getManager();
@ -112,10 +112,10 @@ class AttemptController extends Controller
public function show(Webhook $webhook, WebhookMessage $message, WebhookAttempt $attempt): JsonResponse
{
if ($message->webhook_id !== $webhook->id) {
throw new FireflyException('Webhook and webhook message are no match');
throw new FireflyException('200040: Webhook and webhook message are no match');
}
if ($attempt->webhook_message_id !== $message->id) {
throw new FireflyException('Webhook message and webhook attempt are no match');
throw new FireflyException('200041: Webhook message and webhook attempt are no match');
}
$manager = $this->getManager();

View File

@ -91,10 +91,10 @@ class DestroyController extends Controller
public function destroyAttempt(Webhook $webhook, WebhookMessage $message, WebhookAttempt $attempt): JsonResponse
{
if ($message->webhook_id !== $webhook->id) {
throw new FireflyException('Webhook and webhook message are no match');
throw new FireflyException('200040: Webhook and webhook message are no match');
}
if ($attempt->webhook_message_id !== $message->id) {
throw new FireflyException('Webhook message and webhook attempt are no match');
throw new FireflyException('200041: Webhook message and webhook attempt are no match');
}
$this->repository->destroyAttempt($attempt);
@ -119,7 +119,7 @@ class DestroyController extends Controller
public function destroyMessage(Webhook $webhook, WebhookMessage $message): JsonResponse
{
if ($message->webhook_id !== $webhook->id) {
throw new FireflyException('Webhook and webhook message are no match');
throw new FireflyException('200040: Webhook and webhook message are no match');
}
$this->repository->destroyMessage($message);
app('preferences')->mark();

View File

@ -103,7 +103,7 @@ class MessageController extends Controller
public function show(Webhook $webhook, WebhookMessage $message): JsonResponse
{
if ($message->webhook_id !== $webhook->id) {
throw new FireflyException('Webhook and webhook message are no match');
throw new FireflyException('200040: Webhook and webhook message are no match');
}
$manager = $this->getManager();

View File

@ -32,6 +32,7 @@ use Illuminate\Database\Eloquent\Model;
use Illuminate\Pagination\LengthAwarePaginator;
use Illuminate\Routing\Controller as BaseController;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Log;
use League\Fractal\Manager;
use League\Fractal\Pagination\IlluminatePaginatorAdapter;
use League\Fractal\Resource\Collection as FractalCollection;
@ -39,6 +40,7 @@ use League\Fractal\Resource\Item;
use League\Fractal\Serializer\JsonApiSerializer;
use Psr\Container\ContainerExceptionInterface;
use Psr\Container\NotFoundExceptionInterface;
use Symfony\Component\HttpFoundation\Exception\BadRequestException;
use Symfony\Component\HttpFoundation\ParameterBag;
/**
@ -90,14 +92,20 @@ class Controller extends BaseController
// some date fields:
foreach ($dates as $field) {
$date = request()->query->get($field);
$obj = null;
try {
$date = request()->query->get($field);
} catch (BadRequestException $e) {
Log::error(sprintf('Request field "%s" contains a non-scalar value. Value set to NULL.', $field));
Log::error($e->getMessage());
$value = null;
}
$obj = null;
if (null !== $date) {
try {
$obj = Carbon::parse($date);
} catch (InvalidDateException|InvalidFormatException $e) {
// don't care
app('log')->warning(sprintf('Ignored invalid date "%s" in API v2 controller parameter check: %s', $date, $e->getMessage()));
app('log')->warning(sprintf('Ignored invalid date "%s" in API v2 controller parameter check: %s', substr($date, 0, 20), $e->getMessage()));
}
}
$bag->set($field, $obj);
@ -105,7 +113,13 @@ class Controller extends BaseController
// integer fields:
foreach ($integers as $integer) {
$value = request()->query->get($integer);
try {
$value = request()->query->get($integer);
} catch (BadRequestException $e) {
Log::error(sprintf('Request field "%s" contains a non-scalar value. Value set to NULL.', $integer));
Log::error($e->getMessage());
$value = null;
}
if (null !== $value) {
$bag->set($integer, (int)$value);
}

View File

@ -139,17 +139,17 @@ class FixTransactionTypes extends Command
}
);
if (0 === $collection->count()) {
throw new FireflyException(sprintf('Journal #%d has no source transaction.', $journal->id));
throw new FireflyException(sprintf('300001: Journal #%d has no source transaction.', $journal->id));
}
if (1 !== $collection->count()) {
throw new FireflyException(sprintf('Journal #%d has multiple source transactions.', $journal->id));
throw new FireflyException(sprintf('300002: Journal #%d has multiple source transactions.', $journal->id));
}
/** @var Transaction $transaction */
$transaction = $collection->first();
/** @var Account|null $account */
$account = $transaction->account;
if (null === $account) {
throw new FireflyException(sprintf('Journal #%d, transaction #%d has no source account.', $journal->id, $transaction->id));
throw new FireflyException(sprintf('300003: Journal #%d, transaction #%d has no source account.', $journal->id, $transaction->id));
}
return $account;
@ -169,17 +169,17 @@ class FixTransactionTypes extends Command
}
);
if (0 === $collection->count()) {
throw new FireflyException(sprintf('Journal #%d has no destination transaction.', $journal->id));
throw new FireflyException(sprintf('300004: Journal #%d has no destination transaction.', $journal->id));
}
if (1 !== $collection->count()) {
throw new FireflyException(sprintf('Journal #%d has multiple destination transactions.', $journal->id));
throw new FireflyException(sprintf('300005: Journal #%d has multiple destination transactions.', $journal->id));
}
/** @var Transaction $transaction */
$transaction = $collection->first();
/** @var Account|null $account */
$account = $transaction->account;
if (null === $account) {
throw new FireflyException(sprintf('Journal #%d, transaction #%d has no destination account.', $journal->id, $transaction->id));
throw new FireflyException(sprintf('300006: Journal #%d, transaction #%d has no destination account.', $journal->id, $transaction->id));
}
return $account;

View File

@ -249,7 +249,7 @@ class ExportData extends Command
}
}
if (0 === $final->count()) {
throw new FireflyException('Ended up with zero valid accounts to export from.');
throw new FireflyException('300007: Ended up with zero valid accounts to export from.');
}
return $final;

View File

@ -2,7 +2,7 @@
/*
* CreateGroupMemberships.php
* Copyright (c) 2021 james@firefly-iii.org
* Copyright (c) 2023 james@firefly-iii.org
*
* This file is part of Firefly III (https://github.com/firefly-iii).
*
@ -22,7 +22,7 @@
declare(strict_types=1);
namespace FireflyIII\Console\Commands\Upgrade;
namespace FireflyIII\Console\Commands\Integrity;
use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Models\GroupMembership;
@ -31,8 +31,6 @@ use FireflyIII\Models\UserRole;
use FireflyIII\User;
use Illuminate\Console\Command;
use Log;
use Psr\Container\ContainerExceptionInterface;
use Psr\Container\NotFoundExceptionInterface;
/**
* Class CreateGroupMemberships
@ -51,7 +49,7 @@ class CreateGroupMemberships extends Command
*
* @var string
*/
protected $signature = 'firefly-iii:create-group-memberships {--F|force : Force the execution of this command.}';
protected $signature = 'firefly-iii:create-group-memberships';
/**
* Execute the console command.
@ -62,36 +60,15 @@ class CreateGroupMemberships extends Command
public function handle(): int
{
$start = microtime(true);
if ($this->isExecuted() && true !== $this->option('force')) {
$this->warn('This command has already been executed.');
return 0;
}
$this->createGroupMemberships();
$this->markAsExecuted();
$end = round(microtime(true) - $start, 2);
$this->info(sprintf('in %s seconds.', $end));
$this->info(sprintf('Validated group memberships in %s seconds.', $end));
return 0;
}
/**
* @return bool
* @throws FireflyException
* @throws ContainerExceptionInterface
* @throws NotFoundExceptionInterface
*/
private function isExecuted(): bool
{
$configVar = app('fireflyconfig')->get(self::CONFIG_NAME, false);
if (null !== $configVar) {
return (bool)$configVar->data;
}
return false;
}
/**
*
* @throws FireflyException
@ -102,24 +79,11 @@ class CreateGroupMemberships extends Command
/** @var User $user */
foreach ($users as $user) {
Log::debug(sprintf('Manage group memberships for user #%d', $user->id));
if (!$this->hasGroupMembership($user)) {
Log::debug(sprintf('User #%d has no main group.', $user->id));
$this->createGroupMembership($user);
}
$this->createGroupMembership($user);
Log::debug(sprintf('Done with user #%d', $user->id));
}
}
/**
* @param User $user
*
* @return bool
*/
private function hasGroupMembership(User $user): bool
{
return $user->groupMemberships()->count() > 0;
}
/**
* @param User $user
*
@ -127,35 +91,37 @@ class CreateGroupMemberships extends Command
*/
private function createGroupMembership(User $user): void
{
$userGroup = UserGroup::create(['title' => $user->email]);
$userRole = UserRole::where('title', UserRole::OWNER)->first();
// check if membership exists
$userGroup = UserGroup::where('title', $user->email)->first();
if (null === $userGroup) {
$userGroup = UserGroup::create(['title' => $user->email]);
Log::debug(sprintf('Created new user group #%d ("%s")', $userGroup->id, $userGroup->title));
}
$userRole = UserRole::where('title', UserRole::OWNER)->first();
if (null === $userRole) {
throw new FireflyException('Firefly III could not find a user role. Please make sure all validations have run.');
throw new FireflyException('Firefly III could not find a user role. Please make sure all migrations have run.');
}
/** @var GroupMembership|null $membership */
$membership = GroupMembership::create(
[
'user_id' => $user->id,
'user_role_id' => $userRole->id,
'user_group_id' => $userGroup->id,
]
);
$membership = GroupMembership::where('user_id', $user->id)
->where('user_group_id', $userGroup->id)
->where('user_role_id', $userRole->id)->first();
if (null === $membership) {
throw new FireflyException('Firefly III could not create user group management object. Please make sure all validations have run.');
GroupMembership::create(
[
'user_id' => $user->id,
'user_role_id' => $userRole->id,
'user_group_id' => $userGroup->id,
]
);
Log::debug('Created new membership.');
}
if (null === $user->user_group_id) {
$user->user_group_id = $userGroup->id;
$user->save();
Log::debug('Put user in default group.');
}
$user->user_group_id = $userGroup->id;
$user->save();
Log::debug(sprintf('User #%d now has main group.', $user->id));
}
/**
*
*/
private function markAsExecuted(): void
{
app('fireflyconfig')->set(self::CONFIG_NAME, true);
}
}

View File

@ -74,7 +74,6 @@ class UpgradeDatabase extends Command
'firefly-iii:migrate-tag-locations',
'firefly-iii:migrate-recurrence-type',
'firefly-iii:upgrade-liabilities',
'firefly-iii:create-group-memberships',
'firefly-iii:liabilities-600',
// there are 16 verify commands.
@ -100,6 +99,7 @@ class UpgradeDatabase extends Command
'firefly-iii:fix-transaction-types',
'firefly-iii:fix-frontpage-accounts',
'firefly-iii:fix-ibans',
'firefly-iii:create-group-memberships',
'firefly-iii:upgrade-group-information',
// two report commands

View File

@ -48,7 +48,7 @@ trait VerifiesAccessToken
$repository = app(UserRepositoryInterface::class);
$user = $repository->find($userId);
if (null === $user) {
throw new FireflyException('User is unexpectedly NULL');
throw new FireflyException('300000: User is unexpectedly NULL');
}
return $user;

View File

@ -168,7 +168,7 @@ class Handler extends ExceptionHandler
return response()->view('errors.FireflyException', ['exception' => $e, 'debug' => $isDebug], 500);
}
Log::debug('Error has no Firefly III treatment, parent will handle.');
Log::debug(sprintf('Error "%s" has no Firefly III treatment, parent will handle.', get_class($e)));
return parent::render($request, $e);
}

View File

@ -73,12 +73,12 @@ class NetWorth implements NetWorthInterface
$cache->addProperty('net-worth-by-currency');
$cache->addProperty(implode(',', $accounts->pluck('id')->toArray()));
if ($cache->has()) {
// return $cache->get();
return $cache->get();
}
$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);
@ -89,16 +89,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 for %s is %s', $date->format('Y-m-d'), $balance));
//Log::debug(sprintf('Balance for %s is %s', $date->format('Y-m-d'), $balance));
// always subtract virtual balance.
$virtualBalance = (string)$account->virtual_balance;
@ -106,14 +106,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);

View File

@ -150,6 +150,7 @@ class RecurrenceController extends Controller
*/
public function suggest(Request $request): JsonResponse
{
$request->validate(['date' => ['required', 'date'],]);
$string = $request->get('date') ?? date('Y-m-d');
$today = Carbon::now()->startOfDay();
$date = Carbon::createFromFormat('Y-m-d', $string)->startOfDay();

View File

@ -106,7 +106,11 @@ class SearchController extends Controller
*/
public function search(Request $request, SearchInterface $searcher): JsonResponse
{
$fullQuery = (string)$request->get('query');
$entry = $request->get('query');
if (!is_scalar($entry)) {
$entry = '';
}
$fullQuery = (string)$entry;
$page = 0 === (int)$request->get('page') ? 1 : (int)$request->get('page');
$searcher->parseQuery($fullQuery);

View File

@ -85,7 +85,6 @@ class InstallController extends Controller
'firefly-iii:migrate-tag-locations' => [],
'firefly-iii:migrate-recurrence-type' => [],
'firefly-iii:upgrade-liabilities' => [],
'firefly-iii:create-group-memberships' => [],
'firefly-iii:liabilities-600' => [],
// verify commands
@ -111,9 +110,10 @@ class InstallController extends Controller
'firefly-iii:fix-transaction-types' => [],
'firefly-iii:fix-frontpage-accounts' => [],
'firefly-iii:fix-ibans' => [],
'firefly-iii:create-group-memberships' => [],
'firefly-iii:upgrade-group-information' => [],
// final command to set latest version in DB
// final command to set the latest version in DB
'firefly-iii:set-latest-version' => ['--james-is-cool' => true],
'firefly-iii:verify-security-alerts' => [],
];

View File

@ -25,11 +25,14 @@ namespace FireflyIII\Http\Requests;
use FireflyIII\Models\Account;
use FireflyIII\Models\Location;
use FireflyIII\Models\UserRole;
use FireflyIII\Rules\UniqueIban;
use FireflyIII\Support\Request\AppendsLocationData;
use FireflyIII\Support\Request\ChecksLogin;
use FireflyIII\Support\Request\ConvertsDataTypes;
use FireflyIII\Validation\Administration\ValidatesAdministrationAccess;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Validation\Validator;
/**
* Class AccountFormRequest.
@ -39,6 +42,7 @@ class AccountFormRequest extends FormRequest
use ConvertsDataTypes;
use AppendsLocationData;
use ChecksLogin;
use ValidatesAdministrationAccess;
/**
* Get all data.
@ -48,6 +52,7 @@ class AccountFormRequest extends FormRequest
public function getAccountData(): array
{
$data = [
'administration_id' => $this->convertInteger('administration_id'),
'name' => $this->convertString('name'),
'active' => $this->boolean('active'),
'account_type_name' => $this->convertString('objectType'),
@ -67,6 +72,9 @@ class AccountFormRequest extends FormRequest
'include_net_worth' => '1',
'liability_direction' => $this->convertString('liability_direction'),
];
if (0 === $data['administration_id']) {
$data['administration_id'] = auth()->user()->getAdministrationId();
}
$data = $this->appendLocationData($data, 'location');
if (false === $this->boolean('include_net_worth')) {
@ -101,6 +109,7 @@ class AccountFormRequest extends FormRequest
$types = implode(',', array_keys(config('firefly.subTitlesByIdentifier')));
$ccPaymentTypes = implode(',', array_keys(config('firefly.ccTypes')));
$rules = [
'administration_id' => 'min:1|max:16777216|numeric',
'name' => 'required|min:1|uniqueAccountForUser',
'opening_balance' => 'numeric|nullable|max:1000000000',
'opening_balance_date' => 'date|required_with:opening_balance|nullable',
@ -130,4 +139,20 @@ class AccountFormRequest extends FormRequest
return $rules;
}
/**
* Configure the validator instance with special rules for after the basic validation rules.
*
* @param Validator $validator
*
* @return void
*/
public function withValidator(Validator $validator): void
{
$validator->after(
function (Validator $validator) {
// validate if the account can access this administration
$this->validateAdministration($validator, [UserRole::CHANGE_TRANSACTIONS]);
}
);
}
}

View File

@ -215,7 +215,12 @@ class ReportFormRequest extends FormRequest
$repository = app(TagRepositoryInterface::class);
$set = $this->get('tag');
$collection = new Collection();
Log::debug('Set is:', $set ?? []);
if (is_array($set)) {
Log::debug('Set is:', $set);
}
if (!is_array($set)) {
Log::error(sprintf('Set is not an array! "%s"', $set));
}
if (is_array($set)) {
foreach ($set as $tagTag) {
Log::debug(sprintf('Now searching for "%s"', $tagTag));

View File

@ -297,7 +297,7 @@ class CreateAutoBudgetLimits implements ShouldQueue
Log::info(sprintf('The amount left is negative, so it will be reset to %s.', $totalAmount));
}
if (1 !== bccomp('0', $budgetLeft)) {
$totalAmount = bcadd($budgetLeft, $budgetLimit->amount);
$totalAmount = bcadd($budgetLeft, $totalAmount);
Log::info(sprintf('The amount left is positive, so the new amount will be %s.', $totalAmount));
}

View File

@ -48,6 +48,7 @@ trait CreatesObjectGroups
*/
protected function findOrCreateObjectGroup(string $title): ?ObjectGroup
{
$title = substr($title, 0, 255);
$maxOrder = $this->getObjectGroupMaxOrder();
if (!$this->hasObjectGroup($title)) {
return ObjectGroup::create(

View File

@ -426,7 +426,7 @@ class RuleRepository implements RuleRepositoryInterface
$stopProcessing = $trigger['stop_processing'] ?? false;
$active = $trigger['active'] ?? true;
$type = $trigger['type'];
if (true === $trigger['prohibited']) {
if (true === ($trigger['prohibited'] ?? false) && !str_starts_with($type, '-')) {
$type = sprintf('-%s', $type);
}

View File

@ -27,6 +27,7 @@ use Carbon\Carbon;
use Exception;
use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Models\BudgetLimit;
use FireflyIII\Models\GroupMembership;
use FireflyIII\Models\InvitedUser;
use FireflyIII\Models\Role;
use FireflyIII\Models\UserGroup;
@ -466,4 +467,25 @@ class UserRepository implements UserRepositoryInterface
$invitee = InvitedUser::where('invite_code', $code)->where('expires', '>', $now->format('Y-m-d H:i:s'))->where('redeemed', 0)->first();
return null !== $invitee;
}
/**
* @inheritDoc
* @throws FireflyException
*/
public function getRolesInGroup(User $user, int $groupId): array
{
/** @var UserGroup $group */
$group = UserGroup::find($groupId);
if (null === $group) {
throw new FireflyException(sprintf('Could not find group #%d', $groupId));
}
$memberships = $group->groupMemberships()->where('user_id', $user->id)->get();
$roles = [];
/** @var GroupMembership $membership */
foreach ($memberships as $membership) {
$role = $membership->userRole;
$roles[] = $role->title;
}
return $roles;
}
}

View File

@ -40,6 +40,13 @@ interface UserRepositoryInterface
*/
public function all(): Collection;
/**
* @param User $user
* @param int $groupId
* @return array
*/
public function getRolesInGroup(User $user, int $groupId): array;
/**
* Gives a user a role.
*

View File

@ -112,7 +112,7 @@ class IsValidAttachmentModel implements Rule
TransactionJournal::class => 'validateJournal',
];
if (!array_key_exists($this->model, $methods)) {
Log::error(sprintf('Cannot validate model "%s" in %s.', $this->model, __METHOD__));
Log::error(sprintf('Cannot validate model "%s" in %s.', substr($this->model, 0, 20), __METHOD__));
return false;
}

View File

@ -56,7 +56,7 @@ 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);
$signatureGenerator = app(SignatureGeneratorInterface::class);
$this->message->sent = true;
$this->message->save();
try {
@ -109,7 +109,7 @@ class StandardWebhookSender implements WebhookSenderInterface
];
$client = new Client();
try {
$res = $client->request('POST', $this->message->webhook->url, $options);
$res = $client->request('POST', $this->message->webhook->url, $options);
} catch (RequestException|ConnectException $e) {
Log::error($e->getMessage());
Log::error($e->getTraceAsString());
@ -122,8 +122,11 @@ class StandardWebhookSender implements WebhookSenderInterface
$attempt = new WebhookAttempt();
$attempt->webhookMessage()->associate($this->message);
$attempt->status_code = $e->hasResponse() ? $e->getResponse()->getStatusCode() : 0;
$attempt->logs = $logs;
$attempt->status_code = 0;
if (method_exists($e, 'hasResponse') && method_exists($e, 'getResponse')) {
$attempt->status_code = $e->hasResponse() ? $e->getResponse()->getStatusCode() : 0;
}
$attempt->logs = $logs;
$attempt->save();
return;

View File

@ -52,7 +52,7 @@ class RemoteUserGuard implements Guard
{
/** @var Request $request */
$request = $app->get('request');
Log::debug(sprintf('Created RemoteUserGuard for "%s"', $request?->getRequestUri()));
Log::debug(sprintf('Created RemoteUserGuard for %s "%s"', $request?->getMethod(), $request?->getRequestUri()));
$this->application = $app;
$this->provider = $provider;
$this->user = null;
@ -82,16 +82,18 @@ class RemoteUserGuard implements Guard
$header = config('auth.guard_header', 'REMOTE_USER');
$userID = request()->server($header) ?? null;
//$userID = 'james@firefly';
if (function_exists('apache_request_headers')) {
Log::debug('Use apache_request_headers to find user ID.');
$userID = request()->server($header) ?? apache_request_headers()[$header] ?? null;
}
if (null === $userID) {
if (null === $userID || '' === $userID) {
Log::error(sprintf('No user in header "%s".', $header));
throw new FireflyException('The guard header was unexpectedly empty. See the logs.');
}
Log::debug(sprintf('User ID found in header is "%s"', $userID));
/** @var User $retrievedUser */
$retrievedUser = $this->provider->retrieveById($userID);
@ -139,8 +141,13 @@ class RemoteUserGuard implements Guard
public function user(): ?User
{
Log::debug(sprintf('Now at %s', __METHOD__));
//$this->authenticate();
return $this->user;
$user = $this->user;
if (null === $user) {
Log::debug('User is NULL');
return null;
}
return $user;
}
/**

View File

@ -46,10 +46,11 @@ trait ConvertsDataTypes
* Abstract method that always exists in the Request classes that use this
* trait, OR a stub needs to be added by any other class that uses this train.
*
* @param mixed $key
* @param mixed $key
* @return mixed
*/
abstract public function has($key);
/**
* Return integer value.
*
@ -71,7 +72,11 @@ trait ConvertsDataTypes
*/
public function convertString(string $field): string
{
return $this->clearString((string)($this->get($field) ?? ''), false);
$entry = $this->get($field);
if (!is_scalar($entry)) {
return '';
}
return $this->clearString((string)$entry, false);
}
/**
@ -85,7 +90,10 @@ trait ConvertsDataTypes
if (null === $string) {
return null;
}
$search = [
if ('' === $string) {
return '';
}
$search = [
"\0", // NUL
"\f", // form feed
"\v", // vertical tab
@ -135,14 +143,20 @@ trait ConvertsDataTypes
"\u{3000}", // ideographic space
"\u{FEFF}", // zero width no -break space
];
$replace = "\x20"; // plain old normal space
$string = str_replace($search, $replace, $string);
$replace = "\x20"; // plain old normal space
$string = str_replace($search, $replace, $string);
$secondSearch = $keepNewlines ? ["\r"] : ["\r", "\n", "\t", "\036", "\025"];
$string = str_replace($secondSearch, '', $string);
// clear zalgo text (TODO also in API v2)
$string = preg_replace('/\pM/u', '', $string);
if (null === $string) {
return null;
}
if ('' === $string) {
return '';
}
return trim($string);
}

View File

@ -162,7 +162,7 @@ class OperatorQuerySearch implements SearchInterface
} 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);
throw new FireflyException(sprintf('Invalid search value "%s". See the logs.', e($query)), 0, $e);
}
Log::debug(sprintf('Found %d node(s)', count($query1->getNodes())));

View File

@ -23,6 +23,7 @@ declare(strict_types=1);
namespace FireflyIII\Transformers;
use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Repositories\User\UserRepositoryInterface;
use FireflyIII\User;
@ -40,20 +41,22 @@ class UserTransformer extends AbstractTransformer
* @param User $user
*
* @return array
* @throws FireflyException
*/
public function transform(User $user): array
{
$this->repository = $this->repository ?? app(UserRepositoryInterface::class);
return [
'id' => (int)$user->id,
'created_at' => $user->created_at->toAtomString(),
'updated_at' => $user->updated_at->toAtomString(),
'email' => $user->email,
'blocked' => 1 === (int)$user->blocked,
'blocked_code' => '' === $user->blocked_code ? null : $user->blocked_code,
'role' => $this->repository->getRoleByUser($user),
'links' => [
'id' => (int)$user->id,
'administration_id' => (string)$user->getAdministrationId(),
'created_at' => $user->created_at->toAtomString(),
'updated_at' => $user->updated_at->toAtomString(),
'email' => $user->email,
'blocked' => 1 === (int)$user->blocked,
'blocked_code' => '' === $user->blocked_code ? null : $user->blocked_code,
'role' => $this->repository->getRoleByUser($user),
'links' => [
[
'rel' => 'self',
'uri' => '/users/'.$user->id,

View File

@ -239,7 +239,7 @@ class TransactionGroupTransformer extends AbstractTransformer
*/
private function stringFromArray(NullArrayObject $array, string $key, ?string $default): ?string
{
Log::debug(sprintf('%s: %s', $key, var_export($array[$key])));
//Log::debug(sprintf('%s: %s', $key, var_export($array[$key], true)));
if (null === $array[$key] && null === $default) {
return null;
}

View File

@ -27,6 +27,7 @@ namespace FireflyIII;
use Eloquent;
use Exception;
use FireflyIII\Events\RequestedNewPassword;
use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Models\Account;
use FireflyIII\Models\Attachment;
use FireflyIII\Models\AvailableBudget;
@ -361,6 +362,21 @@ class User extends Authenticatable
return $this->hasMany(GroupMembership::class)->with(['userGroup', 'userRole']);
}
/**
* A safe method that returns the user's current administration ID (group ID).
*
* @return int
* @throws FireflyException
*/
public function getAdministrationId(): int
{
$groupId = (int)$this->user_group_id;
if (0 === $groupId) {
throw new FireflyException('User has no administration ID.');
}
return $groupId;
}
/**
* @codeCoverageIgnore
* Link to object groups.

View File

@ -0,0 +1,99 @@
<?php
declare(strict_types=1);
/*
* ValidatesAdministrationAccess.php
* Copyright (c) 2023 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\Validation\Administration;
use FireflyIII\Models\UserRole;
use FireflyIII\Repositories\User\UserRepositoryInterface;
use FireflyIII\User;
use Illuminate\Auth\AuthenticationException;
use Illuminate\Support\Facades\Log;
use Illuminate\Validation\Validator;
/**
* Trait ValidatesAdministrationAccess
*/
trait ValidatesAdministrationAccess
{
/**
* @param Validator $validator
* @param array $allowedRoles
* @return void
* @throws AuthenticationException
*/
protected function validateAdministration(Validator $validator, array $allowedRoles): void
{
Log::debug('Now in validateAdministration()');
if (!auth()->check()) {
Log::error('User is not authenticated.');
throw new AuthenticationException('No access to validateAdministration() method.');
}
/** @var User $user */
$user = auth()->user();
// get data from request:
$data = $validator->getData();
// check if user is part of this administration
$administrationId = (int)($data['administration_id'] ?? $user->getAdministrationId());
// safety catch:
if (0 === $administrationId) {
Log::error('validateAdministration ran into empty administration ID.');
throw new AuthenticationException('Cannot validate administration.');
}
// grab the group:
$repository = app(UserRepositoryInterface::class);
// collect the user's roles in this group:
$array = $repository->getRolesInGroup($user, $administrationId);
if (0 === count($array)) {
Log::error(sprintf('User #%d ("%s") has no membership in group #%d.', $user->id, $user->email, $administrationId));
$validator->errors()->add('administration', (string)trans('validation.no_access_user_group'));
return;
}
if (in_array(UserRole::OWNER, $array, true)) {
Log::debug('User is owner of this administration.');
return;
}
if (in_array(UserRole::FULL, $array, true)) {
Log::debug('User has full access to this administration.');
return;
}
$access = true;
foreach ($allowedRoles as $allowedRole) {
if (!in_array($allowedRole, $array, true)) {
$access = false;
}
}
if (false === $access) {
Log::error(
sprintf(
'User #%d has memberships [%s] to group #%d but needs [%s].',
$user->id,
join(', ', $array),
$administrationId,
join(', ', $allowedRoles)
)
);
$validator->errors()->add('administration', (string)trans('validation.no_access_user_group'));
}
}
}

View File

@ -46,6 +46,9 @@ trait CurrencyValidation
$transactions = $this->getTransactionsArray($validator);
foreach ($transactions as $index => $transaction) {
if (!is_array($transaction)) {
continue;
}
// if foreign amount is present, then the currency must be as well.
if (array_key_exists('foreign_amount', $transaction)
&& !(array_key_exists('foreign_currency_id', $transaction)

View File

@ -2,6 +2,43 @@
All notable changes to this project will be documented in this file.
This project adheres to [Semantic Versioning](http://semver.org/).
## 6.0.0-alpha.2 - 2023-02-05
### Warnings
- ⚠️ Make a backup of your database first!
- ⚠️ This version requires **PHP 8.2**.
You can access the new V3 layout under `/v3/`. If you decide to use or test it:
- ⚠️ Read the instructions under the ☠️ icon FIRST.
- ⚠️ The new layout is not yet finished. Use it to change your data at your own risk.
### Added
- Warning for remote user guard
- Improved validation
- Can now validate a user's financial administration
- Dark mode CSS
### Changed
- Various errors no longer throw a 500 but a 422 (validation failed)
### Removed
- Cryptocurrencies in default currency set
- Unused environment variables
### Fixed
- Bad escape in JS code.
- [Issue 6869](https://github.com/firefly-iii/firefly-iii/issues/6869) Liability created via API is not applying opening balance.
- [Issue 6870](https://github.com/firefly-iii/firefly-iii/issues/6870) Old inactive recurring transactions do not lose categories when the categories are deleted
- [Issue 6876](https://github.com/firefly-iii/firefly-iii/issues/6876) Date field is validated in recurring transactions
- [Issue 6974](https://github.com/firefly-iii/firefly-iii/issues/6974) Auto budget amount fix.
- Date validation in routes
- Shorter titles in object groups
### API
- Various API fixes
## 6.0.0-alpha.1 - 2023-01-16
This is the first release of the new 6.0.0 series of Firefly III. It should upgrade the database automatically BUT please make a backup of your database first! I guarantee nothing.

View File

@ -90,7 +90,7 @@
"jc5/recovery": "^2",
"laravel/framework": "^9",
"laravel/passport": "11.*",
"laravel/sanctum": "^3.0",
"laravel/sanctum": "^3.2",
"laravel/slack-notification-channel": "^2.4",
"laravel/ui": "^4.1",
"laravelcollective/html": "6.*",
@ -99,7 +99,7 @@
"league/fractal": "0.*",
"nunomaduro/collision": "^6.3",
"pragmarx/google2fa": "^8.0",
"predis/predis": "^2.0",
"predis/predis": "^2.1",
"psr/log": "<4",
"ramsey/uuid": "^4.7",
"rcrowe/twigbridge": "^0.14",
@ -174,7 +174,6 @@
"@php artisan firefly-iii:migrate-tag-locations",
"@php artisan firefly-iii:migrate-recurrence-type",
"@php artisan firefly-iii:upgrade-liabilities",
"@php artisan firefly-iii:create-group-memberships",
"@php artisan firefly-iii:liabilities-600",
"@php artisan firefly-iii:fix-piggies",
"@php artisan firefly-iii:create-link-types",
@ -198,6 +197,7 @@
"@php artisan firefly-iii:fix-transaction-types",
"@php artisan firefly-iii:fix-frontpage-accounts",
"@php artisan firefly-iii:fix-ibans",
"@php artisan firefly-iii:create-group-memberships",
"@php artisan firefly-iii:report-empty-objects",
"@php artisan firefly-iii:report-sum",
"@php artisan firefly-iii:restore-oauth-keys",

509
composer.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -106,8 +106,8 @@ return [
'webhooks' => true,
'handle_debts' => true,
],
'version' => '6.0.0-alpha.1',
'api_version' => '2.0.0-alpha.1',
'version' => '6.0.0-alpha.2',
'api_version' => '2.0.0-alpha.2',
'db_version' => 19,
// generic settings
@ -128,8 +128,6 @@ return [
'allow_webhooks' => env('ALLOW_WEBHOOKS', false),
// email flags
'send_registration_mail' => env('SEND_REGISTRATION_MAIL', true),
'warn_new_ip' => env('SEND_LOGIN_NEW_IP_WARNING', true),
'send_report_journals' => envNonEmpty('SEND_REPORT_JOURNALS', true),
// info for demo site

View File

@ -70,11 +70,6 @@ class TransactionCurrencySeeder extends Seeder
$currencies[] = ['code' => 'RUB', 'name' => 'Russian ruble', 'symbol' => '₽', 'decimal_places' => 2];
$currencies[] = ['code' => 'INR', 'name' => 'Indian rupee', 'symbol' => '₹', 'decimal_places' => 2];
// international currencies
$currencies[] = ['code' => 'XBT', 'name' => 'Bitcoin', 'symbol' => '₿', 'decimal_places' => 8];
$currencies[] = ['code' => 'BCH', 'name' => 'Bitcoin cash', 'symbol' => '₿C', 'decimal_places' => 8];
$currencies[] = ['code' => 'ETH', 'name' => 'Ethereum', 'symbol' => 'Ξ', 'decimal_places' => 12];
// PLEASE ADD NEW CURRENCIES BELOW THIS LINE
$currencies[] = ['code' => 'ILS', 'name' => 'Israeli new shekel', 'symbol' => '₪', 'decimal_places' => 2];
$currencies[] = ['code' => 'CHF', 'name' => 'Swiss franc', 'symbol' => 'CHF', 'decimal_places' => 2];

View File

@ -11,7 +11,7 @@
},
"dependencies": {
"@popperjs/core": "^2.11.2",
"@quasar/extras": "^1.15.9",
"@quasar/extras": "^1.15.10",
"apexcharts": "^3.32.1",
"axios": "^0.21.1",
"axios-cache-adapter": "^2.7.3",
@ -26,7 +26,7 @@
},
"devDependencies": {
"@babel/eslint-parser": "^7.13.14",
"@quasar/app-webpack": "^3.6.2",
"@quasar/app-webpack": "^3.7.1",
"@types/node": "^12.20.21",
"@typescript-eslint/eslint-plugin": "^4.16.1",
"@typescript-eslint/parser": "^4.16.1",

View File

@ -55,7 +55,7 @@ module.exports = configure(function (ctx) {
extras: [
// 'ionicons-v4',
// 'mdi-v5',
'fontawesome-v5',
'fontawesome-v6',
// 'eva-icons',
// 'themify',
// 'line-awesome',
@ -126,7 +126,7 @@ module.exports = configure(function (ctx) {
},
lang: 'en-US', // Quasar language pack
iconSet: 'fontawesome-v5',
iconSet: 'fontawesome-v6',
// For special cases outside of where the auto-import strategy can have an impact
// (like functional components as one of the examples),

View File

@ -19,19 +19,9 @@
-->
<template>
<div class="q-mt-sm q-mr-sm">
<q-card bordered>
<q-item>
<q-item-section>
<q-item-label><strong>Bla bla accounts</strong></q-item-label>
</q-item-section>
</q-item>
<q-separator/>
<q-card flat bordered>
<ApexChart ref="chart" :options="options" :series="series" height="350" type="line"></ApexChart>
</q-card>
</div>
<div>
</div>
</template>
<script>

View File

@ -22,14 +22,13 @@
<!-- TODO most left? q-mr-sm -->
<!-- TODO middle? dan q-mx-sm -->
<!-- TODO right? dan q-ml-sm -->
<div class="q-mr-sm">
<q-card bordered>
<q-card bordered flat class="fit">
<q-item>
<q-item-section>
<q-item-label><strong>{{ $t('firefly.bills_to_pay') }}</strong></q-item-label>
<q-item-label><strong>{{ $t('firefly.bills') }} </strong></q-item-label>
</q-item-section>
</q-item>
<q-separator/>
<q-separator />
<q-card-section horizontal>
<q-card-section>
<q-circular-progress
@ -63,7 +62,6 @@
</q-card-section>
</q-card-section>
</q-card>
</div>
</template>
<script>

View File

@ -22,8 +22,7 @@
<!-- TODO most left? q-mr-sm -->
<!-- TODO middle? dan q-mx-sm -->
<!-- TODO right? dan q-ml-sm -->
<div class="q-ml-sm">
<q-card bordered>
<q-card bordered flat class="fit">
<q-item>
<q-item-section>
<q-item-label><strong>{{ $t('firefly.net_worth') }}</strong></q-item-label>
@ -47,7 +46,6 @@
</q-card-section>
</q-card-section>
</q-card>
</div>
</template>
<script>

View File

@ -22,8 +22,7 @@
<!-- TODO most left? q-mr-sm -->
<!-- TODO middle? dan q-mx-sm -->
<!-- TODO right? dan q-ml-sm -->
<div class="q-mx-sm">
<q-card bordered>
<q-card bordered flat>
<q-item>
<q-item-section>
<q-item-label><strong>{{ $t('firefly.left_to_spend') }}</strong></q-item-label>
@ -66,7 +65,6 @@
</q-card-section>
</q-card-section>
</q-card>
</div>
</template>
<script>

View File

@ -24,7 +24,6 @@
:columns="columns"
:loading="loading"
:rows="rows"
:title="title"
class="q-ma-md"
row-key="group_id"
@request="onRequest"

View File

@ -60,6 +60,7 @@ export default {
"liabilities_accounts": "\u0417\u0430\u0434\u044a\u043b\u0436\u0435\u043d\u0438\u044f"
},
"firefly": {
"administration_index": "Financial administration",
"actions": "\u0414\u0435\u0439\u0441\u0442\u0432\u0438\u044f",
"edit": "\u041f\u0440\u043e\u043c\u0435\u043d\u0438",
"delete": "\u0418\u0437\u0442\u0440\u0438\u0439",

View File

@ -60,6 +60,7 @@ export default {
"liabilities_accounts": "Passius"
},
"firefly": {
"administration_index": "Administraci\u00f3 financera",
"actions": "Accions",
"edit": "Editar",
"delete": "Eliminar",

View File

@ -60,6 +60,7 @@ export default {
"liabilities_accounts": "Liabilities"
},
"firefly": {
"administration_index": "Financial administration",
"actions": "Akce",
"edit": "Upravit",
"delete": "Odstranit",

View File

@ -60,6 +60,7 @@ export default {
"liabilities_accounts": "G\u00e6ld"
},
"firefly": {
"administration_index": "Financial administration",
"actions": "Handlinger",
"edit": "Rediger",
"delete": "Slet",

View File

@ -60,6 +60,7 @@ export default {
"liabilities_accounts": "Verbindlichkeiten"
},
"firefly": {
"administration_index": "Finanzverwaltung",
"actions": "Aktionen",
"edit": "Bearbeiten",
"delete": "L\u00f6schen",
@ -161,7 +162,7 @@ export default {
"rule_action_clear_notes_choice": "Alle Notizen entfernen",
"rule_action_set_notes_choice": "Setze Notizen auf ..",
"rule_action_link_to_bill_choice": "Mit einer Rechnung verkn\u00fcpfen..",
"rule_action_convert_deposit_choice": "Buchung in eine Einzahlung umwandeln",
"rule_action_convert_deposit_choice": "Buchung in eine Einnahme umwandeln",
"rule_action_convert_withdrawal_choice": "Buchung in eine Ausgabe umwandeln",
"rule_action_convert_transfer_choice": "Buchung in eine Umbuchung umwandeln",
"placeholder": "[Placeholder]",

View File

@ -60,6 +60,7 @@ export default {
"liabilities_accounts": "\u03a5\u03c0\u03bf\u03c7\u03c1\u03b5\u03ce\u03c3\u03b5\u03b9\u03c2"
},
"firefly": {
"administration_index": "Financial administration",
"actions": "\u0395\u03bd\u03ad\u03c1\u03b3\u03b5\u03b9\u03b5\u03c2",
"edit": "\u0395\u03c0\u03b5\u03be\u03b5\u03c1\u03b3\u03b1\u03c3\u03af\u03b1",
"delete": "\u0394\u03b9\u03b1\u03b3\u03c1\u03b1\u03c6\u03ae",

View File

@ -60,6 +60,7 @@ export default {
"liabilities_accounts": "Liabilities"
},
"firefly": {
"administration_index": "Financial administration",
"actions": "Actions",
"edit": "Edit",
"delete": "Delete",

View File

@ -60,6 +60,7 @@ export default {
"liabilities_accounts": "Liabilities"
},
"firefly": {
"administration_index": "Financial administration",
"actions": "Actions",
"edit": "Edit",
"delete": "Delete",

View File

@ -60,6 +60,7 @@ export default {
"liabilities_accounts": "Pasivos"
},
"firefly": {
"administration_index": "Administraci\u00f3n financiera",
"actions": "Acciones",
"edit": "Editar",
"delete": "Eliminar",
@ -72,7 +73,7 @@ export default {
"submission_options": "Submission options",
"apply_rules_checkbox": "Apply rules",
"fire_webhooks_checkbox": "Fire webhooks",
"newDeposit": "Nuevo deposito",
"newDeposit": "Nuevo ingreso",
"newWithdrawal": "Nuevo gasto",
"bills_paid": "Facturas pagadas",
"left_to_spend": "Disponible para gastar",
@ -161,7 +162,7 @@ export default {
"rule_action_clear_notes_choice": "Eliminar cualquier nota",
"rule_action_set_notes_choice": "Set notes to ..",
"rule_action_link_to_bill_choice": "Link to a bill ..",
"rule_action_convert_deposit_choice": "Convierta esta transacci\u00f3n en un dep\u00f3sito",
"rule_action_convert_deposit_choice": "Convertir transacci\u00f3n en un ingreso",
"rule_action_convert_withdrawal_choice": "Convierta esta transacci\u00f3n en un gasto",
"rule_action_convert_transfer_choice": "Convierta la transacci\u00f3n a una transferencia",
"placeholder": "[Placeholder]",

View File

@ -60,6 +60,7 @@ export default {
"liabilities_accounts": "Lainat"
},
"firefly": {
"administration_index": "Financial administration",
"actions": "Toiminnot",
"edit": "Muokkaa",
"delete": "Poista",

View File

@ -60,6 +60,7 @@ export default {
"liabilities_accounts": "Passifs"
},
"firefly": {
"administration_index": "Administration financi\u00e8re",
"actions": "Actions",
"edit": "Modifier",
"delete": "Supprimer",

View File

@ -60,6 +60,7 @@ export default {
"liabilities_accounts": "Liabilities"
},
"firefly": {
"administration_index": "Financial administration",
"actions": "M\u0171veletek",
"edit": "Szerkeszt\u00e9s",
"delete": "T\u00f6rl\u00e9s",

View File

@ -60,6 +60,7 @@ export default {
"liabilities_accounts": "Kewajiban"
},
"firefly": {
"administration_index": "Financial administration",
"actions": "Tindakan",
"edit": "Edit",
"delete": "Menghapus",

View File

@ -60,6 +60,7 @@ export default {
"liabilities_accounts": "Passivit\u00e0"
},
"firefly": {
"administration_index": "Amministrazione finanziaria",
"actions": "Azioni",
"edit": "Modifica",
"delete": "Elimina",
@ -84,10 +85,10 @@ export default {
"rule_trigger_source_account_ends_choice": "Il nome del conto di origine termina con..",
"rule_trigger_source_account_is_choice": "Il nome del conto di origine \u00e8..",
"rule_trigger_source_account_contains_choice": "Il nome del conto di origine contiene...",
"rule_trigger_account_id_choice": "Either account ID is exactly..",
"rule_trigger_account_id_choice": "Entrambi gli ID del conto sono esattamente..",
"rule_trigger_source_account_id_choice": "L'ID del conto di origine \u00e8 esattamente...",
"rule_trigger_destination_account_id_choice": "L'ID del conto di destinazione \u00e8 esattamente...",
"rule_trigger_account_is_cash_choice": "Either account is cash",
"rule_trigger_account_is_cash_choice": "Entrambi i conti sono contanti",
"rule_trigger_source_is_cash_choice": "Il conte di origine \u00e8 un conto (in contanti)",
"rule_trigger_destination_is_cash_choice": "Il conto destinazione \u00e8 un conto (in contanti)",
"rule_trigger_source_account_nr_starts_choice": "Il numero del conto di origine \/ l'IBAN inizia con...",
@ -105,13 +106,13 @@ export default {
"rule_trigger_transaction_type_choice": "La transazione \u00e8 di tipo...",
"rule_trigger_category_is_choice": "La categoria \u00e8...",
"rule_trigger_amount_less_choice": "L'importo \u00e8 inferiore a...",
"rule_trigger_amount_is_choice": "Amount is..",
"rule_trigger_amount_is_choice": "L'importo \u00e8..",
"rule_trigger_amount_more_choice": "L'importo \u00e8 pi\u00f9 di...",
"rule_trigger_description_starts_choice": "La descrizione inizia con...",
"rule_trigger_description_ends_choice": "La descrizione termina con...",
"rule_trigger_description_contains_choice": "La descrizione contiene...",
"rule_trigger_description_is_choice": "La descrizione \u00e8...",
"rule_trigger_date_on_choice": "Transaction date is..",
"rule_trigger_date_on_choice": "La data della transizione \u00e8..",
"rule_trigger_date_before_choice": "La data della transazione \u00e8 antecedente al...",
"rule_trigger_date_after_choice": "La data della transazione \u00e8 successiva al...",
"rule_trigger_created_at_on_choice": "Transaction was made on..",

View File

@ -60,6 +60,7 @@ export default {
"liabilities_accounts": "\u8ca0\u50b5"
},
"firefly": {
"administration_index": "Financial administration",
"actions": "\u64cd\u4f5c",
"edit": "\u7de8\u96c6",
"delete": "\u524a\u9664",

View File

@ -60,6 +60,7 @@ export default {
"liabilities_accounts": "Gjeld"
},
"firefly": {
"administration_index": "Financial administration",
"actions": "Handlinger",
"edit": "Rediger",
"delete": "Slett",

View File

@ -60,6 +60,7 @@ export default {
"liabilities_accounts": "Passiva"
},
"firefly": {
"administration_index": "Financi\u00eble administratie",
"actions": "Acties",
"edit": "Wijzig",
"delete": "Verwijder",
@ -69,9 +70,9 @@ export default {
"new_budget": "Nieuw budget",
"new_asset_account": "Nieuwe betaalrekening",
"newTransfer": "Nieuwe overschrijving",
"submission_options": "Submission options",
"apply_rules_checkbox": "Apply rules",
"fire_webhooks_checkbox": "Fire webhooks",
"submission_options": "Inzending opties",
"apply_rules_checkbox": "Regels toepassen",
"fire_webhooks_checkbox": "Webhooks starten",
"newDeposit": "Nieuwe inkomsten",
"newWithdrawal": "Nieuwe uitgave",
"bills_paid": "Betaalde contracten",
@ -142,25 +143,25 @@ export default {
"rule_trigger_any_external_url_choice": "De transactie heeft een externe URL",
"rule_trigger_no_external_url_choice": "De transactie heeft geen externe URL",
"rule_trigger_id_choice": "Transactie-ID is..",
"rule_action_delete_transaction_choice": "DELETE transaction(!)",
"rule_action_set_category_choice": "Set category to ..",
"rule_action_delete_transaction_choice": "VERWIJDER transactie(!)",
"rule_action_set_category_choice": "Stel categorie in op ..",
"rule_action_clear_category_choice": "Geef geen categorie",
"rule_action_set_budget_choice": "Set budget to ..",
"rule_action_set_budget_choice": "Stel budget in op ..",
"rule_action_clear_budget_choice": "Maak budget-veld leeg",
"rule_action_add_tag_choice": "Add tag ..",
"rule_action_remove_tag_choice": "Remove tag ..",
"rule_action_add_tag_choice": "Voeg tag toe ..",
"rule_action_remove_tag_choice": "Haal tag weg ..",
"rule_action_remove_all_tags_choice": "Haal alle tags weg",
"rule_action_set_description_choice": "Set description to ..",
"rule_action_update_piggy_choice": "Add \/ remove transaction amount in piggy bank ..",
"rule_action_append_description_choice": "Append description with ..",
"rule_action_prepend_description_choice": "Prepend description with ..",
"rule_action_set_source_account_choice": "Set source account to ..",
"rule_action_set_destination_account_choice": "Set destination account to ..",
"rule_action_append_notes_choice": "Append notes with ..",
"rule_action_prepend_notes_choice": "Prepend notes with ..",
"rule_action_set_description_choice": "Stel beschrijving in op ..",
"rule_action_update_piggy_choice": "Voeg toe \/ verwijder het transactiebedrag in spaarpotje ..",
"rule_action_append_description_choice": "Zet .. achter de omschrijving",
"rule_action_prepend_description_choice": "Zet .. voor de omschrijving",
"rule_action_set_source_account_choice": "Verander bronrekening naar ..",
"rule_action_set_destination_account_choice": "Verander doelrekening naar ..",
"rule_action_append_notes_choice": "Vul notitie aan met ..",
"rule_action_prepend_notes_choice": "Zet .. voor notitie",
"rule_action_clear_notes_choice": "Verwijder notitie",
"rule_action_set_notes_choice": "Set notes to ..",
"rule_action_link_to_bill_choice": "Link to a bill ..",
"rule_action_set_notes_choice": "Stel notities in op ..",
"rule_action_link_to_bill_choice": "Link naar een contract ..",
"rule_action_convert_deposit_choice": "Verander de transactie in inkomsten",
"rule_action_convert_withdrawal_choice": "Verander de transactie in een uitgave",
"rule_action_convert_transfer_choice": "Verander de transactie in een overschrijving",

View File

@ -60,6 +60,7 @@ export default {
"liabilities_accounts": "Zobowi\u0105zania"
},
"firefly": {
"administration_index": "Financial administration",
"actions": "Akcje",
"edit": "Modyfikuj",
"delete": "Usu\u0144",

View File

@ -60,6 +60,7 @@ export default {
"liabilities_accounts": "Passivos"
},
"firefly": {
"administration_index": "Financial administration",
"actions": "A\u00e7\u00f5es",
"edit": "Editar",
"delete": "Apagar",
@ -74,12 +75,12 @@ export default {
"fire_webhooks_checkbox": "Acionar webhooks",
"newDeposit": "Novo dep\u00f3sito",
"newWithdrawal": "Nova despesa",
"bills_paid": "Contas pagas",
"bills_paid": "Faturas pagas",
"left_to_spend": "Restante para gastar",
"no_budget": "(sem or\u00e7amento)",
"budgeted": "Or\u00e7ado",
"spent": "Gasto",
"no_bill": "(sem conta)",
"no_bill": "(sem fatura)",
"rule_trigger_source_account_starts_choice": "Nome da conta de origem come\u00e7a com..",
"rule_trigger_source_account_ends_choice": "O nome da conta de origem termina com..",
"rule_trigger_source_account_is_choice": "Nome da conta de origem \u00e9..",
@ -126,7 +127,7 @@ export default {
"rule_trigger_has_no_budget_choice": "N\u00e3o tem or\u00e7amento",
"rule_trigger_has_any_budget_choice": "Tem um or\u00e7amento (qualquer)",
"rule_trigger_has_no_bill_choice": "N\u00e3o tem nenhuma conta",
"rule_trigger_has_any_bill_choice": "Tem uma conta (qualquer)",
"rule_trigger_has_any_bill_choice": "Tem uma fatura (qualquer)",
"rule_trigger_has_no_tag_choice": "N\u00e3o tem tag(s)",
"rule_trigger_has_any_tag_choice": "Tem uma ou mais tags (qualquer)",
"rule_trigger_any_notes_choice": "Tem notas (qualquer)",
@ -135,32 +136,32 @@ export default {
"rule_trigger_notes_contains_choice": "As notas cont\u00eam..",
"rule_trigger_notes_starts_choice": "As notas come\u00e7am com..",
"rule_trigger_notes_ends_choice": "As notas terminam com..",
"rule_trigger_bill_is_choice": "Conta \u00e9..",
"rule_trigger_bill_is_choice": "Fatura \u00e9..",
"rule_trigger_external_id_is_choice": "ID externo \u00e9..",
"rule_trigger_internal_reference_is_choice": "Refer\u00eancia interna \u00e9..",
"rule_trigger_journal_id_choice": "ID do livro de transa\u00e7\u00e3o \u00e9..",
"rule_trigger_any_external_url_choice": "A transa\u00e7\u00e3o tem um link externo",
"rule_trigger_no_external_url_choice": "A transa\u00e7\u00e3o n\u00e3o tem um link externo",
"rule_trigger_id_choice": "O identificador da transa\u00e7\u00e3o \u00e9..",
"rule_action_delete_transaction_choice": "DELETE transaction(!)",
"rule_action_set_category_choice": "Set category to ..",
"rule_action_delete_transaction_choice": "EXCLUIR transa\u00e7\u00e3o(!)",
"rule_action_set_category_choice": "Definir a categoria para ..",
"rule_action_clear_category_choice": "Limpar qualquer categoria",
"rule_action_set_budget_choice": "Set budget to ..",
"rule_action_set_budget_choice": "Definir or\u00e7amento para ..",
"rule_action_clear_budget_choice": "Limpar qualquer or\u00e7amento",
"rule_action_add_tag_choice": "Add tag ..",
"rule_action_remove_tag_choice": "Remove tag ..",
"rule_action_add_tag_choice": "Adicionar tag ..",
"rule_action_remove_tag_choice": "Remover tag ..",
"rule_action_remove_all_tags_choice": "Remover todas as tags",
"rule_action_set_description_choice": "Set description to ..",
"rule_action_update_piggy_choice": "Add \/ remove transaction amount in piggy bank ..",
"rule_action_append_description_choice": "Append description with ..",
"rule_action_prepend_description_choice": "Prepend description with ..",
"rule_action_set_source_account_choice": "Set source account to ..",
"rule_action_set_destination_account_choice": "Set destination account to ..",
"rule_action_append_notes_choice": "Append notes with ..",
"rule_action_prepend_notes_choice": "Prepend notes with ..",
"rule_action_set_description_choice": "Definir descri\u00e7\u00e3o para ..",
"rule_action_update_piggy_choice": "Adicionar \/ remover o valor da transa\u00e7\u00e3o no cofrinho ..",
"rule_action_append_description_choice": "Adicionar descri\u00e7\u00e3o com ..",
"rule_action_prepend_description_choice": "Adicionar (ao come\u00e7o) descri\u00e7\u00e3o com ..",
"rule_action_set_source_account_choice": "Definir a conta de origem para ..",
"rule_action_set_destination_account_choice": "Definir a conta de destino para ..",
"rule_action_append_notes_choice": "Adicionar notas com ..",
"rule_action_prepend_notes_choice": "Adicionar (ao come\u00e7o) notas com ..",
"rule_action_clear_notes_choice": "Remover quaisquer notas",
"rule_action_set_notes_choice": "Set notes to ..",
"rule_action_link_to_bill_choice": "Link to a bill ..",
"rule_action_set_notes_choice": "Defina notas para..",
"rule_action_link_to_bill_choice": "Vincular a uma fatura ..",
"rule_action_convert_deposit_choice": "Converter esta transfer\u00eancia em entrada",
"rule_action_convert_withdrawal_choice": "Converter esta transa\u00e7\u00e3o para uma sa\u00edda",
"rule_action_convert_transfer_choice": "Converter esta transa\u00e7\u00e3o para transfer\u00eancia",
@ -214,7 +215,7 @@ export default {
"budgets": "Or\u00e7amentos",
"subscriptions": "Assinaturas",
"welcome_back": "O que est\u00e1 acontecendo?",
"bills_to_pay": "Contas a pagar",
"bills_to_pay": "Faturas a pagar",
"net_worth": "Valor L\u00edquido",
"pref_last365": "Ano passado",
"pref_last90": "\u00daltimos 90 dias",

View File

@ -54,15 +54,16 @@ export default {
"title_deposit": "Receita \/ renda",
"title_transfer": "Transfer\u00eancias",
"title_transfers": "Transfer\u00eancias",
"asset_accounts": "Conta de activos",
"asset_accounts": "Conta de ativos",
"expense_accounts": "Conta de despesas",
"revenue_accounts": "Conta de receitas",
"liabilities_accounts": "Passivos"
"liabilities_accounts": "Conta de passivos"
},
"firefly": {
"administration_index": "Financial administration",
"actions": "A\u00e7\u00f5es",
"edit": "Alterar",
"delete": "Apagar",
"edit": "Editar",
"delete": "Eliminar",
"reconcile": "Reconciliar",
"create_new_asset": "Criar nova conta de activos",
"confirm_action": "Confirmar a\u00e7\u00e3o",
@ -180,10 +181,10 @@ export default {
"repeat_freq_quarterly": "trimestral",
"repeat_freq_monthly": "mensalmente",
"repeat_freq_weekly": "semanalmente",
"single_split": "Dividir",
"single_split": "Divis\u00e3o",
"asset_accounts": "Conta de activos",
"expense_accounts": "Conta de despesas",
"liabilities_accounts": "Passivos",
"liabilities_accounts": "Conta de passivos",
"undefined_accounts": "Accounts",
"name": "Nome",
"revenue_accounts": "Conta de receitas",
@ -213,7 +214,7 @@ export default {
"balance": "Saldo",
"budgets": "Or\u00e7amentos",
"subscriptions": "Subscri\u00e7\u00f5es",
"welcome_back": "Tudo bem?",
"welcome_back": "Painel de controlo",
"bills_to_pay": "Faturas a pagar",
"net_worth": "Patrim\u00f3nio liquido",
"pref_last365": "Last year",

View File

@ -60,6 +60,7 @@ export default {
"liabilities_accounts": "Liabilities"
},
"firefly": {
"administration_index": "Financial administration",
"actions": "Ac\u021biuni",
"edit": "Editeaz\u0103",
"delete": "\u0218terge",

View File

@ -60,6 +60,7 @@ export default {
"liabilities_accounts": "\u0414\u043e\u043b\u0433\u0438"
},
"firefly": {
"administration_index": "Financial administration",
"actions": "\u0414\u0435\u0439\u0441\u0442\u0432\u0438\u044f",
"edit": "\u0418\u0437\u043c\u0435\u043d\u0438\u0442\u044c",
"delete": "\u0423\u0434\u0430\u043b\u0438\u0442\u044c",

View File

@ -60,6 +60,7 @@ export default {
"liabilities_accounts": "Liabilities"
},
"firefly": {
"administration_index": "Financial administration",
"actions": "Akcie",
"edit": "Upravi\u0165",
"delete": "Odstr\u00e1ni\u0165",

View File

@ -60,6 +60,7 @@ export default {
"liabilities_accounts": "Obveznosti"
},
"firefly": {
"administration_index": "Financial administration",
"actions": "Dejanja",
"edit": "uredi",
"delete": "izbri\u0161i",

View File

@ -60,6 +60,7 @@ export default {
"liabilities_accounts": "Skulder"
},
"firefly": {
"administration_index": "Financial administration",
"actions": "\u00c5tg\u00e4rder",
"edit": "Redigera",
"delete": "Ta bort",

View File

@ -60,6 +60,7 @@ export default {
"liabilities_accounts": "Sorumluluk"
},
"firefly": {
"administration_index": "Financial administration",
"actions": "Eylemler",
"edit": "D\u00fczenle",
"delete": "Sil",

View File

@ -60,6 +60,7 @@ export default {
"liabilities_accounts": "\u0417\u043e\u0431\u043e\u0432'\u044f\u0437\u0430\u043d\u043d\u044f"
},
"firefly": {
"administration_index": "Financial administration",
"actions": "\u0414\u0456\u0457",
"edit": "\u0420\u0435\u0434\u0430\u0433\u0443\u0432\u0430\u0442\u0438",
"delete": "\u0412\u0438\u0434\u0430\u043b\u0438\u0442\u0438",

View File

@ -60,6 +60,7 @@ export default {
"liabilities_accounts": "N\u1ee3 ph\u1ea3i tr\u1ea3"
},
"firefly": {
"administration_index": "Financial administration",
"actions": "H\u00e0nh \u0111\u1ed9ng",
"edit": "S\u1eeda",
"delete": "X\u00f3a",

View File

@ -60,6 +60,7 @@ export default {
"liabilities_accounts": "\u503a\u52a1"
},
"firefly": {
"administration_index": "Financial administration",
"actions": "\u64cd\u4f5c",
"edit": "\u7f16\u8f91",
"delete": "\u5220\u9664",

View File

@ -60,6 +60,7 @@ export default {
"liabilities_accounts": "Liabilities"
},
"firefly": {
"administration_index": "Financial administration",
"actions": "\u64cd\u4f5c",
"edit": "\u7de8\u8f2f",
"delete": "\u522a\u9664",

View File

@ -18,12 +18,26 @@
- along with this program. If not, see <https://www.gnu.org/licenses/>.
-->
<!--
instructions for padding and margin
menu: top padding under top bar: q-pt-xs (padding top, xs)
page container: q-ma-xs (margin all, xs) AND q-mb-md to give the page content some space.
TODO main DIV always use q-ma-md for the main holder
TODO rows use a q-mb-sm to give them space
-->
<template>
<q-layout view="hHh lpR fFf">
<q-header class="bg-primary text-white" elevated>
<q-header reveal class="bg-primary text-white">
<q-toolbar>
<q-btn dense flat icon="fas fa-bars" round @click="toggleLeftDrawer"/>
<q-btn flat icon="fas fa-bars" round @click="toggleLeftDrawer"/>
<q-toolbar-title>
<q-avatar>
@ -32,7 +46,6 @@
Firefly III
</q-toolbar-title>
<q-select
ref="search" v-model="search" :stack-label="false" class="q-mx-xs" color="black" dark
dense
@ -101,7 +114,7 @@
<q-item-section>Currencies</q-item-section>
</q-item>
<q-item :to="{ name: 'admin.index' }" clickable>
<q-item-section>Administration</q-item-section>
<q-item-section>System settings</q-item-section>
</q-item>
</q-list>
</q-menu>
@ -119,9 +132,12 @@
<q-item :to="{ name: 'profile.index' }" clickable>
<q-item-section> Profile</q-item-section>
</q-item>
<q-item :to="{ name: 'profile.daa' }" clickable>
<q-item :to="{ name: 'profile.data' }" clickable>
<q-item-section> Data management</q-item-section>
</q-item>
<q-item :to="{ name: 'administration.index' }" clickable>
<q-item-section>Administration management</q-item-section>
</q-item>
<q-item :to="{ name: 'preferences.index' }" clickable>
<q-item-section>Preferences</q-item-section>
</q-item>
@ -137,9 +153,9 @@
</q-btn>
</q-toolbar>
</q-header>
<q-drawer v-model="leftDrawerOpen" bordered show-if-above side="left">
<q-drawer show-if-above v-model="leftDrawerOpen" side="left" bordered>
<q-scroll-area class="fit">
<div class="q-pa-md">
<div class="q-pt-md">
<q-list>
<q-item v-ripple :to="{ name: 'index' }" clickable>
<q-item-section avatar>
@ -180,7 +196,7 @@
icon="fas fa-exchange-alt"
label="Transactions"
>
<q-item v-ripple :inset-level="1" :to="{ name: 'transactions.index', params: {type: 'withdrawal'} }"
<q-item v-ripple :inset-level="1" :to="{ name: 'transactions.index', params: {type: 'withdrawal'} }"
clickable>
<q-item-section>
Withdrawals
@ -199,6 +215,13 @@
Transfers
</q-item-section>
</q-item>
<q-item v-ripple :inset-level="1" :to="{ name: 'transactions.index', params: {type: 'all'} }"
clickable>
<q-item-section>
All transactions
</q-item-section>
</q-item>
</q-expansion-item>
@ -229,7 +252,7 @@
icon="fas fa-credit-card"
label="Accounts"
>
<q-item v-ripple :inset-level="1" :to="{ name: 'accounts.index', params: {type: 'asset'} }" clickable>
<q-item v-ripple :inset-level="1" :to="{ name: 'accounts.index', params: {type: 'asset'} }" clickable>
<q-item-section>
Asset accounts
</q-item-section>
@ -291,12 +314,13 @@
<q-page-container>
<Alert></Alert>
<!-- breadcrumb, page title? -->
<div class="q-ma-md">
<div class="row">
<div class="col-6">
<h4 class="q-ma-none q-pa-none">{{ $t($route.meta.pageTitle || 'firefly.welcome_back') }}</h4>
<h4 class="q-ma-none q-pa-none">
<em class="fa-solid fa-fire"></em>
{{ $t($route.meta.pageTitle || 'firefly.welcome_back') }}</h4>
</div>
<div class="col-6">
<q-breadcrumbs align="right">
@ -308,13 +332,13 @@
</div>
</div>
<router-view/>
<router-view />
</q-page-container>
<q-footer class="bg-grey-8 text-white" elevated>
<q-footer class="bg-grey-8 text-white" bordered>
<q-toolbar>
<div>
<small>Firefly III v TODO &copy; James Cole, AGPL-3.0-or-later.</small>
<small>Firefly III v v6.0.0-alpha.2 &copy; James Cole, AGPL-3.0-or-later.</small>
</div>
</q-toolbar>
</q-footer>
@ -327,6 +351,7 @@
import {defineComponent, ref} from 'vue';
import DateRange from "../components/DateRange";
import Alert from '../components/Alert';
import {useQuasar} from "quasar";
export default defineComponent(
{
@ -339,7 +364,7 @@ export default defineComponent(
setup() {
const leftDrawerOpen = ref(true)
const search = ref('')
const $q = useQuasar();
return {
search,
leftDrawerOpen,

View File

@ -0,0 +1,60 @@
<!--
- Index.vue
- Copyright (c) 2023 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/>.
-->
<template>
<div>
<div class="q-ma-md">
<div class="row q-mb-sm">
<div class="col">
<div class="q-mt-sm q-mr-sm">
<q-card bordered>
<q-item>
<q-item-section>
<q-item-label><strong>Financial administration</strong></q-item-label>
</q-item-section>
</q-item>
<q-separator/>
<q-card-section>
<p>
Bla bla bla
List of groups.
Edit members, edit group, delete group.
add new administration.
switch to new administration.
</p>
</q-card-section>
</q-card>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
export default {
name: "Index"
}
</script>
<style scoped>
</style>

View File

@ -1,207 +0,0 @@
<!--
- Boxes.vue
- 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/>.
-->
<template>
<div class="row">
<div class="col-4 q-pr-sm q-pr-sm">
<q-card bordered>
<q-card-section class="q-pt-xs">
<div class="text-overline">
{{ $t('firefly.bills_to_pay') }}
<span class="float-right">
<span class="text-grey-4 fas fa-redo-alt" style="cursor: pointer;" @click="triggerForcedUpgrade"></span>
</span>
</div>
</q-card-section>
<q-card-section class="q-pt-xs">
<span v-for="balance in prefBillsUnpaid">{{ balance.value_parsed }}</span>
<span v-for="(bill, index) in notPrefBillsUnpaid">
{{ bill.value_parsed }}<span v-if="index+1 !== notPrefBillsUnpaid.length">, </span>
</span>
</q-card-section>
</q-card>
</div>
<div class="col-4 q-pr-sm q-pl-sm">
<q-card bordered>
<q-card-section class="q-pt-xs">
<div class="text-overline">
{{ $t('firefly.left_to_spend') }}
<span class="float-right">
<span class="text-grey-4 fas fa-redo-alt" style="cursor: pointer;" @click="triggerForcedUpgrade"></span>
</span>
</div>
</q-card-section>
<q-card-section class="q-pt-xs">
<!-- left to spend in preferred currency -->
<span v-for="left in prefLeftToSpend" :title="left.sub_title">{{ left.value_parsed }}</span>
<span v-for="(left, index) in notPrefLeftToSpend">
{{ left.value_parsed }}<span v-if="index+1 !== notPrefLeftToSpend.length">, </span>
</span>
</q-card-section>
</q-card>
</div>
<div class="col-4 q-pl-sm">
<q-card bordered>
<q-card-section class="q-pt-xs">
<div class="text-overline">
{{ $t('firefly.net_worth') }}
<span class="float-right">
<span class="text-grey-4 fas fa-redo-alt" style="cursor: pointer;" @click="triggerForcedUpgrade"></span>
</span>
</div>
</q-card-section>
<q-card-section class="q-pt-xs">
<span v-for="nw in prefNetWorth" :title="nw.sub_title">{{ nw.value_parsed }}</span>
<span v-for="(nw, index) in notPrefNetWorth">
{{ nw.value_parsed }}<span v-if="index+1 !== notPrefNetWorth.length">, </span>
</span>
<span v-if="0===notPrefNetWorth.length">&nbsp;</span>
</q-card-section>
</q-card>
</div>
</div>
</template>
<script>
import Basic from "src/api/summary/basic";
import {useFireflyIIIStore} from '../../stores/fireflyiii'
export default {
name: 'Boxes',
computed: {
prefBillsUnpaid: function () {
return this.filterOnCurrency(this.billsUnpaid);
},
notPrefBillsUnpaid: function () {
return this.filterOnNotCurrency(this.billsUnpaid);
},
prefLeftToSpend: function () {
return this.filterOnCurrency(this.leftToSpend);
},
notPrefLeftToSpend: function () {
return this.filterOnNotCurrency(this.leftToSpend);
},
prefNetWorth: function () {
return this.filterOnCurrency(this.netWorth);
},
notPrefNetWorth: function () {
return this.filterOnNotCurrency(this.netWorth);
},
},
created() {
},
data() {
return {
summary: [],
billsPaid: [],
billsUnpaid: [],
leftToSpend: [],
netWorth: [],
range: {
start: null,
end: null,
},
store: null
}
},
mounted() {
this.store = useFireflyIIIStore();
if (null === this.range.start || null === this.range.end) {
// subscribe, then update:
this.store.$onAction(
({name, $store, args, after, onError,}) => {
after((result) => {
if (name === 'setRange') {
this.range = result;
this.triggerUpdate();
}
})
}
)
}
if (null !== this.store.getRange.start && null !== this.store.getRange.end) {
this.start = this.store.getRange.start;
this.end = this.store.getRange.end;
this.triggerUpdate();
}
},
methods: {
triggerForcedUpgrade: function () {
this.store.refreshCacheKey();
this.triggerUpdate();
},
triggerUpdate: function () {
if (null !== this.store.getRange.start && null !== this.store.getRange.end) {
const basic = new Basic;
basic.list({
start: this.store.getRange.start,
end: this.store.getRange.end
}, this.store.getCacheKey).then(data => {
this.netWorth = this.getKeyedEntries(data.data, 'net-worth-in-');
this.leftToSpend = this.getKeyedEntries(data.data, 'left-to-spend-in-');
this.billsPaid = this.getKeyedEntries(data.data, 'bills-paid-in-');
this.billsUnpaid = this.getKeyedEntries(data.data, 'bills-unpaid-in-');
});
}
},
getKeyedEntries(array, expected) {
let result = [];
for (const key in array) {
if (array.hasOwnProperty(key)) {
if (expected === key.substr(0, expected.length)) {
result.push(array[key]);
}
}
}
return result;
},
filterOnCurrency(array) {
let ret = [];
for (const key in array) {
if (array.hasOwnProperty(key)) {
if (array[key].currency_id === this.store.getCurrencyId) {
ret.push(array[key]);
}
}
}
// or just the first one:
if (0 === ret.length && array.hasOwnProperty(0)) {
ret.push(array[0]);
}
return ret;
},
filterOnNotCurrency(array) {
let ret = [];
for (const key in array) {
if (array.hasOwnProperty(key)) {
if (array[key].currency_id !== this.store.getCurrencyId) {
ret.push(array[key]);
}
}
}
return ret;
},
}
}
</script>

View File

@ -19,31 +19,29 @@
-->
<template>
<!-- TODO main DIV always use q-ma-md for the main holder-->
<!-- TODO rows use a q-mb-sm to give them space -->
<div class="q-ma-md">
<div class="row q-mb-sm">
<div class="col">
<q-page class="q-ma-md">
<div class="row">
<div class="col-xl-4 col-lg-4 col-md-6 q-pr-sm">
<BillInsightBox/>
</div>
<div class="col">
<div class="col-xl-4 col-lg-4 col-md-6 q-px-sm">
<SpendInsightBox/>
</div>
<div class="col">
<div class="col-xl-4 col-lg-4 col-md-6 q-pl-sm">
<NetWorthInsightBox/>
</div>
</div>
<div class="row q-mb-sm">
<div class="col">
<div class="row">
<div class="col-12 q-pt-sm q-pb-sm">
<AccountChart/>
</div>
</div>
<div class="row q-mb-sm">
<div class="row">
<div class="col">
<TransactionLists/>
</div>
</div>
<div class="row q-mb-sm">
<div class="row">
<div class="col">
<BudgetBox/>
</div>
@ -51,7 +49,7 @@
Category box
</div>
</div>
<div class="row q-mb-sm">
<div class="row">
<div class="col">
Expense Box
</div>
@ -59,7 +57,7 @@
Revenue Box
</div>
</div>
<div class="row q-mb-sm">
<div class="row">
<div class="col">
Piggy box
</div>
@ -77,19 +75,27 @@
square
vertical-actions-align="right"
>
<q-fab-action :label="$t('firefly.new_budget')" :to="{ name: 'budgets.create' }" color="primary" icon="fas fa-chart-pie"
<q-fab-action :label="$t('firefly.new_budget')" :to="{ name: 'budgets.create' }" color="primary"
icon="fas fa-chart-pie"
square/>
<q-fab-action :label="$t('firefly.new_asset_account')" :to="{ name: 'accounts.create', params: {type: 'asset'} }" color="primary" icon="far fa-money-bill-alt"
<q-fab-action :label="$t('firefly.new_asset_account')"
:to="{ name: 'accounts.create', params: {type: 'asset'} }" color="primary"
icon="far fa-money-bill-alt"
square/>
<q-fab-action :label="$t('firefly.newTransfer')" :to="{ name: 'transactions.create', params: {type: 'transfer'} }" color="primary" icon="fas fa-exchange-alt"
<q-fab-action :label="$t('firefly.newTransfer')"
:to="{ name: 'transactions.create', params: {type: 'transfer'} }" color="primary"
icon="fas fa-exchange-alt"
square/>
<q-fab-action :label="$t('firefly.newDeposit')" :to="{ name: 'transactions.create', params: {type: 'deposit'} }" color="primary" icon="fas fa-long-arrow-alt-right"
<q-fab-action :label="$t('firefly.newDeposit')" :to="{ name: 'transactions.create', params: {type: 'deposit'} }"
color="primary" icon="fas fa-long-arrow-alt-right"
square/>
<q-fab-action :label="$t('firefly.newWithdrawal')" :to="{ name: 'transactions.create', params: {type: 'withdrawal'} }" color="primary" icon="fas fa-long-arrow-alt-left"
<q-fab-action :label="$t('firefly.newWithdrawal')"
:to="{ name: 'transactions.create', params: {type: 'withdrawal'} }" color="primary"
icon="fas fa-long-arrow-alt-left"
square/>
</q-fab>
</q-page-sticky>
</div>
</q-page>
</template>
<script>

View File

@ -20,23 +20,114 @@
<template>
<q-page>
<div class="row q-mx-md q-mb-sm">
<!-- search box -->
<div class="col q-mr-sm">
<q-card bordered>
<q-card-section>
<div class="row items-center no-wrap">
<div class="col">
Search and filter
</div>
<!-- insert LargeTable -->
<LargeTable ref="table"
:loading="loading"
:page="page"
:rows="rows"
:rows-number="rowsNumber"
:rows-per-page="rowsPerPage"
:title="$t('firefly.title_' + this.type)"
v-on:on-request="onRequest"
>
<div class="col-auto">
<q-btn color="grey" round flat dense
:icon="searchExpanded ? 'fas fa-chevron-up' : 'fas fa-chevron-down'" @click="searchExpanded = !searchExpanded">
</q-btn>
</div>
</div>
</q-card-section>
<q-slide-transition>
<div v-show="searchExpanded">
<q-separator />
<q-card-section>
Here be stuff
</q-card-section>
</div>
</q-slide-transition>
</q-card>
</div>
<!-- date and range box -->
<div class="col q-ml-sm">
<q-card bordered>
</LargeTable>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<q-card-section>
<div class="row items-center no-wrap">
<div class="col">
Dates and ranges
</div>
<div class="col-auto">
<q-btn color="grey" round flat dense
:icon="dateExpanded ? 'fas fa-chevron-up' : 'fas fa-chevron-down'" @click="dateExpanded = !dateExpanded">
</q-btn>
</div>
</div>
</q-card-section>
<q-slide-transition>
<div v-show="dateExpanded">
<q-separator />
<q-card-section>
Here be stuff
</q-card-section>
</div>
</q-slide-transition>
</q-card>
</div>
</div>
<div class="row q-mx-md">
<div class="col">
<q-card bordered>
<q-card-section>
<div class="row items-center no-wrap">
<div class="col">
Stats
</div>
<div class="col-auto">
<q-btn color="grey" round flat dense
:icon="statsExpanded ? 'fas fa-chevron-up' : 'fas fa-chevron-down'" @click="statsExpanded = !statsExpanded">
</q-btn>
</div>
</div>
</q-card-section>
<q-slide-transition>
<div v-show="statsExpanded">
<q-separator />
<q-card-section>
Here be stuff
</q-card-section>
</div>
</q-slide-transition>
</q-card>
</div>
</div>
<div class="row">
<div class="col">
<!-- insert LargeTable -->
<LargeTable ref="table"
:loading="loading"
:page="page"
:rows="rows"
class="mb-5"
:rows-number="rowsNumber"
:rows-per-page="rowsPerPage"
:title="$t('firefly.title_' + this.type)"
v-on:on-request="onRequest"
>
</LargeTable>
</div>
</div>
<div class="box">
<div class="row">
<p>&nbsp;</p>
<p>&nbsp;</p>
</div>
</div>
<q-page-sticky :offset="[18, 18]" position="bottom-right">
<q-fab
color="green"
@ -47,12 +138,15 @@
square
vertical-actions-align="right"
>
<q-fab-action :to="{ name: 'transactions.create', params: {type: 'transfer'} }" color="primary" icon="fas fa-exchange-alt"
<q-fab-action :to="{ name: 'transactions.create', params: {type: 'transfer'} }" color="primary"
icon="fas fa-exchange-alt"
label="New transfer" square/>
<q-fab-action :to="{ name: 'transactions.create', params: {type: 'deposit'} }" color="primary" icon="fas fa-long-arrow-alt-right"
<q-fab-action :to="{ name: 'transactions.create', params: {type: 'deposit'} }" color="primary"
icon="fas fa-long-arrow-alt-right"
label="New deposit"
square/>
<q-fab-action :to="{ name: 'transactions.create', params: {type: 'withdrawal'} }" color="primary" icon="fas fa-long-arrow-alt-left"
<q-fab-action :to="{ name: 'transactions.create', params: {type: 'withdrawal'} }" color="primary"
icon="fas fa-long-arrow-alt-left"
label="New withdrawal"
square/>
@ -66,6 +160,7 @@
import List from "../../api/transactions/list";
import LargeTable from "../../components/transactions/LargeTable";
import Parser from "../../api/transactions/parser";
import {useFireflyIIIStore} from "stores/fireflyiii";
export default {
name: 'Index',
@ -92,27 +187,26 @@ export default {
columns: [
{name: 'type', label: ' ', field: 'type', style: 'width: 30px'},
{name: 'description', label: 'Description', field: 'description', align: 'left'},
{
name: 'amount', label: 'Amount', field: 'amount'
},
{
name: 'date', label: 'Date', field: 'date',
align: 'left',
},
{name: 'source', label: 'Source', field: 'source', align: 'left'},
{name: 'destination', label: 'Destination', field: 'destination', align: 'left'},
{name: 'category', label: 'Category', field: 'category', align: 'left'},
{name: 'budget', label: 'Budget', field: 'budget', align: 'left'},
{name: 'amount', label: 'Amount', field: 'amount'},
{name: 'date', label: 'Date', field: 'date', align: 'left',},
//{name: 'source', label: 'Source', field: 'source', align: 'left'},
//{name: 'destination', label: 'Destination', field: 'destination', align: 'left'},
//{name: 'category', label: 'Category', field: 'category', align: 'left'},
//{name: 'budget', label: 'Budget', field: 'budget', align: 'left'},
{name: 'menu', label: ' ', field: 'menu', align: 'left'},
],
type: 'withdrawal',
page: 1,
rowsPerPage: 50,
rowsNumber: 100,
store: null,
range: {
start: null,
end: null
}
},
searchExpanded: false, // TODO store in cookie.
dateExpanded : false,
statsExpanded: false,
}
},
computed: {
@ -120,6 +214,8 @@ export default {
},
created() {
this.rowsPerPage = this.getListPageSize;
this.store = useFireflyIIIStore();
},
mounted() {
this.type = this.$route.params.type;

View File

@ -900,6 +900,22 @@ const routes = [
}
]
},
// financial administration
{
path: '/administrations',
component: () => import('layouts/MainLayout.vue'),
children: [
{
path: '',
component: () => import('pages/administration/Index.vue'),
name: 'administration.index',
meta: {
pageTitle: 'firefly.administration_index'
}
}
]
},
// profile
{
path: '/profile',
@ -946,17 +962,17 @@ const routes = [
]
},
// administration
// administration (system settings)
{
path: '/admin',
path: '/system',
component: () => import('layouts/MainLayout.vue'),
children: [
{
path: '',
component: () => import('pages/admin/Index.vue'),
name: 'admin.index',
component: () => import('pages/system/Index.vue'),
name: 'system.index',
meta: {
pageTitle: 'firefly.administration'
pageTitle: 'firefly.system'
}
}
]

File diff suppressed because it is too large Load Diff

View File

@ -1 +1,80 @@
span.multiselect-native-select{position:relative}span.multiselect-native-select select{border:0!important;clip:rect(0 0 0 0)!important;height:1px!important;margin:-1px -1px -1px -3px!important;overflow:hidden!important;padding:0!important;position:absolute!important;width:1px!important;left:50%;top:30px}.multiselect-container{position:absolute;list-style-type:none;margin:0;padding:0}.multiselect-container .input-group{margin:5px}.multiselect-container>li{padding:0}.multiselect-container>li>a.multiselect-all label{font-weight:700}.multiselect-container>li.multiselect-group label{margin:0;padding:3px 20px 3px 20px;height:100%;font-weight:700}.multiselect-container>li.multiselect-group-clickable label{cursor:pointer}.multiselect-container>li>a{padding:0}.multiselect-container>li>a>label{margin:0;height:100%;cursor:pointer;font-weight:400;padding:3px 20px 3px 40px}.multiselect-container>li>a>label.radio,.multiselect-container>li>a>label.checkbox{margin:0}.multiselect-container>li>a>label>input[type=checkbox]{margin-bottom:5px}.btn-group>.btn-group:nth-child(2)>.multiselect.btn{border-top-left-radius:4px;border-bottom-left-radius:4px}.form-inline .multiselect-container label.checkbox,.form-inline .multiselect-container label.radio{padding:3px 20px 3px 40px}.form-inline .multiselect-container li a label.checkbox input[type=checkbox],.form-inline .multiselect-container li a label.radio input[type=radio]{margin-left:-20px;margin-right:0}
span.multiselect-native-select {
position: relative
}
span.multiselect-native-select select {
border: 0 !important;
clip: rect(0 0 0 0) !important;
height: 1px !important;
margin: -1px -1px -1px -3px !important;
overflow: hidden !important;
padding: 0 !important;
position: absolute !important;
width: 1px !important;
left: 50%;
top: 30px
}
.multiselect-container {
position: absolute;
list-style-type: none;
margin: 0;
padding: 0
}
.multiselect-container .input-group {
margin: 5px
}
.multiselect-container > li {
padding: 0
}
.multiselect-container > li > a.multiselect-all label {
font-weight: 700
}
.multiselect-container > li.multiselect-group label {
margin: 0;
padding: 3px 20px 3px 20px;
height: 100%;
font-weight: 700
}
.multiselect-container > li.multiselect-group-clickable label {
cursor: pointer
}
.multiselect-container > li > a {
padding: 0
}
.multiselect-container > li > a > label {
margin: 0;
height: 100%;
cursor: pointer;
font-weight: 400;
padding: 3px 20px 3px 40px
}
.multiselect-container > li > a > label.radio, .multiselect-container > li > a > label.checkbox {
margin: 0
}
.multiselect-container > li > a > label > input[type=checkbox] {
margin-bottom: 5px
}
.btn-group > .btn-group:nth-child(2) > .multiselect.btn {
border-top-left-radius: 4px;
border-bottom-left-radius: 4px
}
.form-inline .multiselect-container label.checkbox, .form-inline .multiselect-container label.radio {
padding: 3px 20px 3px 40px
}
.form-inline .multiselect-container li a label.checkbox input[type=checkbox], .form-inline .multiselect-container li a label.radio input[type=radio] {
margin-left: -20px;
margin-right: 0
}

View File

@ -100,9 +100,15 @@ table.sortable > thead th:not([data-defaultsort=disabled]) {
top: 0;
left: 0;
}
table.sortable > thead th:hover:not([data-defaultsort=disabled]) {
background: #efefef;
@media (prefers-color-scheme: light) {
table.sortable > thead th:hover:not([data-defaultsort=disabled]) {
background: #efefef;
}
}
@media (prefers-color-scheme: dark) {
table.sortable > thead th:hover:not([data-defaultsort=disabled]) {
background: #2a2f34;
}
}
table.sortable > thead th div.mozilla {

View File

@ -1,20 +1,95 @@
.daterangepicker {
position: absolute;
color: inherit;
background-color: #fff;
border-radius: 4px;
border: 1px solid #ddd;
width: 278px;
max-width: none;
padding: 0;
margin-top: 7px;
top: 100px;
left: 20px;
z-index: 3001;
display: none;
font-family: sans-serif, Arial;
font-size: 15px;
line-height: 1em;
@media (prefers-color-scheme: light) {
.daterangepicker {
position: absolute;
color: inherit;
background-color: #fff;
border-radius: 4px;
border: 1px solid #ddd;
width: 278px;
max-width: none;
padding: 0;
margin-top: 7px;
top: 100px;
left: 20px;
z-index: 3001;
display: none;
font-family: sans-serif, Arial;
font-size: 15px;
line-height: 1em;
}
.daterangepicker .calendar-table {
border: 1px solid #fff;
border-radius: 4px;
background-color: #fff;
}
.daterangepicker td.available:hover, .daterangepicker th.available:hover {
background-color: #eee;
border-color: transparent;
color: inherit;
}
.daterangepicker td.off, .daterangepicker td.off.in-range, .daterangepicker td.off.start-date, .daterangepicker td.off.end-date {
background-color: #fff;
border-color: transparent;
color: #999;
}
.daterangepicker td.in-range {
background-color: #ebf4f8;
border-color: transparent;
color: #000;
border-radius: 0;
}
}
@media (prefers-color-scheme: dark) {
/**
fff = 282d32
eee = 31373e
ddd = 3f4750
ebf4f8 = 4b4f50
*/
.daterangepicker {
position: absolute;
color: inherit;
background-color: #282d32;
border-radius: 4px;
border: 1px solid #3f4750;
width: 278px;
max-width: none;
padding: 0;
margin-top: 7px;
top: 100px;
left: 20px;
z-index: 3001;
display: none;
font-family: sans-serif, Arial;
font-size: 15px;
line-height: 1em;
}
.daterangepicker .calendar-table {
border: 1px solid #282d32;
border-radius: 4px;
background-color: #282d32;
}
.daterangepicker td.available:hover, .daterangepicker th.available:hover {
background-color: #31373e;
border-color: transparent;
color: inherit;
}
.daterangepicker td.off, .daterangepicker td.off.in-range, .daterangepicker td.off.start-date, .daterangepicker td.off.end-date {
background-color: #282d32;
border-color: transparent;
color: #999;
}
.daterangepicker td.in-range {
background-color: #4b4f50;
border-color: transparent;
color: #000;
border-radius: 0;
}
}
.daterangepicker:before, .daterangepicker:after {
@ -158,12 +233,6 @@
cursor: pointer;
}
.daterangepicker .calendar-table {
border: 1px solid #fff;
border-radius: 4px;
background-color: #fff;
}
.daterangepicker .calendar-table table {
width: 100%;
margin: 0;
@ -171,29 +240,13 @@
border-collapse: collapse;
}
.daterangepicker td.available:hover, .daterangepicker th.available:hover {
background-color: #eee;
border-color: transparent;
color: inherit;
}
.daterangepicker td.week, .daterangepicker th.week {
font-size: 80%;
color: #ccc;
}
.daterangepicker td.off, .daterangepicker td.off.in-range, .daterangepicker td.off.start-date, .daterangepicker td.off.end-date {
background-color: #fff;
border-color: transparent;
color: #999;
}
.daterangepicker td.in-range {
background-color: #ebf4f8;
border-color: transparent;
color: #000;
border-radius: 0;
}
.daterangepicker td.start-date {
border-radius: 4px 0 0 4px;

View File

@ -18,36 +18,19 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
/* used in transaction groups */
.no-margin-pagination {padding-bottom:0;padding-top:0;}
.no-margin-pagination ul.pagination {margin:0 !important;}
/* TODO find out where this is used. */
input.ti-new-tag-input {
font-size: 14px !important;
line-height: 1.42857143;
color: #555;
color:red; /* color: #555;*/
font-family:"Source Sans Pro", "Helvetica Neue",Helvetica,Arial,sans-serif !important;
}
.split_amount_input {
width: 40%;
border-radius: 0;
height: 36px;
padding: 6px 12px;
background-color: #fff;
background-image: none;
border: 1px solid #ccd0d2;
-webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075);
box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075);
-webkit-transition: border-color .15s ease-in-out, box-shadow .15s ease-in-out;
-webkit-transition: border-color .15s ease-in-out, -webkit-box-shadow .15s ease-in-out;
transition: border-color .15s ease-in-out, -webkit-box-shadow .15s ease-in-out;
transition: border-color .15s ease-in-out, box-shadow .15s ease-in-out;
transition: border-color .15s ease-in-out, box-shadow .15s ease-in-out, -webkit-box-shadow .15s ease-in-out;
}
/* TODO Find out where this is used. */
.autocomplete-suggestions { border: 1px solid #999; background: #FFF; overflow: auto; }
.autocomplete-suggestion { padding: 2px 5px; white-space: nowrap; overflow: hidden; }
.autocomplete-selected { background: #F0F0F0; }
@ -55,13 +38,6 @@ input.ti-new-tag-input {
.autocomplete-group { padding: 2px 5px; font-weight: bold;}
.autocomplete-group strong { display: block; border-bottom: 1px solid #000; }
.split_amount_input:focus {
border-color: #98cbe8;
outline: 0;
-webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 8px rgba(152, 203, 232, .6);
box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 8px rgba(152, 203, 232, .6);
}
#daterange {
cursor: pointer;
}

2
public/v1/js/app.js vendored

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -159,19 +159,21 @@ function listLengthInitial() {
function triggerList(e) {
"use strict";
var link = $(e.target);
var table = link.parent().parent().parent().parent();
var table = $(link.parent().parent().parent().parent());
if (table.attr('data-hidden') === 'no') {
// hide all elements, return false.
table.find('.overListLength').hide();
table.attr('data-hidden', 'yes');
link.text(showFullList);
return false;
}
if (table.attr('data-hidden') !== 'no') {
// show all, return false
table.find('.overListLength').show();
table.attr('data-hidden', 'no');
link.text(showOnlyTop);
return false;
}
return false;
}
}

File diff suppressed because one or more lines are too long

Some files were not shown because too many files have changed in this diff Show More