Merge branch 'release/v6.0.21'

This commit is contained in:
James Cole
2023-09-01 05:33:50 +02:00
237 changed files with 7343 additions and 3957 deletions

View File

@@ -79,16 +79,16 @@
}, },
{ {
"name": "composer/semver", "name": "composer/semver",
"version": "3.3.2", "version": "3.4.0",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/composer/semver.git", "url": "https://github.com/composer/semver.git",
"reference": "3953f23262f2bff1919fc82183ad9acb13ff62c9" "reference": "35e8d0af4486141bc745f23a29cc2091eb624a32"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/composer/semver/zipball/3953f23262f2bff1919fc82183ad9acb13ff62c9", "url": "https://api.github.com/repos/composer/semver/zipball/35e8d0af4486141bc745f23a29cc2091eb624a32",
"reference": "3953f23262f2bff1919fc82183ad9acb13ff62c9", "reference": "35e8d0af4486141bc745f23a29cc2091eb624a32",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@@ -138,9 +138,9 @@
"versioning" "versioning"
], ],
"support": { "support": {
"irc": "irc://irc.freenode.org/composer", "irc": "ircs://irc.libera.chat:6697/composer",
"issues": "https://github.com/composer/semver/issues", "issues": "https://github.com/composer/semver/issues",
"source": "https://github.com/composer/semver/tree/3.3.2" "source": "https://github.com/composer/semver/tree/3.4.0"
}, },
"funding": [ "funding": [
{ {
@@ -156,7 +156,7 @@
"type": "tidelift" "type": "tidelift"
} }
], ],
"time": "2022-04-01T19:23:25+00:00" "time": "2023-08-31T09:50:34+00:00"
}, },
{ {
"name": "composer/xdebug-handler", "name": "composer/xdebug-handler",
@@ -224,178 +224,23 @@
], ],
"time": "2022-02-25T21:32:43+00:00" "time": "2022-02-25T21:32:43+00:00"
}, },
{
"name": "doctrine/annotations",
"version": "2.0.1",
"source": {
"type": "git",
"url": "https://github.com/doctrine/annotations.git",
"reference": "e157ef3f3124bbf6fe7ce0ffd109e8a8ef284e7f"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/doctrine/annotations/zipball/e157ef3f3124bbf6fe7ce0ffd109e8a8ef284e7f",
"reference": "e157ef3f3124bbf6fe7ce0ffd109e8a8ef284e7f",
"shasum": ""
},
"require": {
"doctrine/lexer": "^2 || ^3",
"ext-tokenizer": "*",
"php": "^7.2 || ^8.0",
"psr/cache": "^1 || ^2 || ^3"
},
"require-dev": {
"doctrine/cache": "^2.0",
"doctrine/coding-standard": "^10",
"phpstan/phpstan": "^1.8.0",
"phpunit/phpunit": "^7.5 || ^8.5 || ^9.5",
"symfony/cache": "^5.4 || ^6",
"vimeo/psalm": "^4.10"
},
"suggest": {
"php": "PHP 8.0 or higher comes with attributes, a native replacement for annotations"
},
"type": "library",
"autoload": {
"psr-4": {
"Doctrine\\Common\\Annotations\\": "lib/Doctrine/Common/Annotations"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Guilherme Blanco",
"email": "guilhermeblanco@gmail.com"
},
{
"name": "Roman Borschel",
"email": "roman@code-factory.org"
},
{
"name": "Benjamin Eberlei",
"email": "kontakt@beberlei.de"
},
{
"name": "Jonathan Wage",
"email": "jonwage@gmail.com"
},
{
"name": "Johannes Schmitt",
"email": "schmittjoh@gmail.com"
}
],
"description": "Docblock Annotations Parser",
"homepage": "https://www.doctrine-project.org/projects/annotations.html",
"keywords": [
"annotations",
"docblock",
"parser"
],
"support": {
"issues": "https://github.com/doctrine/annotations/issues",
"source": "https://github.com/doctrine/annotations/tree/2.0.1"
},
"time": "2023-02-02T22:02:53+00:00"
},
{
"name": "doctrine/lexer",
"version": "3.0.0",
"source": {
"type": "git",
"url": "https://github.com/doctrine/lexer.git",
"reference": "84a527db05647743d50373e0ec53a152f2cde568"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/doctrine/lexer/zipball/84a527db05647743d50373e0ec53a152f2cde568",
"reference": "84a527db05647743d50373e0ec53a152f2cde568",
"shasum": ""
},
"require": {
"php": "^8.1"
},
"require-dev": {
"doctrine/coding-standard": "^10",
"phpstan/phpstan": "^1.9",
"phpunit/phpunit": "^9.5",
"psalm/plugin-phpunit": "^0.18.3",
"vimeo/psalm": "^5.0"
},
"type": "library",
"autoload": {
"psr-4": {
"Doctrine\\Common\\Lexer\\": "src"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Guilherme Blanco",
"email": "guilhermeblanco@gmail.com"
},
{
"name": "Roman Borschel",
"email": "roman@code-factory.org"
},
{
"name": "Johannes Schmitt",
"email": "schmittjoh@gmail.com"
}
],
"description": "PHP Doctrine Lexer parser library that can be used in Top-Down, Recursive Descent Parsers.",
"homepage": "https://www.doctrine-project.org/projects/lexer.html",
"keywords": [
"annotations",
"docblock",
"lexer",
"parser",
"php"
],
"support": {
"issues": "https://github.com/doctrine/lexer/issues",
"source": "https://github.com/doctrine/lexer/tree/3.0.0"
},
"funding": [
{
"url": "https://www.doctrine-project.org/sponsorship.html",
"type": "custom"
},
{
"url": "https://www.patreon.com/phpdoctrine",
"type": "patreon"
},
{
"url": "https://tidelift.com/funding/github/packagist/doctrine%2Flexer",
"type": "tidelift"
}
],
"time": "2022-12-15T16:57:16+00:00"
},
{ {
"name": "friendsofphp/php-cs-fixer", "name": "friendsofphp/php-cs-fixer",
"version": "v3.22.0", "version": "v3.25.0",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer.git", "url": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer.git",
"reference": "92b019f6c8d79aa26349d0db7671d37440dc0ff3" "reference": "9025b7d2b6e1d90a63d0ac0905018ce5d03ec88d"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/PHP-CS-Fixer/PHP-CS-Fixer/zipball/92b019f6c8d79aa26349d0db7671d37440dc0ff3", "url": "https://api.github.com/repos/PHP-CS-Fixer/PHP-CS-Fixer/zipball/9025b7d2b6e1d90a63d0ac0905018ce5d03ec88d",
"reference": "92b019f6c8d79aa26349d0db7671d37440dc0ff3", "reference": "9025b7d2b6e1d90a63d0ac0905018ce5d03ec88d",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
"composer/semver": "^3.3", "composer/semver": "^3.3",
"composer/xdebug-handler": "^3.0.3", "composer/xdebug-handler": "^3.0.3",
"doctrine/annotations": "^2",
"doctrine/lexer": "^2 || ^3",
"ext-json": "*", "ext-json": "*",
"ext-tokenizer": "*", "ext-tokenizer": "*",
"php": "^7.4 || ^8.0", "php": "^7.4 || ^8.0",
@@ -464,7 +309,7 @@
], ],
"support": { "support": {
"issues": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/issues", "issues": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/issues",
"source": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/tree/v3.22.0" "source": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/tree/v3.25.0"
}, },
"funding": [ "funding": [
{ {
@@ -472,56 +317,7 @@
"type": "github" "type": "github"
} }
], ],
"time": "2023-07-16T23:08:06+00:00" "time": "2023-08-31T21:27:18+00:00"
},
{
"name": "psr/cache",
"version": "3.0.0",
"source": {
"type": "git",
"url": "https://github.com/php-fig/cache.git",
"reference": "aa5030cfa5405eccfdcb1083ce040c2cb8d253bf"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/php-fig/cache/zipball/aa5030cfa5405eccfdcb1083ce040c2cb8d253bf",
"reference": "aa5030cfa5405eccfdcb1083ce040c2cb8d253bf",
"shasum": ""
},
"require": {
"php": ">=8.0.0"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.0.x-dev"
}
},
"autoload": {
"psr-4": {
"Psr\\Cache\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "PHP-FIG",
"homepage": "https://www.php-fig.org/"
}
],
"description": "Common interface for caching libraries",
"keywords": [
"cache",
"psr",
"psr-6"
],
"support": {
"source": "https://github.com/php-fig/cache/tree/3.0.0"
},
"time": "2021-02-03T23:26:27+00:00"
}, },
{ {
"name": "psr/container", "name": "psr/container",
@@ -745,16 +541,16 @@
}, },
{ {
"name": "symfony/console", "name": "symfony/console",
"version": "v6.3.2", "version": "v6.3.4",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/symfony/console.git", "url": "https://github.com/symfony/console.git",
"reference": "aa5d64ad3f63f2e48964fc81ee45cb318a723898" "reference": "eca495f2ee845130855ddf1cf18460c38966c8b6"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/symfony/console/zipball/aa5d64ad3f63f2e48964fc81ee45cb318a723898", "url": "https://api.github.com/repos/symfony/console/zipball/eca495f2ee845130855ddf1cf18460c38966c8b6",
"reference": "aa5d64ad3f63f2e48964fc81ee45cb318a723898", "reference": "eca495f2ee845130855ddf1cf18460c38966c8b6",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@@ -815,7 +611,7 @@
"terminal" "terminal"
], ],
"support": { "support": {
"source": "https://github.com/symfony/console/tree/v6.3.2" "source": "https://github.com/symfony/console/tree/v6.3.4"
}, },
"funding": [ "funding": [
{ {
@@ -831,7 +627,7 @@
"type": "tidelift" "type": "tidelift"
} }
], ],
"time": "2023-07-19T20:17:28+00:00" "time": "2023-08-16T10:10:12+00:00"
}, },
{ {
"name": "symfony/deprecation-contracts", "name": "symfony/deprecation-contracts",
@@ -1252,16 +1048,16 @@
}, },
{ {
"name": "symfony/polyfill-ctype", "name": "symfony/polyfill-ctype",
"version": "v1.27.0", "version": "v1.28.0",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/symfony/polyfill-ctype.git", "url": "https://github.com/symfony/polyfill-ctype.git",
"reference": "5bbc823adecdae860bb64756d639ecfec17b050a" "reference": "ea208ce43cbb04af6867b4fdddb1bdbf84cc28cb"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/5bbc823adecdae860bb64756d639ecfec17b050a", "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/ea208ce43cbb04af6867b4fdddb1bdbf84cc28cb",
"reference": "5bbc823adecdae860bb64756d639ecfec17b050a", "reference": "ea208ce43cbb04af6867b4fdddb1bdbf84cc28cb",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@@ -1276,7 +1072,7 @@
"type": "library", "type": "library",
"extra": { "extra": {
"branch-alias": { "branch-alias": {
"dev-main": "1.27-dev" "dev-main": "1.28-dev"
}, },
"thanks": { "thanks": {
"name": "symfony/polyfill", "name": "symfony/polyfill",
@@ -1314,7 +1110,7 @@
"portable" "portable"
], ],
"support": { "support": {
"source": "https://github.com/symfony/polyfill-ctype/tree/v1.27.0" "source": "https://github.com/symfony/polyfill-ctype/tree/v1.28.0"
}, },
"funding": [ "funding": [
{ {
@@ -1330,20 +1126,20 @@
"type": "tidelift" "type": "tidelift"
} }
], ],
"time": "2022-11-03T14:55:06+00:00" "time": "2023-01-26T09:26:14+00:00"
}, },
{ {
"name": "symfony/polyfill-intl-grapheme", "name": "symfony/polyfill-intl-grapheme",
"version": "v1.27.0", "version": "v1.28.0",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/symfony/polyfill-intl-grapheme.git", "url": "https://github.com/symfony/polyfill-intl-grapheme.git",
"reference": "511a08c03c1960e08a883f4cffcacd219b758354" "reference": "875e90aeea2777b6f135677f618529449334a612"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/511a08c03c1960e08a883f4cffcacd219b758354", "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/875e90aeea2777b6f135677f618529449334a612",
"reference": "511a08c03c1960e08a883f4cffcacd219b758354", "reference": "875e90aeea2777b6f135677f618529449334a612",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@@ -1355,7 +1151,7 @@
"type": "library", "type": "library",
"extra": { "extra": {
"branch-alias": { "branch-alias": {
"dev-main": "1.27-dev" "dev-main": "1.28-dev"
}, },
"thanks": { "thanks": {
"name": "symfony/polyfill", "name": "symfony/polyfill",
@@ -1395,7 +1191,7 @@
"shim" "shim"
], ],
"support": { "support": {
"source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.27.0" "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.28.0"
}, },
"funding": [ "funding": [
{ {
@@ -1411,20 +1207,20 @@
"type": "tidelift" "type": "tidelift"
} }
], ],
"time": "2022-11-03T14:55:06+00:00" "time": "2023-01-26T09:26:14+00:00"
}, },
{ {
"name": "symfony/polyfill-intl-normalizer", "name": "symfony/polyfill-intl-normalizer",
"version": "v1.27.0", "version": "v1.28.0",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/symfony/polyfill-intl-normalizer.git", "url": "https://github.com/symfony/polyfill-intl-normalizer.git",
"reference": "19bd1e4fcd5b91116f14d8533c57831ed00571b6" "reference": "8c4ad05dd0120b6a53c1ca374dca2ad0a1c4ed92"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/19bd1e4fcd5b91116f14d8533c57831ed00571b6", "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/8c4ad05dd0120b6a53c1ca374dca2ad0a1c4ed92",
"reference": "19bd1e4fcd5b91116f14d8533c57831ed00571b6", "reference": "8c4ad05dd0120b6a53c1ca374dca2ad0a1c4ed92",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@@ -1436,7 +1232,7 @@
"type": "library", "type": "library",
"extra": { "extra": {
"branch-alias": { "branch-alias": {
"dev-main": "1.27-dev" "dev-main": "1.28-dev"
}, },
"thanks": { "thanks": {
"name": "symfony/polyfill", "name": "symfony/polyfill",
@@ -1479,7 +1275,7 @@
"shim" "shim"
], ],
"support": { "support": {
"source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.27.0" "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.28.0"
}, },
"funding": [ "funding": [
{ {
@@ -1495,20 +1291,20 @@
"type": "tidelift" "type": "tidelift"
} }
], ],
"time": "2022-11-03T14:55:06+00:00" "time": "2023-01-26T09:26:14+00:00"
}, },
{ {
"name": "symfony/polyfill-mbstring", "name": "symfony/polyfill-mbstring",
"version": "v1.27.0", "version": "v1.28.0",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/symfony/polyfill-mbstring.git", "url": "https://github.com/symfony/polyfill-mbstring.git",
"reference": "8ad114f6b39e2c98a8b0e3bd907732c207c2b534" "reference": "42292d99c55abe617799667f454222c54c60e229"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/8ad114f6b39e2c98a8b0e3bd907732c207c2b534", "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/42292d99c55abe617799667f454222c54c60e229",
"reference": "8ad114f6b39e2c98a8b0e3bd907732c207c2b534", "reference": "42292d99c55abe617799667f454222c54c60e229",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@@ -1523,7 +1319,7 @@
"type": "library", "type": "library",
"extra": { "extra": {
"branch-alias": { "branch-alias": {
"dev-main": "1.27-dev" "dev-main": "1.28-dev"
}, },
"thanks": { "thanks": {
"name": "symfony/polyfill", "name": "symfony/polyfill",
@@ -1562,7 +1358,7 @@
"shim" "shim"
], ],
"support": { "support": {
"source": "https://github.com/symfony/polyfill-mbstring/tree/v1.27.0" "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.28.0"
}, },
"funding": [ "funding": [
{ {
@@ -1578,20 +1374,20 @@
"type": "tidelift" "type": "tidelift"
} }
], ],
"time": "2022-11-03T14:55:06+00:00" "time": "2023-07-28T09:04:16+00:00"
}, },
{ {
"name": "symfony/polyfill-php80", "name": "symfony/polyfill-php80",
"version": "v1.27.0", "version": "v1.28.0",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/symfony/polyfill-php80.git", "url": "https://github.com/symfony/polyfill-php80.git",
"reference": "7a6ff3f1959bb01aefccb463a0f2cd3d3d2fd936" "reference": "6caa57379c4aec19c0a12a38b59b26487dcfe4b5"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/7a6ff3f1959bb01aefccb463a0f2cd3d3d2fd936", "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/6caa57379c4aec19c0a12a38b59b26487dcfe4b5",
"reference": "7a6ff3f1959bb01aefccb463a0f2cd3d3d2fd936", "reference": "6caa57379c4aec19c0a12a38b59b26487dcfe4b5",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@@ -1600,7 +1396,7 @@
"type": "library", "type": "library",
"extra": { "extra": {
"branch-alias": { "branch-alias": {
"dev-main": "1.27-dev" "dev-main": "1.28-dev"
}, },
"thanks": { "thanks": {
"name": "symfony/polyfill", "name": "symfony/polyfill",
@@ -1645,7 +1441,7 @@
"shim" "shim"
], ],
"support": { "support": {
"source": "https://github.com/symfony/polyfill-php80/tree/v1.27.0" "source": "https://github.com/symfony/polyfill-php80/tree/v1.28.0"
}, },
"funding": [ "funding": [
{ {
@@ -1661,20 +1457,20 @@
"type": "tidelift" "type": "tidelift"
} }
], ],
"time": "2022-11-03T14:55:06+00:00" "time": "2023-01-26T09:26:14+00:00"
}, },
{ {
"name": "symfony/polyfill-php81", "name": "symfony/polyfill-php81",
"version": "v1.27.0", "version": "v1.28.0",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/symfony/polyfill-php81.git", "url": "https://github.com/symfony/polyfill-php81.git",
"reference": "707403074c8ea6e2edaf8794b0157a0bfa52157a" "reference": "7581cd600fa9fd681b797d00b02f068e2f13263b"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-php81/zipball/707403074c8ea6e2edaf8794b0157a0bfa52157a", "url": "https://api.github.com/repos/symfony/polyfill-php81/zipball/7581cd600fa9fd681b797d00b02f068e2f13263b",
"reference": "707403074c8ea6e2edaf8794b0157a0bfa52157a", "reference": "7581cd600fa9fd681b797d00b02f068e2f13263b",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@@ -1683,7 +1479,7 @@
"type": "library", "type": "library",
"extra": { "extra": {
"branch-alias": { "branch-alias": {
"dev-main": "1.27-dev" "dev-main": "1.28-dev"
}, },
"thanks": { "thanks": {
"name": "symfony/polyfill", "name": "symfony/polyfill",
@@ -1724,7 +1520,7 @@
"shim" "shim"
], ],
"support": { "support": {
"source": "https://github.com/symfony/polyfill-php81/tree/v1.27.0" "source": "https://github.com/symfony/polyfill-php81/tree/v1.28.0"
}, },
"funding": [ "funding": [
{ {
@@ -1740,20 +1536,20 @@
"type": "tidelift" "type": "tidelift"
} }
], ],
"time": "2022-11-03T14:55:06+00:00" "time": "2023-01-26T09:26:14+00:00"
}, },
{ {
"name": "symfony/process", "name": "symfony/process",
"version": "v6.3.2", "version": "v6.3.4",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/symfony/process.git", "url": "https://github.com/symfony/process.git",
"reference": "c5ce962db0d9b6e80247ca5eb9af6472bd4d7b5d" "reference": "0b5c29118f2e980d455d2e34a5659f4579847c54"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/symfony/process/zipball/c5ce962db0d9b6e80247ca5eb9af6472bd4d7b5d", "url": "https://api.github.com/repos/symfony/process/zipball/0b5c29118f2e980d455d2e34a5659f4579847c54",
"reference": "c5ce962db0d9b6e80247ca5eb9af6472bd4d7b5d", "reference": "0b5c29118f2e980d455d2e34a5659f4579847c54",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@@ -1785,7 +1581,7 @@
"description": "Executes commands in sub-processes", "description": "Executes commands in sub-processes",
"homepage": "https://symfony.com", "homepage": "https://symfony.com",
"support": { "support": {
"source": "https://github.com/symfony/process/tree/v6.3.2" "source": "https://github.com/symfony/process/tree/v6.3.4"
}, },
"funding": [ "funding": [
{ {
@@ -1801,7 +1597,7 @@
"type": "tidelift" "type": "tidelift"
} }
], ],
"time": "2023-07-12T16:00:22+00:00" "time": "2023-08-07T10:39:22+00:00"
}, },
{ {
"name": "symfony/service-contracts", "name": "symfony/service-contracts",

View File

@@ -45,12 +45,6 @@ TRUSTED_PROXIES=
# Default setting 'stack' will log to 'daily' and to 'stdout' at the same time. # Default setting 'stack' will log to 'daily' and to 'stdout' at the same time.
LOG_CHANNEL=stack LOG_CHANNEL=stack
#
# Used when logging to papertrail:
#
PAPERTRAIL_HOST=
PAPERTRAIL_PORT=
# Log level. You can set this from least severe to most severe: # Log level. You can set this from least severe to most severe:
# debug, info, notice, warning, error, critical, alert, emergency # debug, info, notice, warning, error, critical, alert, emergency
# If you set it to debug your logs will grow large, and fast. If you set it to emergency probably # If you set it to debug your logs will grow large, and fast. If you set it to emergency probably
@@ -58,8 +52,30 @@ PAPERTRAIL_PORT=
APP_LOG_LEVEL=notice APP_LOG_LEVEL=notice
# Audit log level. # Audit log level.
# Set this to "emergency" if you dont want to store audit logs, leave on info otherwise. # The audit log is used to log notable Firefly III events on a separate channel.
AUDIT_LOG_LEVEL=info # These log entries may contain sensitive financial information.
# The audit log is disabled by default.
#
# To enable it, set AUDIT_LOG_LEVEL to "info"
# To disable it, set AUDIT_LOG_LEVEL to "emergency"
AUDIT_LOG_LEVEL=emergency
#
# If you want, you can redirect the audit logs to another channel.
# Set 'audit_stdout', 'audit_syslog', 'audit_errorlog' to log to the system itself.
# Use audit_daily to log to a rotating file.
# Use audit_papertrail to log to papertrail.
#
# If you do this, the audit logs may be mixed with normal logs because the settings for these channels
# are often the same as the settings for the normal logs.
AUDIT_LOG_CHANNEL=
#
# Used when logging to papertrail:
# Also used when audit logs log to papertrail:
#
PAPERTRAIL_HOST=
PAPERTRAIL_PORT=
# Database credentials. Make sure the database exists. I recommend a dedicated user for Firefly III # Database credentials. Make sure the database exists. I recommend a dedicated user for Firefly III
# For other database types, please see the FAQ: https://docs.firefly-iii.org/firefly-iii/faq/self-hosted/#i-want-to-use-sqlite # For other database types, please see the FAQ: https://docs.firefly-iii.org/firefly-iii/faq/self-hosted/#i-want-to-use-sqlite

View File

@@ -102,6 +102,7 @@ abstract class Controller extends BaseController
} catch (BadRequestException $e) { } catch (BadRequestException $e) {
Log::error(sprintf('Request field "%s" contains a non-scalar value. Value set to NULL.', $field)); Log::error(sprintf('Request field "%s" contains a non-scalar value. Value set to NULL.', $field));
Log::error($e->getMessage()); Log::error($e->getMessage());
Log::error($e->getTraceAsString());
$value = null; $value = null;
} }
$obj = null; $obj = null;
@@ -130,6 +131,7 @@ abstract class Controller extends BaseController
} catch (BadRequestException $e) { } catch (BadRequestException $e) {
Log::error(sprintf('Request field "%s" contains a non-scalar value. Value set to NULL.', $integer)); Log::error(sprintf('Request field "%s" contains a non-scalar value. Value set to NULL.', $integer));
Log::error($e->getMessage()); Log::error($e->getMessage());
Log::error($e->getTraceAsString());
$value = null; $value = null;
} }
if (null !== $value) { if (null !== $value) {
@@ -154,6 +156,7 @@ abstract class Controller extends BaseController
} catch (BadRequestException $e) { } catch (BadRequestException $e) {
Log::error('Request field "sort" contains a non-scalar value. Value set to NULL.'); Log::error('Request field "sort" contains a non-scalar value. Value set to NULL.');
Log::error($e->getMessage()); Log::error($e->getMessage());
Log::error($e->getTraceAsString());
$param = ''; $param = '';
} }
if ('' === $param) { if ('' === $param) {

View File

@@ -99,6 +99,7 @@ class Controller extends BaseController
} catch (BadRequestException $e) { } catch (BadRequestException $e) {
Log::error(sprintf('Request field "%s" contains a non-scalar value. Value set to NULL.', $field)); Log::error(sprintf('Request field "%s" contains a non-scalar value. Value set to NULL.', $field));
Log::error($e->getMessage()); Log::error($e->getMessage());
Log::error($e->getTraceAsString());
} }
if (null !== $date) { if (null !== $date) {
try { try {

View File

@@ -0,0 +1,45 @@
<?php
declare(strict_types=1);
/*
* StoreController.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\Api\V2\Controllers\Model\Transaction;
use FireflyIII\Api\V2\Controllers\Controller;
use Illuminate\Http\JsonResponse;
/**
* Class StoreController
*/
class StoreController extends Controller
{
/**
* @return JsonResponse
*/
public function post(): JsonResponse
{
return response()->json([]);
}
}

View File

@@ -105,10 +105,6 @@ class BasicController extends Controller
$end = $this->parameters->get('end'); $end = $this->parameters->get('end');
// balance information: // balance information:
$balanceData = [];
$billData = [];
$spentData = [];
$netWorthData = [];
$balanceData = $this->getBalanceInformation($start, $end); $balanceData = $this->getBalanceInformation($start, $end);
$billData = $this->getBillInformation($start, $end); $billData = $this->getBillInformation($start, $end);
$spentData = $this->getLeftToSpendInfo($start, $end); $spentData = $this->getLeftToSpendInfo($start, $end);
@@ -127,9 +123,15 @@ class BasicController extends Controller
private function getBalanceInformation(Carbon $start, Carbon $end): array private function getBalanceInformation(Carbon $start, Carbon $end): array
{ {
// prep some arrays: // prep some arrays:
$incomes = []; $incomes = [
$expenses = []; 'native' => '0',
$sums = []; ];
$expenses = [
'native' => '0',
];
$sums = [
'native' => '0',
];
$return = []; $return = [];
$currencies = []; $currencies = [];
$converter = new ExchangeRateConverter(); $converter = new ExchangeRateConverter();
@@ -354,6 +356,7 @@ class BasicController extends Controller
*/ */
private function getLeftToSpendInfo(Carbon $start, Carbon $end): array private function getLeftToSpendInfo(Carbon $start, Carbon $end): array
{ {
app('log')->debug('Now in getLeftToSpendInfo');
$return = []; $return = [];
$today = today(config('app.timezone')); $today = today(config('app.timezone'));
$available = $this->abRepository->getAvailableBudgetWithCurrency($start, $end); $available = $this->abRepository->getAvailableBudgetWithCurrency($start, $end);
@@ -367,7 +370,7 @@ class BasicController extends Controller
$nativeLeft = [ $nativeLeft = [
'key' => 'left-to-spend-in-native', 'key' => 'left-to-spend-in-native',
'value' => '0', 'value' => '0',
'currency_id' => (int)$default->id, 'currency_id' => (string)$default->id,
'currency_code' => $default->code, 'currency_code' => $default->code,
'currency_symbol' => $default->symbol, 'currency_symbol' => $default->symbol,
'currency_decimal_places' => (int)$default->decimal_places, 'currency_decimal_places' => (int)$default->decimal_places,
@@ -375,7 +378,7 @@ class BasicController extends Controller
$nativePerDay = [ $nativePerDay = [
'key' => 'left-per-day-to-spend-in-native', 'key' => 'left-per-day-to-spend-in-native',
'value' => '0', 'value' => '0',
'currency_id' => (int)$default->id, 'currency_id' => (string)$default->id,
'currency_code' => $default->code, 'currency_code' => $default->code,
'currency_symbol' => $default->symbol, 'currency_symbol' => $default->symbol,
'currency_decimal_places' => (int)$default->decimal_places, 'currency_decimal_places' => (int)$default->decimal_places,
@@ -386,17 +389,20 @@ class BasicController extends Controller
* @var array $row * @var array $row
*/ */
foreach ($spent as $currencyId => $row) { foreach ($spent as $currencyId => $row) {
app('log')->debug(sprintf('Processing spent array in currency #%d', $currencyId));
$currencyId = (int)$currencyId;
$spent = '0'; $spent = '0';
$spentNative = '0'; $spentNative = '0';
// get the sum from the array of transactions (double loop but who cares) // get the sum from the array of transactions (double loop but who cares)
/** @var array $budget */ /** @var array $budget */
foreach ($row['budgets'] as $budget) { foreach ($row['budgets'] as $budget) {
app('log')->debug(sprintf('Processing expenses in budget "%s".', $budget['name']));
/** @var array $journal */ /** @var array $journal */
foreach ($budget['transaction_journals'] as $journal) { foreach ($budget['transaction_journals'] as $journal) {
$journalCurrencyId = $journal['currency_id']; $journalCurrencyId = $journal['currency_id'];
$currency = $currencies[$journalCurrencyId] ?? $this->currencyRepos->find($journalCurrencyId); $currency = $currencies[$journalCurrencyId] ?? $this->currencyRepos->find($journalCurrencyId);
$currencies[$currencyId] = $currency; $currencies[$currencyId] = $currency;
$amount = bcmul($journal['amount'], '-1'); $amount = app('steam')->negative($journal['amount']);
$amountNative = $converter->convert($default, $currency, $start, $amount); $amountNative = $converter->convert($default, $currency, $start, $amount);
if ((int)$journal['foreign_currency_id'] === (int)$default->id) { if ((int)$journal['foreign_currency_id'] === (int)$default->id) {
$amountNative = $journal['foreign_amount']; $amountNative = $journal['foreign_amount'];
@@ -404,15 +410,18 @@ class BasicController extends Controller
$spent = bcadd($spent, $amount); $spent = bcadd($spent, $amount);
$spentNative = bcadd($spentNative, $amountNative); $spentNative = bcadd($spentNative, $amountNative);
} }
app('log')->debug(sprintf('Total spent in budget "%s" is %s', $budget['name'], $spent));
} }
// either an amount was budgeted or 0 is available. // either an amount was budgeted or 0 is available.
$currency = $currencies[$currencyId] ?? $this->currencyRepos->find($currencyId); $currency = $currencies[$currencyId] ?? $this->currencyRepos->find($currencyId);
$currencies[$currencyId] = $currency; $currencies[$currencyId] = $currency;
$amount = $available[$currencyId]['amount'] ?? '0'; $amount = $available[$currencyId]['amount'] ?? '0';
$amountNative = $converter->convert($default, $currency, $start, $amount); $amountNative = $available[$currencyId]['native_amount'] ?? '0';
$left = bcadd($amount, $spent); $left = bcadd($amount, $spent);
$leftNative = bcadd($amountNative, $spentNative); $leftNative = bcadd($amountNative, $spentNative);
app('log')->debug(sprintf('Available amount is %s', $amount));
app('log')->debug(sprintf('Amount left is %s', $left));
// how much left per day? // how much left per day?
$days = $today->diffInDays($end) + 1; $days = $today->diffInDays($end) + 1;
@@ -429,10 +438,10 @@ class BasicController extends Controller
$return[] = [ $return[] = [
'key' => sprintf('left-to-spend-in-%s', $row['currency_code']), 'key' => sprintf('left-to-spend-in-%s', $row['currency_code']),
'value' => $left, 'value' => $left,
'currency_id' => $row['currency_id'], 'currency_id' => (string)$row['currency_id'],
'currency_code' => $row['currency_code'], 'currency_code' => $row['currency_code'],
'currency_symbol' => $row['currency_symbol'], 'currency_symbol' => $row['currency_symbol'],
'currency_decimal_places' => $row['currency_decimal_places'], 'currency_decimal_places' => (int)$row['currency_decimal_places'],
]; ];
// left (native) // left (native)
$nativeLeft['value'] = $leftNative; $nativeLeft['value'] = $leftNative;
@@ -441,10 +450,10 @@ class BasicController extends Controller
$return[] = [ $return[] = [
'key' => sprintf('left-per-day-to-spend-in-%s', $row['currency_code']), 'key' => sprintf('left-per-day-to-spend-in-%s', $row['currency_code']),
'value' => $perDay, 'value' => $perDay,
'currency_id' => $row['currency_id'], 'currency_id' => (string)$row['currency_id'],
'currency_code' => $row['currency_code'], 'currency_code' => $row['currency_code'],
'currency_symbol' => $row['currency_symbol'], 'currency_symbol' => $row['currency_symbol'],
'currency_decimal_places' => $row['currency_decimal_places'], 'currency_decimal_places' => (int)$row['currency_decimal_places'],
]; ];
// left per day (native) // left per day (native)

View File

@@ -42,7 +42,7 @@ class BalanceChartRequest extends FormRequest
{ {
return [ return [
'accounts' => $this->getAccountList(), 'accounts' => $this->getAccountList(),
'period' => $this->string('period'), 'period' => $this->convertString('period'),
]; ];
} }

View File

@@ -34,6 +34,7 @@ use FireflyIII\Models\PiggyBankRepetition;
use FireflyIII\Models\RecurrenceTransaction; use FireflyIII\Models\RecurrenceTransaction;
use FireflyIII\Models\RuleTrigger; use FireflyIII\Models\RuleTrigger;
use Illuminate\Console\Command; use Illuminate\Console\Command;
use ValueError;
/** /**
* Class ReportSkeleton * Class ReportSkeleton
@@ -247,7 +248,16 @@ class CorrectAmounts extends Command
/** @var RuleTrigger $item */ /** @var RuleTrigger $item */
foreach ($set as $item) { foreach ($set as $item) {
// basic check: // basic check:
if (-1 === bccomp((string)$item->trigger_value, '0')) { $check = 0;
try {
$check = bccomp((string)$item->trigger_value, '0');
} catch (ValueError $e) {
$this->friendlyError(sprintf('Rule #%d contained invalid %s-trigger "%s". The trigger has been removed, and the rule is disabled.', $item->rule_id, $item->trigger_type, $item->trigger_value));
$item->rule->active = false;
$item->rule->save();
$item->forceDelete();
}
if (-1 === $check) {
$fixed++; $fixed++;
$item->trigger_value = app('steam')->positive((string)$item->trigger_value); $item->trigger_value = app('steam')->positive((string)$item->trigger_value);
$item->save(); $item->save();

View File

@@ -82,6 +82,7 @@ class DeleteEmptyJournals extends Command
TransactionJournal::find((int)$row->transaction_journal_id)->delete(); TransactionJournal::find((int)$row->transaction_journal_id)->delete();
} catch (QueryException $e) { } catch (QueryException $e) {
Log::info(sprintf('Could not delete journal: %s', $e->getMessage())); Log::info(sprintf('Could not delete journal: %s', $e->getMessage()));
Log::error($e->getTraceAsString());
} }
@@ -113,6 +114,7 @@ class DeleteEmptyJournals extends Command
TransactionJournal::find($entry->id)->delete(); TransactionJournal::find($entry->id)->delete();
} catch (QueryException $e) { } catch (QueryException $e) {
Log::info(sprintf('Could not delete entry: %s', $e->getMessage())); Log::info(sprintf('Could not delete entry: %s', $e->getMessage()));
Log::error($e->getTraceAsString());
} }

View File

@@ -0,0 +1,52 @@
<?php
declare(strict_types=1);
/*
* RuleActionFailedOnArray.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\Events\Model\Rule;
use FireflyIII\Models\RuleAction;
use Illuminate\Queue\SerializesModels;
/**
* Class RuleActionFailedOnArray
*/
class RuleActionFailedOnArray
{
use SerializesModels;
public string $error;
public array $journal;
public RuleAction $ruleAction;
/**
* @param RuleAction $ruleAction
* @param array $journal
* @param string $error
*/
public function __construct(RuleAction $ruleAction, array $journal, string $error)
{
app('log')->debug('Created new RuleActionFailedOnArray');
$this->ruleAction = $ruleAction;
$this->journal = $journal;
$this->error = $error;
}
}

View File

@@ -0,0 +1,53 @@
<?php
declare(strict_types=1);
/*
* RuleActionFailedOnArray.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\Events\Model\Rule;
use FireflyIII\Models\RuleAction;
use FireflyIII\Models\TransactionJournal;
use Illuminate\Queue\SerializesModels;
/**
* Class RuleActionFailedOnObject
*/
class RuleActionFailedOnObject
{
use SerializesModels;
public string $error;
public TransactionJournal $journal;
public RuleAction $ruleAction;
/**
* @param RuleAction $ruleAction
* @param TransactionJournal $journal
* @param string $error
*/
public function __construct(RuleAction $ruleAction, TransactionJournal $journal, string $error)
{
app('log')->debug('Created new RuleActionFailedOnObject');
$this->ruleAction = $ruleAction;
$this->journal = $journal;
$this->error = $error;
}
}

View File

@@ -218,6 +218,7 @@ class AccountFactory
} }
// create account! // create account!
$account = Account::create($databaseData); $account = Account::create($databaseData);
Log::channel('audit')->info(sprintf('Account #%d ("%s") has been created.', $account->id, $account->name));
// update meta data: // update meta data:
$data = $this->cleanMetaDataArray($account, $data); $data = $this->cleanMetaDataArray($account, $data);
@@ -228,6 +229,7 @@ class AccountFactory
$this->storeOpeningBalance($account, $data); $this->storeOpeningBalance($account, $data);
} catch (FireflyException $e) { } catch (FireflyException $e) {
Log::error($e->getMessage()); Log::error($e->getMessage());
Log::error($e->getTraceAsString());
} }
// create credit liability data (only liabilities) // create credit liability data (only liabilities)
@@ -235,6 +237,7 @@ class AccountFactory
$this->storeCreditLiability($account, $data); $this->storeCreditLiability($account, $data);
} catch (FireflyException $e) { } catch (FireflyException $e) {
Log::error($e->getMessage()); Log::error($e->getMessage());
Log::error($e->getTraceAsString());
} }
// create notes // create notes

View File

@@ -26,6 +26,7 @@ namespace FireflyIII\Factory;
use FireflyIII\Models\Account; use FireflyIII\Models\Account;
use FireflyIII\Models\AccountMeta; use FireflyIII\Models\AccountMeta;
use Illuminate\Support\Facades\Log;
/** /**
* Class AccountMetaFactory * Class AccountMetaFactory

View File

@@ -77,6 +77,7 @@ class CategoryFactory
); );
} catch (QueryException $e) { } catch (QueryException $e) {
Log::error($e->getMessage()); Log::error($e->getMessage());
Log::error($e->getTraceAsString());
throw new FireflyException('400003: Could not store new category.', 0, $e); throw new FireflyException('400003: Could not store new category.', 0, $e);
} }
} }

View File

@@ -130,6 +130,7 @@ class RecurrenceFactory
$this->createTransactions($recurrence, $data['transactions'] ?? []); $this->createTransactions($recurrence, $data['transactions'] ?? []);
} catch (FireflyException $e) { } catch (FireflyException $e) {
Log::error($e->getMessage()); Log::error($e->getMessage());
Log::error($e->getTraceAsString());
$recurrence->forceDelete(); $recurrence->forceDelete();
$message = sprintf('Could not create recurring transaction: %s', $e->getMessage()); $message = sprintf('Could not create recurring transaction: %s', $e->getMessage());
$this->errors->add('store', $message); $this->errors->add('store', $message);

View File

@@ -69,6 +69,7 @@ class TransactionCurrencyFactory
} catch (QueryException $e) { } catch (QueryException $e) {
$result = null; $result = null;
Log::error(sprintf('Could not create new currency: %s', $e->getMessage())); Log::error(sprintf('Could not create new currency: %s', $e->getMessage()));
Log::error($e->getTraceAsString());
throw new FireflyException('400004: Could not store new currency.', 0, $e); throw new FireflyException('400004: Could not store new currency.', 0, $e);
} }

View File

@@ -110,7 +110,7 @@ class TransactionFactory
Log::error(sprintf('Could not create transaction: %s', $e->getMessage()), $data); Log::error(sprintf('Could not create transaction: %s', $e->getMessage()), $data);
Log::error($e->getMessage()); Log::error($e->getMessage());
Log::error($e->getTraceAsString()); Log::error($e->getTraceAsString());
throw new FireflyException('Query exception when creating transaction.', 0, $e); throw new FireflyException(sprintf('Query exception when creating transaction: %s', $e->getMessage()), 0, $e);
} }
if (null === $result) { if (null === $result) {
throw new FireflyException('Transaction is NULL.'); throw new FireflyException('Transaction is NULL.');

View File

@@ -75,6 +75,7 @@ class MonthReportGenerator implements ReportGeneratorInterface
->render(); ->render();
} catch (Throwable $e) { } catch (Throwable $e) {
Log::error(sprintf('Cannot render reports.account.report: %s', $e->getMessage())); Log::error(sprintf('Cannot render reports.account.report: %s', $e->getMessage()));
Log::error($e->getTraceAsString());
$result = sprintf('Could not render report view: %s', $e->getMessage()); $result = sprintf('Could not render report view: %s', $e->getMessage());
throw new FireflyException($result, 0, $e); throw new FireflyException($result, 0, $e);
} }

View File

@@ -77,6 +77,7 @@ class MonthReportGenerator implements ReportGeneratorInterface
->render(); ->render();
} catch (Throwable $e) { } catch (Throwable $e) {
Log::error(sprintf('Cannot render reports.category.month: %s', $e->getMessage())); Log::error(sprintf('Cannot render reports.category.month: %s', $e->getMessage()));
Log::error($e->getTraceAsString());
$result = sprintf('Could not render report view: %s', $e->getMessage()); $result = sprintf('Could not render report view: %s', $e->getMessage());
throw new FireflyException($result, 0, $e); throw new FireflyException($result, 0, $e);
} }

View File

@@ -59,6 +59,7 @@ class MonthReportGenerator implements ReportGeneratorInterface
return view('reports.default.month', compact('accountIds', 'reportType'))->with('start', $this->start)->with('end', $this->end)->render(); return view('reports.default.month', compact('accountIds', 'reportType'))->with('start', $this->start)->with('end', $this->end)->render();
} catch (Throwable $e) { } catch (Throwable $e) {
Log::error(sprintf('Cannot render reports.default.month: %s', $e->getMessage())); Log::error(sprintf('Cannot render reports.default.month: %s', $e->getMessage()));
Log::error($e->getTraceAsString());
$result = 'Could not render report view.'; $result = 'Could not render report view.';
throw new FireflyException($result, 0, $e); throw new FireflyException($result, 0, $e);
} }

View File

@@ -63,6 +63,7 @@ class MultiYearReportGenerator implements ReportGeneratorInterface
)->with('start', $this->start)->with('end', $this->end)->render(); )->with('start', $this->start)->with('end', $this->end)->render();
} catch (Throwable $e) { } catch (Throwable $e) {
Log::error(sprintf('Cannot render reports.default.multi-year: %s', $e->getMessage())); Log::error(sprintf('Cannot render reports.default.multi-year: %s', $e->getMessage()));
Log::error($e->getTraceAsString());
$result = sprintf('Could not render report view: %s', $e->getMessage()); $result = sprintf('Could not render report view: %s', $e->getMessage());
throw new FireflyException($result, 0, $e); throw new FireflyException($result, 0, $e);
} }

View File

@@ -63,6 +63,7 @@ class YearReportGenerator implements ReportGeneratorInterface
)->with('start', $this->start)->with('end', $this->end)->render(); )->with('start', $this->start)->with('end', $this->end)->render();
} catch (Throwable $e) { } catch (Throwable $e) {
Log::error(sprintf('Cannot render reports.account.report: %s', $e->getMessage())); Log::error(sprintf('Cannot render reports.account.report: %s', $e->getMessage()));
Log::error($e->getTraceAsString());
$result = 'Could not render report view.'; $result = 'Could not render report view.';
throw new FireflyException($result, 0, $e); throw new FireflyException($result, 0, $e);
} }

View File

@@ -71,6 +71,7 @@ class MonthReportGenerator implements ReportGeneratorInterface
)->with('start', $this->start)->with('end', $this->end)->with('tags', $this->tags)->with('accounts', $this->accounts)->render(); )->with('start', $this->start)->with('end', $this->end)->with('tags', $this->tags)->with('accounts', $this->accounts)->render();
} catch (Throwable $e) { } catch (Throwable $e) {
Log::error(sprintf('Cannot render reports.tag.month: %s', $e->getMessage())); Log::error(sprintf('Cannot render reports.tag.month: %s', $e->getMessage()));
Log::error($e->getTraceAsString());
$result = sprintf('Could not render report view: %s', $e->getMessage()); $result = sprintf('Could not render report view: %s', $e->getMessage());
throw new FireflyException($result, 0, $e); throw new FireflyException($result, 0, $e);
} }

View File

@@ -174,6 +174,7 @@ class StandardMessageGenerator implements MessageGeneratorInterface
Log::error( Log::error(
sprintf('The transformer could not include the requested transaction group for webhook #%d: %s', $webhook->id, $e->getMessage()) sprintf('The transformer could not include the requested transaction group for webhook #%d: %s', $webhook->id, $e->getMessage())
); );
Log::error($e->getTraceAsString());
return; return;
} }

View File

@@ -166,7 +166,7 @@ class BudgetLimitHandler
$availableBudget->end_date->format('Y-m-d') $availableBudget->end_date->format('Y-m-d')
) )
); );
// have to recalc everything just in case. // have to recalculate everything just in case.
$set = $repository->getAllBudgetLimitsByCurrency($availableBudget->transactionCurrency, $availableBudget->start_date, $availableBudget->end_date); $set = $repository->getAllBudgetLimitsByCurrency($availableBudget->transactionCurrency, $availableBudget->start_date, $availableBudget->end_date);
Log::debug(sprintf('Found %d interesting budget limit(s).', $set->count())); Log::debug(sprintf('Found %d interesting budget limit(s).', $set->count()));
/** @var BudgetLimit $budgetLimit */ /** @var BudgetLimit $budgetLimit */
@@ -188,13 +188,16 @@ class BudgetLimitHandler
); );
// if both equal each other, amount from this BL must be added to the AB // if both equal each other, amount from this BL must be added to the AB
if ($limitPeriod->equals($abPeriod)) { if ($limitPeriod->equals($abPeriod)) {
app('log')->debug('This budget limit is equal to the available budget period.');
$newAmount = bcadd($newAmount, $budgetLimit->amount); $newAmount = bcadd($newAmount, $budgetLimit->amount);
} }
// if budget limit period inside AB period, can be added in full. // if budget limit period is inside AB period, it can be added in full.
if (!$limitPeriod->equals($abPeriod) && $abPeriod->contains($limitPeriod)) { if (!$limitPeriod->equals($abPeriod) && $abPeriod->contains($limitPeriod)) {
app('log')->debug('This budget limit is smaller than the available budget period.');
$newAmount = bcadd($newAmount, $budgetLimit->amount); $newAmount = bcadd($newAmount, $budgetLimit->amount);
} }
if (!$limitPeriod->equals($abPeriod) && $abPeriod->overlapsWith($limitPeriod)) { if (!$limitPeriod->equals($abPeriod) && !$abPeriod->contains($limitPeriod) && $abPeriod->overlapsWith($limitPeriod)) {
app('log')->debug('This budget limit is something else entirely!');
$overlap = $abPeriod->overlap($limitPeriod); $overlap = $abPeriod->overlap($limitPeriod);
if (null !== $overlap) { if (null !== $overlap) {
$length = $overlap->length(); $length = $overlap->length();
@@ -209,7 +212,7 @@ class BudgetLimitHandler
return; return;
} }
Log::debug(sprintf('Concluded new amount for this AB must be %s', $newAmount)); Log::debug(sprintf('Concluded new amount for this AB must be %s', $newAmount));
$availableBudget->amount = $newAmount; $availableBudget->amount = app('steam')->bcround($newAmount, $availableBudget->transactionCurrency->decimal_places);
$availableBudget->save(); $availableBudget->save();
} }

View File

@@ -0,0 +1,95 @@
<?php
declare(strict_types=1);
/*
* RuleHandler.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\Handlers\Events\Model;
use FireflyIII\Events\Model\Rule\RuleActionFailedOnArray;
use FireflyIII\Events\Model\Rule\RuleActionFailedOnObject;
use FireflyIII\Notifications\User\RuleActionFailed;
use FireflyIII\Support\Facades\Preferences;
use Illuminate\Support\Facades\Notification;
/**
* Class RuleHandler
*/
class RuleHandler
{
/**
* @param RuleActionFailedOnArray $event
*
* @return void
*/
public function ruleActionFailedOnArray(RuleActionFailedOnArray $event): void
{
$ruleAction = $event->ruleAction;
$rule = $ruleAction->rule;
$preference = Preferences::getForUser($rule->user, 'notification_rule_action_failures', true)->data;
if (false === $preference) {
return;
}
app('log')->debug('Now in ruleActionFailedOnArray');
$journal = $event->journal;
$error = $event->error;
$user = $ruleAction->rule->user;
$mainMessage = trans('rules.main_message', ['rule' => $rule->title, 'action' => $ruleAction->action_type, 'group' => $journal['transaction_group_id'], 'error' => $error]);
$groupTitle = $journal['description'] ?? '';
$groupLink = route('transactions.show', [$journal['transaction_group_id']]);
$ruleTitle = $rule->title;
$ruleLink = route('rules.edit', [$rule->id]);
$params = [$mainMessage, $groupTitle, $groupLink, $ruleTitle, $ruleLink];
Notification::send($user, new RuleActionFailed($params));
}
/**
* @param RuleActionFailedOnObject $event
*
* @return void
*/
public function ruleActionFailedOnObject(RuleActionFailedOnObject $event): void
{
$ruleAction = $event->ruleAction;
$rule = $ruleAction->rule;
$preference = Preferences::getForUser($rule->user, 'notification_rule_action_failures', true)->data;
if (false === $preference) {
return;
}
app('log')->debug('Now in ruleActionFailedOnObject');
$journal = $event->journal;
$error = $event->error;
$user = $ruleAction->rule->user;
$mainMessage = trans('rules.main_message', ['rule' => $rule->title, 'action' => $ruleAction->action_type, 'group' => $journal->transaction_group_id, 'error' => $error]);
$groupTitle = $journal->description ?? '';
$groupLink = route('transactions.show', [$journal->transaction_group_id]);
$ruleTitle = $rule->title;
$ruleLink = route('rules.edit', [$rule->id]);
$params = [$mainMessage, $groupTitle, $groupLink, $ruleTitle, $ruleLink];
Notification::send($user, new RuleActionFailed($params));
}
}

View File

@@ -275,6 +275,7 @@ class UserEventHandler
Mail::to($newEmail)->send(new ConfirmEmailChangeMail($newEmail, $oldEmail, $url)); Mail::to($newEmail)->send(new ConfirmEmailChangeMail($newEmail, $oldEmail, $url));
} catch (Exception $e) { // intentional generic exception } catch (Exception $e) { // intentional generic exception
Log::error($e->getMessage()); Log::error($e->getMessage());
Log::error($e->getTraceAsString());
throw new FireflyException($e->getMessage(), 0, $e); throw new FireflyException($e->getMessage(), 0, $e);
} }
} }
@@ -299,6 +300,7 @@ class UserEventHandler
Mail::to($oldEmail)->send(new UndoEmailChangeMail($newEmail, $oldEmail, $url)); Mail::to($oldEmail)->send(new UndoEmailChangeMail($newEmail, $oldEmail, $url));
} catch (Exception $e) { // intentional generic exception } catch (Exception $e) { // intentional generic exception
Log::error($e->getMessage()); Log::error($e->getMessage());
Log::error($e->getTraceAsString());
throw new FireflyException($e->getMessage(), 0, $e); throw new FireflyException($e->getMessage(), 0, $e);
} }
} }
@@ -342,6 +344,7 @@ class UserEventHandler
Mail::to($invitee)->send(new InvitationMail($invitee, $admin, $url)); Mail::to($invitee)->send(new InvitationMail($invitee, $admin, $url));
} catch (Exception $e) { // intentional generic exception } catch (Exception $e) { // intentional generic exception
Log::error($e->getMessage()); Log::error($e->getMessage());
Log::error($e->getTraceAsString());
throw new FireflyException($e->getMessage(), 0, $e); throw new FireflyException($e->getMessage(), 0, $e);
} }
} }

View File

@@ -57,7 +57,7 @@ class VersionCheckEventHandler
$permission = app('fireflyconfig')->get('permission_update_check', -1); $permission = app('fireflyconfig')->get('permission_update_check', -1);
$value = (int)$permission->data; $value = (int)$permission->data;
if (1 !== $value) { if (1 !== $value) {
Log::info('Update check is not enabled.'); Log::debug('Update check is not enabled.');
$this->warnToCheckForUpdates($event); $this->warnToCheckForUpdates($event);
return; return;

View File

@@ -871,7 +871,7 @@ class GroupCollector implements GroupCollectorInterface
public function setLimit(int $limit): GroupCollectorInterface public function setLimit(int $limit): GroupCollectorInterface
{ {
$this->limit = $limit; $this->limit = $limit;
app('log')->debug(sprintf('GroupCollector: The limit is now %d', $limit)); //app('log')->debug(sprintf('GroupCollector: The limit is now %d', $limit));
return $this; return $this;
} }
@@ -976,7 +976,7 @@ class GroupCollector implements GroupCollectorInterface
{ {
$page = 0 === $page ? 1 : $page; $page = 0 === $page ? 1 : $page;
$this->page = $page; $this->page = $page;
app('log')->debug(sprintf('GroupCollector: page is now %d', $page)); //app('log')->debug(sprintf('GroupCollector: page is now %d', $page));
return $this; return $this;
} }

View File

@@ -27,6 +27,7 @@ use FireflyIII\Events\AdminRequestedTestMessage;
use FireflyIII\Http\Controllers\Controller; use FireflyIII\Http\Controllers\Controller;
use FireflyIII\Http\Middleware\IsDemoUser; use FireflyIII\Http\Middleware\IsDemoUser;
use FireflyIII\Support\Facades\FireflyConfig; use FireflyIII\Support\Facades\FireflyConfig;
use FireflyIII\Support\Notifications\UrlValidator;
use FireflyIII\User; use FireflyIII\User;
use Illuminate\Contracts\View\Factory; use Illuminate\Contracts\View\Factory;
use Illuminate\Http\RedirectResponse; use Illuminate\Http\RedirectResponse;
@@ -99,7 +100,7 @@ class HomeController extends Controller
if ('' === $url) { if ('' === $url) {
FireflyConfig::delete('slack_webhook_url'); FireflyConfig::delete('slack_webhook_url');
} }
if (str_starts_with($url, 'https://hooks.slack.com/services/')) { if (UrlValidator::isValidWebhookURL($url)) {
FireflyConfig::set('slack_webhook_url', $url); FireflyConfig::set('slack_webhook_url', $url);
} }

View File

@@ -146,7 +146,7 @@ class IndexController extends Controller
private function getSums(array $bills): array private function getSums(array $bills): array
{ {
$sums = []; $sums = [];
$range = app('navigation')->getViewRange(false); $range = app('navigation')->getViewRange(true);
/** @var array $group */ /** @var array $group */
foreach ($bills as $groupOrder => $group) { foreach ($bills as $groupOrder => $group) {
@@ -177,7 +177,6 @@ class IndexController extends Controller
$sums[$groupOrder][$currencyId]['per_period'] = bcadd($sums[$groupOrder][$currencyId]['per_period'], $this->amountPerPeriod($bill, $range)); $sums[$groupOrder][$currencyId]['per_period'] = bcadd($sums[$groupOrder][$currencyId]['per_period'], $this->amountPerPeriod($bill, $range));
} }
} }
return $sums; return $sums;
} }

View File

@@ -101,6 +101,7 @@ class IndexController extends Controller
*/ */
public function index(Request $request, Carbon $start = null, Carbon $end = null) public function index(Request $request, Carbon $start = null, Carbon $end = null)
{ {
$this->abRepository->cleanup();
Log::debug(sprintf('Start of IndexController::index("%s", "%s")', $start?->format('Y-m-d'), $end?->format('Y-m-d'))); Log::debug(sprintf('Start of IndexController::index("%s", "%s")', $start?->format('Y-m-d'), $end?->format('Y-m-d')));
// collect some basic vars: // collect some basic vars:
@@ -337,6 +338,7 @@ class IndexController extends Controller
*/ */
public function reorder(Request $request, BudgetRepositoryInterface $repository): JsonResponse public function reorder(Request $request, BudgetRepositoryInterface $repository): JsonResponse
{ {
$this->abRepository->cleanup();
$budgetIds = $request->get('budgetIds'); $budgetIds = $request->get('budgetIds');
foreach ($budgetIds as $index => $budgetId) { foreach ($budgetIds as $index => $budgetId) {

View File

@@ -138,6 +138,9 @@ class HomeController extends Controller
$accounts = $repository->getAccountsById($frontPage->data); $accounts = $repository->getAccountsById($frontPage->data);
$today = today(config('app.timezone')); $today = today(config('app.timezone'));
// sort frontpage accounts by order
$accounts = $accounts->sortBy('order');
Log::debug('Frontpage accounts are ', $frontPage->data); Log::debug('Frontpage accounts are ', $frontPage->data);
/** @var BillRepositoryInterface $billRepository */ /** @var BillRepositoryInterface $billRepository */

View File

@@ -55,10 +55,12 @@ class BoxController extends Controller
*/ */
public function available(): JsonResponse public function available(): JsonResponse
{ {
app('log')->debug('Now in available()');
/** @var OperationsRepositoryInterface $opsRepository */ /** @var OperationsRepositoryInterface $opsRepository */
$opsRepository = app(OperationsRepositoryInterface::class); $opsRepository = app(OperationsRepositoryInterface::class);
/** @var AvailableBudgetRepositoryInterface $abRepository */ /** @var AvailableBudgetRepositoryInterface $abRepository */
$abRepository = app(AvailableBudgetRepositoryInterface::class); $abRepository = app(AvailableBudgetRepositoryInterface::class);
$abRepository->cleanup();
/** @var Carbon $start */ /** @var Carbon $start */
$start = session('start', today(config('app.timezone'))->startOfMonth()); $start = session('start', today(config('app.timezone'))->startOfMonth());
/** @var Carbon $end */ /** @var Carbon $end */
@@ -73,40 +75,57 @@ class BoxController extends Controller
$cache->addProperty($today); $cache->addProperty($today);
$cache->addProperty('box-available'); $cache->addProperty('box-available');
if ($cache->has()) { if ($cache->has()) {
return response()->json($cache->get()); //return response()->json($cache->get());
} }
$leftPerDayAmount = '0'; $leftPerDayAmount = '0';
$leftToSpendAmount = '0'; $leftToSpendAmount = '0';
$currency = app('amount')->getDefaultCurrency(); $currency = app('amount')->getDefaultCurrency();
$availableBudgets = $abRepository->getAvailableBudgetsByDate($start, $end); app('log')->debug(sprintf('Default currency is %s', $currency->code));
$availableBudgets = $abRepository->getAvailableBudgetsByExactDate($start, $end);
app('log')->debug(sprintf('Found %d available budget(s)', $availableBudgets->count()));
$availableBudgets = $availableBudgets->filter( $availableBudgets = $availableBudgets->filter(
static function (AvailableBudget $availableBudget) use ($currency) { static function (AvailableBudget $availableBudget) use ($currency) {
if ($availableBudget->transaction_currency_id === $currency->id) { if ($availableBudget->transaction_currency_id === $currency->id) {
app('log')->debug(sprintf(
'Will include AB #%d: from %s-%s amount %s',
$availableBudget->id,
$availableBudget->start_date->format('Y-m-d'),
$availableBudget->end_date->format('Y-m-d'),
$availableBudget->amount
));
return $availableBudget; return $availableBudget;
} }
return null; return null;
} }
); );
app('log')->debug(sprintf('Filtered back to %d available budgets', $availableBudgets->count()));
// spent in this period, in budgets, for default currency. // spent in this period, in budgets, for default currency.
// also calculate spent per day. // also calculate spent per day.
$spent = $opsRepository->sumExpenses($start, $end, null, null, $currency); $spent = $opsRepository->sumExpenses($start, $end, null, null, $currency);
$spentAmount = $spent[(int)$currency->id]['sum'] ?? '0'; $spentAmount = $spent[(int)$currency->id]['sum'] ?? '0';
app('log')->debug(sprintf('Spent for default currency for all budgets in this period: %s', $spentAmount));
$days = $today->between($start, $end) ? $today->diffInDays($start) + 1 : $end->diffInDays($start) + 1; $days = $today->between($start, $end) ? $today->diffInDays($start) + 1 : $end->diffInDays($start) + 1;
app('log')->debug(sprintf('Number of days left: %d', $days));
$spentPerDay = bcdiv($spentAmount, (string)$days); $spentPerDay = bcdiv($spentAmount, (string)$days);
app('log')->debug(sprintf('Available to spend per day: %s', $spentPerDay));
if ($availableBudgets->count() > 0) { if ($availableBudgets->count() > 0) {
$display = 0; // assume user overspent $display = 0; // assume user overspent
$boxTitle = (string)trans('firefly.overspent'); $boxTitle = (string)trans('firefly.overspent');
$totalAvailableSum = (string)$availableBudgets->sum('amount'); $totalAvailableSum = (string)$availableBudgets->sum('amount');
app('log')->debug(sprintf('Total available sum is %s', $totalAvailableSum));
// calculate with available budget. // calculate with available budget.
$leftToSpendAmount = bcadd($totalAvailableSum, $spentAmount); $leftToSpendAmount = bcadd($totalAvailableSum, $spentAmount);
app('log')->debug(sprintf('So left to spend is %s', $leftToSpendAmount));
if (1 === bccomp($leftToSpendAmount, '0')) { if (1 === bccomp($leftToSpendAmount, '0')) {
app('log')->debug(sprintf('Left to spend is positive!'));
$boxTitle = (string)trans('firefly.left_to_spend'); $boxTitle = (string)trans('firefly.left_to_spend');
$days = $today->diffInDays($end) + 1; $days = $today->diffInDays($end) + 1;
$display = 1; // not overspent $display = 1; // not overspent
$leftPerDayAmount = bcdiv($leftToSpendAmount, (string)$days); $leftPerDayAmount = bcdiv($leftToSpendAmount, (string)$days);
app('log')->debug(sprintf('Left to spend per day is %s', $leftPerDayAmount));
} }
} }
@@ -118,9 +137,10 @@ class BoxController extends Controller
'left_per_day' => app('amount')->formatAnything($currency, $leftPerDayAmount, false), 'left_per_day' => app('amount')->formatAnything($currency, $leftPerDayAmount, false),
'title' => $boxTitle, 'title' => $boxTitle,
]; ];
app('log')->debug('Final output', $return);
$cache->store($return); $cache->store($return);
app('log')->debug('Now done with available()');
return response()->json($return); return response()->json($return);
} }

View File

@@ -75,6 +75,7 @@ class FrontpageController extends Controller
$html = view('json.piggy-banks', compact('info'))->render(); $html = view('json.piggy-banks', compact('info'))->render();
} catch (Throwable $e) { } catch (Throwable $e) {
Log::error(sprintf('Cannot render json.piggy-banks: %s', $e->getMessage())); Log::error(sprintf('Cannot render json.piggy-banks: %s', $e->getMessage()));
Log::error($e->getTraceAsString());
$html = 'Could not render view.'; $html = 'Could not render view.';
throw new FireflyException($html, 0, $e); throw new FireflyException($html, 0, $e);
} }

View File

@@ -157,6 +157,7 @@ class ReconcileController extends Controller
)->render(); )->render();
} catch (Throwable $e) { } catch (Throwable $e) {
Log::debug(sprintf('View error: %s', $e->getMessage())); Log::debug(sprintf('View error: %s', $e->getMessage()));
Log::error($e->getTraceAsString());
$view = sprintf('Could not render accounts.reconcile.overview: %s', $e->getMessage()); $view = sprintf('Could not render accounts.reconcile.overview: %s', $e->getMessage());
throw new FireflyException($view, 0, $e); throw new FireflyException($view, 0, $e);
} }
@@ -258,6 +259,7 @@ class ReconcileController extends Controller
)->render(); )->render();
} catch (Throwable $e) { } catch (Throwable $e) {
Log::debug(sprintf('Could not render: %s', $e->getMessage())); Log::debug(sprintf('Could not render: %s', $e->getMessage()));
Log::error($e->getTraceAsString());
$html = sprintf('Could not render accounts.reconcile.transactions: %s', $e->getMessage()); $html = sprintf('Could not render accounts.reconcile.transactions: %s', $e->getMessage());
throw new FireflyException($html, 0, $e); throw new FireflyException($html, 0, $e);
} }

View File

@@ -55,6 +55,7 @@ class RuleController extends Controller
$view = view('rules.partials.action', compact('actions', 'count'))->render(); $view = view('rules.partials.action', compact('actions', 'count'))->render();
} catch (Throwable $e) { } catch (Throwable $e) {
Log::error(sprintf('Cannot render rules.partials.action: %s', $e->getMessage())); Log::error(sprintf('Cannot render rules.partials.action: %s', $e->getMessage()));
Log::error($e->getTraceAsString());
$view = 'Could not render view.'; $view = 'Could not render view.';
throw new FireflyException($view, 0, $e); throw new FireflyException($view, 0, $e);
} }
@@ -86,6 +87,7 @@ class RuleController extends Controller
$view = view('rules.partials.trigger', compact('triggers', 'count'))->render(); $view = view('rules.partials.trigger', compact('triggers', 'count'))->render();
} catch (Throwable $e) { } catch (Throwable $e) {
Log::error(sprintf('Cannot render rules.partials.trigger: %s', $e->getMessage())); Log::error(sprintf('Cannot render rules.partials.trigger: %s', $e->getMessage()));
Log::error($e->getTraceAsString());
$view = 'Could not render view.'; $view = 'Could not render view.';
throw new FireflyException($view, 0, $e); throw new FireflyException($view, 0, $e);
} }

View File

@@ -28,6 +28,7 @@ use FireflyIII\Models\Account;
use FireflyIII\Models\AccountType; use FireflyIII\Models\AccountType;
use FireflyIII\Models\Preference; use FireflyIII\Models\Preference;
use FireflyIII\Repositories\Account\AccountRepositoryInterface; use FireflyIII\Repositories\Account\AccountRepositoryInterface;
use FireflyIII\Support\Notifications\UrlValidator;
use Illuminate\Contracts\View\Factory; use Illuminate\Contracts\View\Factory;
use Illuminate\Http\RedirectResponse; use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request; use Illuminate\Http\Request;
@@ -207,7 +208,7 @@ class PreferencesController extends Controller
// slack URL: // slack URL:
if (!auth()->user()->hasRole('demo')) { if (!auth()->user()->hasRole('demo')) {
$url = (string)$request->get('slackUrl'); $url = (string)$request->get('slackUrl');
if (str_starts_with($url, 'https://hooks.slack.com/services/')) { if (UrlValidator::isValidWebhookURL($url)) {
app('preferences')->set('slack_webhook_url', $url); app('preferences')->set('slack_webhook_url', $url);
} }
if ('' === $url) { if ('' === $url) {

View File

@@ -65,7 +65,8 @@ class AccountController extends Controller
try { try {
$result = view('reports.partials.accounts', compact('accountReport'))->render(); $result = view('reports.partials.accounts', compact('accountReport'))->render();
} catch (Throwable $e) { } catch (Throwable $e) {
Log::debug(sprintf('Could not render reports.partials.accounts: %s', $e->getMessage())); Log::error(sprintf('Could not render reports.partials.accounts: %s', $e->getMessage()));
Log::error($e->getTraceAsString());
$result = 'Could not render view.'; $result = 'Could not render view.';
throw new FireflyException($result, 0, $e); throw new FireflyException($result, 0, $e);
} }

View File

@@ -141,7 +141,8 @@ class BalanceController extends Controller
try { try {
$result = view('reports.partials.balance', compact('report'))->render(); $result = view('reports.partials.balance', compact('report'))->render();
} catch (Throwable $e) { } catch (Throwable $e) {
Log::debug(sprintf('Could not render reports.partials.balance: %s', $e->getMessage())); Log::error(sprintf('Could not render reports.partials.balance: %s', $e->getMessage()));
Log::error($e->getTraceAsString());
$result = 'Could not render view.'; $result = 'Could not render view.';
throw new FireflyException($result, 0, $e); throw new FireflyException($result, 0, $e);
} }

View File

@@ -61,7 +61,8 @@ class BillController extends Controller
try { try {
$result = view('reports.partials.bills', compact('report'))->render(); $result = view('reports.partials.bills', compact('report'))->render();
} catch (Throwable $e) { } catch (Throwable $e) {
Log::debug(sprintf('Could not render reports.partials.budgets: %s', $e->getMessage())); Log::error(sprintf('Could not render reports.partials.budgets: %s', $e->getMessage()));
Log::error($e->getTraceAsString());
$result = 'Could not render view.'; $result = 'Could not render view.';
throw new FireflyException($result, 0, $e); throw new FireflyException($result, 0, $e);
} }

View File

@@ -195,8 +195,9 @@ class BudgetController extends Controller
try { try {
$result = view('reports.budget.partials.avg-expenses', compact('result'))->render(); $result = view('reports.budget.partials.avg-expenses', compact('result'))->render();
} catch (Throwable $e) { } catch (Throwable $e) {
Log::debug(sprintf('Could not render reports.partials.budget-period: %s', $e->getMessage())); Log::error(sprintf('Could not render reports.partials.budget-period: %s', $e->getMessage()));
$result = sprintf('Could not render view: %s', $e->getMessage()); $result = sprintf('Could not render view: %s', $e->getMessage());
Log::error($e->getTraceAsString());
throw new FireflyException($result, 0, $e); throw new FireflyException($result, 0, $e);
} }
@@ -353,7 +354,8 @@ class BudgetController extends Controller
try { try {
$result = view('reports.partials.budget-period', compact('report', 'periods'))->render(); $result = view('reports.partials.budget-period', compact('report', 'periods'))->render();
} catch (Throwable $e) { } catch (Throwable $e) {
Log::debug(sprintf('Could not render reports.partials.budget-period: %s', $e->getMessage())); Log::error(sprintf('Could not render reports.partials.budget-period: %s', $e->getMessage()));
Log::error($e->getTraceAsString());
$result = 'Could not render view.'; $result = 'Could not render view.';
throw new FireflyException($result, 0, $e); throw new FireflyException($result, 0, $e);
} }
@@ -406,7 +408,7 @@ class BudgetController extends Controller
try { try {
$result = view('reports.budget.partials.top-expenses', compact('result'))->render(); $result = view('reports.budget.partials.top-expenses', compact('result'))->render();
} catch (Throwable $e) { } catch (Throwable $e) {
Log::debug(sprintf('Could not render reports.partials.budget-period: %s', $e->getMessage())); Log::error(sprintf('Could not render reports.partials.budget-period: %s', $e->getMessage()));
$result = sprintf('Could not render view: %s', $e->getMessage()); $result = sprintf('Could not render view: %s', $e->getMessage());
throw new FireflyException($result, 0, $e); throw new FireflyException($result, 0, $e);
} }

View File

@@ -315,7 +315,7 @@ class CategoryController extends Controller
try { try {
$result = view('reports.category.partials.avg-expenses', compact('result'))->render(); $result = view('reports.category.partials.avg-expenses', compact('result'))->render();
} catch (Throwable $e) { } catch (Throwable $e) {
Log::debug(sprintf('Could not render reports.partials.budget-period: %s', $e->getMessage())); Log::error(sprintf('Could not render reports.partials.budget-period: %s', $e->getMessage()));
$result = sprintf('Could not render view: %s', $e->getMessage()); $result = sprintf('Could not render view: %s', $e->getMessage());
throw new FireflyException($result, 0, $e); throw new FireflyException($result, 0, $e);
} }
@@ -368,7 +368,7 @@ class CategoryController extends Controller
try { try {
$result = view('reports.category.partials.avg-income', compact('result'))->render(); $result = view('reports.category.partials.avg-income', compact('result'))->render();
} catch (Throwable $e) { } catch (Throwable $e) {
Log::debug(sprintf('Could not render reports.partials.budget-period: %s', $e->getMessage())); Log::error(sprintf('Could not render reports.partials.budget-period: %s', $e->getMessage()));
$result = sprintf('Could not render view: %s', $e->getMessage()); $result = sprintf('Could not render view: %s', $e->getMessage());
throw new FireflyException($result, 0, $e); throw new FireflyException($result, 0, $e);
} }

View File

@@ -115,7 +115,7 @@ class DoubleController extends Controller
try { try {
$result = view('reports.double.partials.avg-expenses', compact('result'))->render(); $result = view('reports.double.partials.avg-expenses', compact('result'))->render();
} catch (Throwable $e) { } catch (Throwable $e) {
Log::debug(sprintf('Could not render reports.partials.budget-period: %s', $e->getMessage())); Log::error(sprintf('Could not render reports.partials.budget-period: %s', $e->getMessage()));
$result = sprintf('Could not render view: %s', $e->getMessage()); $result = sprintf('Could not render view: %s', $e->getMessage());
throw new FireflyException($e->getMessage(), 0, $e); throw new FireflyException($e->getMessage(), 0, $e);
} }
@@ -168,7 +168,7 @@ class DoubleController extends Controller
try { try {
$result = view('reports.double.partials.avg-income', compact('result'))->render(); $result = view('reports.double.partials.avg-income', compact('result'))->render();
} catch (Throwable $e) { } catch (Throwable $e) {
Log::debug(sprintf('Could not render reports.partials.budget-period: %s', $e->getMessage())); Log::error(sprintf('Could not render reports.partials.budget-period: %s', $e->getMessage()));
$result = sprintf('Could not render view: %s', $e->getMessage()); $result = sprintf('Could not render view: %s', $e->getMessage());
throw new FireflyException($e->getMessage(), 0, $e); throw new FireflyException($e->getMessage(), 0, $e);
} }
@@ -463,7 +463,7 @@ class DoubleController extends Controller
try { try {
$result = view('reports.double.partials.top-expenses', compact('result'))->render(); $result = view('reports.double.partials.top-expenses', compact('result'))->render();
} catch (Throwable $e) { } catch (Throwable $e) {
Log::debug(sprintf('Could not render reports.partials.budget-period: %s', $e->getMessage())); Log::error(sprintf('Could not render reports.partials.budget-period: %s', $e->getMessage()));
$result = sprintf('Could not render view: %s', $e->getMessage()); $result = sprintf('Could not render view: %s', $e->getMessage());
throw new FireflyException($e->getMessage(), 0, $e); throw new FireflyException($e->getMessage(), 0, $e);
} }
@@ -514,7 +514,7 @@ class DoubleController extends Controller
try { try {
$result = view('reports.double.partials.top-income', compact('result'))->render(); $result = view('reports.double.partials.top-income', compact('result'))->render();
} catch (Throwable $e) { } catch (Throwable $e) {
Log::debug(sprintf('Could not render reports.partials.budget-period: %s', $e->getMessage())); Log::error(sprintf('Could not render reports.partials.budget-period: %s', $e->getMessage()));
$result = sprintf('Could not render view: %s', $e->getMessage()); $result = sprintf('Could not render view: %s', $e->getMessage());
throw new FireflyException($e->getMessage(), 0, $e); throw new FireflyException($e->getMessage(), 0, $e);
} }

View File

@@ -85,7 +85,8 @@ class OperationsController extends Controller
try { try {
$result = view('reports.partials.income-expenses', compact('report', 'type'))->render(); $result = view('reports.partials.income-expenses', compact('report', 'type'))->render();
} catch (Throwable $e) { } catch (Throwable $e) {
Log::debug(sprintf('Could not render reports.partials.income-expense: %s', $e->getMessage())); Log::error(sprintf('Could not render reports.partials.income-expense: %s', $e->getMessage()));
Log::error($e->getTraceAsString());
$result = 'Could not render view.'; $result = 'Could not render view.';
throw new FireflyException($result, 0, $e); throw new FireflyException($result, 0, $e);
} }
@@ -121,7 +122,8 @@ class OperationsController extends Controller
try { try {
$result = view('reports.partials.income-expenses', compact('report', 'type'))->render(); $result = view('reports.partials.income-expenses', compact('report', 'type'))->render();
} catch (Throwable $e) { } catch (Throwable $e) {
Log::debug(sprintf('Could not render reports.partials.income-expenses: %s', $e->getMessage())); Log::error(sprintf('Could not render reports.partials.income-expenses: %s', $e->getMessage()));
Log::error($e->getTraceAsString());
$result = 'Could not render view.'; $result = 'Could not render view.';
throw new FireflyException($result, 0, $e); throw new FireflyException($result, 0, $e);
} }
@@ -177,7 +179,8 @@ class OperationsController extends Controller
try { try {
$result = view('reports.partials.operations', compact('sums'))->render(); $result = view('reports.partials.operations', compact('sums'))->render();
} catch (Throwable $e) { } catch (Throwable $e) {
Log::debug(sprintf('Could not render reports.partials.operations: %s', $e->getMessage())); Log::error(sprintf('Could not render reports.partials.operations: %s', $e->getMessage()));
Log::error($e->getTraceAsString());
$result = 'Could not render view.'; $result = 'Could not render view.';
throw new FireflyException($result, 0, $e); throw new FireflyException($result, 0, $e);
} }

View File

@@ -128,6 +128,7 @@ class SearchController extends Controller
$html = view('search.search', compact('groups', 'hasPages', 'searchTime'))->render(); $html = view('search.search', compact('groups', 'hasPages', 'searchTime'))->render();
} catch (Throwable $e) { } catch (Throwable $e) {
Log::error(sprintf('Cannot render search.search: %s', $e->getMessage())); Log::error(sprintf('Cannot render search.search: %s', $e->getMessage()));
Log::error($e->getTraceAsString());
$html = 'Could not render view.'; $html = 'Could not render view.';
throw new FireflyException($html, 0, $e); throw new FireflyException($html, 0, $e);
} }

View File

@@ -158,6 +158,7 @@ class ReportFormRequest extends FormRequest
} catch (Exception $e) { // intentional generic exception } catch (Exception $e) { // intentional generic exception
$error = sprintf('"%s" is not a valid date range: %s', $range, $e->getMessage()); $error = sprintf('"%s" is not a valid date range: %s', $range, $e->getMessage());
Log::error($error); Log::error($error);
Log::error($e->getTraceAsString());
throw new FireflyException($error, 0, $e); throw new FireflyException($error, 0, $e);
} }
return $date; return $date;
@@ -192,6 +193,7 @@ class ReportFormRequest extends FormRequest
} catch (Exception $e) { // intentional generic exception } catch (Exception $e) { // intentional generic exception
$error = sprintf('"%s" is not a valid date range: %s', $range, $e->getMessage()); $error = sprintf('"%s" is not a valid date range: %s', $range, $e->getMessage());
Log::error($error); Log::error($error);
Log::error($e->getTraceAsString());
throw new FireflyException($error, 0, $e); throw new FireflyException($error, 0, $e);
} }
return $date; return $date;
@@ -220,8 +222,8 @@ class ReportFormRequest extends FormRequest
} }
if (!is_array($set)) { if (!is_array($set)) {
Log::error(sprintf('Set is not an array! "%s"', $set)); Log::error(sprintf('Set is not an array! "%s"', $set));
return $collection;
} }
if (is_array($set)) {
foreach ($set as $tagTag) { foreach ($set as $tagTag) {
Log::debug(sprintf('Now searching for "%s"', $tagTag)); Log::debug(sprintf('Now searching for "%s"', $tagTag));
$tag = $repository->findByTag($tagTag); $tag = $repository->findByTag($tagTag);
@@ -234,7 +236,6 @@ class ReportFormRequest extends FormRequest
$collection->push($tag); $collection->push($tag);
} }
} }
}
return $collection; return $collection;
} }

View File

@@ -24,6 +24,7 @@ declare(strict_types=1);
namespace FireflyIII\Notifications\Admin; namespace FireflyIII\Notifications\Admin;
use FireflyIII\Support\Notifications\UrlValidator;
use FireflyIII\User; use FireflyIII\User;
use Illuminate\Bus\Queueable; use Illuminate\Bus\Queueable;
use Illuminate\Notifications\Messages\MailMessage; use Illuminate\Notifications\Messages\MailMessage;
@@ -101,7 +102,7 @@ class TestNotification extends Notification
/** @var User|null $user */ /** @var User|null $user */
$user = auth()->user(); $user = auth()->user();
$slackUrl = null === $user ? '' : (string)app('preferences')->getForUser(auth()->user(), 'slack_webhook_url', '')->data; $slackUrl = null === $user ? '' : (string)app('preferences')->getForUser(auth()->user(), 'slack_webhook_url', '')->data;
if (str_starts_with($slackUrl, 'https://hooks.slack.com/services/')) { if (UrlValidator::isValidWebhookURL($slackUrl)) {
return ['mail', 'slack']; return ['mail', 'slack'];
} }
return ['mail']; return ['mail'];

View File

@@ -25,6 +25,7 @@ declare(strict_types=1);
namespace FireflyIII\Notifications\Admin; namespace FireflyIII\Notifications\Admin;
use FireflyIII\Models\InvitedUser; use FireflyIII\Models\InvitedUser;
use FireflyIII\Support\Notifications\UrlValidator;
use FireflyIII\User; use FireflyIII\User;
use Illuminate\Bus\Queueable; use Illuminate\Bus\Queueable;
use Illuminate\Notifications\Messages\MailMessage; use Illuminate\Notifications\Messages\MailMessage;
@@ -104,7 +105,7 @@ class UserInvitation extends Notification
/** @var User|null $user */ /** @var User|null $user */
$user = auth()->user(); $user = auth()->user();
$slackUrl = null === $user ? '' : (string)app('preferences')->getForUser(auth()->user(), 'slack_webhook_url', '')->data; $slackUrl = null === $user ? '' : (string)app('preferences')->getForUser(auth()->user(), 'slack_webhook_url', '')->data;
if (str_starts_with($slackUrl, 'https://hooks.slack.com/services/')) { if (UrlValidator::isValidWebhookURL($slackUrl)) {
return ['mail', 'slack']; return ['mail', 'slack'];
} }
return ['mail']; return ['mail'];

View File

@@ -24,6 +24,7 @@ declare(strict_types=1);
namespace FireflyIII\Notifications\Admin; namespace FireflyIII\Notifications\Admin;
use FireflyIII\Support\Notifications\UrlValidator;
use FireflyIII\User; use FireflyIII\User;
use Illuminate\Bus\Queueable; use Illuminate\Bus\Queueable;
use Illuminate\Notifications\Messages\MailMessage; use Illuminate\Notifications\Messages\MailMessage;
@@ -101,7 +102,7 @@ class UserRegistration extends Notification
/** @var User|null $user */ /** @var User|null $user */
$user = auth()->user(); $user = auth()->user();
$slackUrl = null === $user ? '' : (string)app('preferences')->getForUser(auth()->user(), 'slack_webhook_url', '')->data; $slackUrl = null === $user ? '' : (string)app('preferences')->getForUser(auth()->user(), 'slack_webhook_url', '')->data;
if (str_starts_with($slackUrl, 'https://hooks.slack.com/services/')) { if (UrlValidator::isValidWebhookURL($slackUrl)) {
return ['mail', 'slack']; return ['mail', 'slack'];
} }
return ['mail']; return ['mail'];

View File

@@ -24,6 +24,7 @@ declare(strict_types=1);
namespace FireflyIII\Notifications\Admin; namespace FireflyIII\Notifications\Admin;
use FireflyIII\Support\Notifications\UrlValidator;
use FireflyIII\User; use FireflyIII\User;
use Illuminate\Bus\Queueable; use Illuminate\Bus\Queueable;
use Illuminate\Notifications\Messages\MailMessage; use Illuminate\Notifications\Messages\MailMessage;
@@ -87,17 +88,6 @@ class VersionCheckResult extends Notification
*/ */
public function toSlack($notifiable) public function toSlack($notifiable)
{ {
// return (new SlackMessage())->text($this->message)
// ->sectionBlock(function (SectionBlock $block) {
// $button = new ButtonElement('Button');
// $button->url('https://github.com/firefly-iii/firefly-iii/releases');
// $block->accessory($button);
// });
//// ->attachment(function ($attachment) {
//// $attachment->title('Firefly III @ GitHub', 'https://github.com/firefly-iii/firefly-iii/releases');
//// });
return (new SlackMessage())->content($this->message) return (new SlackMessage())->content($this->message)
->attachment(function ($attachment) { ->attachment(function ($attachment) {
$attachment->title('Firefly III @ GitHub', 'https://github.com/firefly-iii/firefly-iii/releases'); $attachment->title('Firefly III @ GitHub', 'https://github.com/firefly-iii/firefly-iii/releases');
@@ -116,7 +106,7 @@ class VersionCheckResult extends Notification
/** @var User|null $user */ /** @var User|null $user */
$user = auth()->user(); $user = auth()->user();
$slackUrl = null === $user ? '' : (string)app('preferences')->getForUser(auth()->user(), 'slack_webhook_url', '')->data; $slackUrl = null === $user ? '' : (string)app('preferences')->getForUser(auth()->user(), 'slack_webhook_url', '')->data;
if (str_starts_with($slackUrl, 'https://hooks.slack.com/services/')) { if (UrlValidator::isValidWebhookURL($slackUrl)) {
return ['mail', 'slack']; return ['mail', 'slack'];
} }
return ['mail']; return ['mail'];

View File

@@ -25,6 +25,7 @@ declare(strict_types=1);
namespace FireflyIII\Notifications\User; namespace FireflyIII\Notifications\User;
use FireflyIII\Models\Bill; use FireflyIII\Models\Bill;
use FireflyIII\Support\Notifications\UrlValidator;
use FireflyIII\User; use FireflyIII\User;
use Illuminate\Bus\Queueable; use Illuminate\Bus\Queueable;
use Illuminate\Notifications\Messages\MailMessage; use Illuminate\Notifications\Messages\MailMessage;
@@ -122,7 +123,7 @@ class BillReminder extends Notification
/** @var User|null $user */ /** @var User|null $user */
$user = auth()->user(); $user = auth()->user();
$slackUrl = null === $user ? '' : (string)app('preferences')->getForUser(auth()->user(), 'slack_webhook_url', '')->data; $slackUrl = null === $user ? '' : (string)app('preferences')->getForUser(auth()->user(), 'slack_webhook_url', '')->data;
if (str_starts_with($slackUrl, 'https://hooks.slack.com/services/')) { if (UrlValidator::isValidWebhookURL($slackUrl)) {
return ['mail', 'slack']; return ['mail', 'slack'];
} }
return ['mail']; return ['mail'];

View File

@@ -24,6 +24,7 @@ declare(strict_types=1);
namespace FireflyIII\Notifications\User; namespace FireflyIII\Notifications\User;
use FireflyIII\Support\Notifications\UrlValidator;
use FireflyIII\User; use FireflyIII\User;
use Illuminate\Bus\Queueable; use Illuminate\Bus\Queueable;
use Illuminate\Notifications\Messages\MailMessage; use Illuminate\Notifications\Messages\MailMessage;
@@ -98,7 +99,7 @@ class NewAccessToken extends Notification
/** @var User|null $user */ /** @var User|null $user */
$user = auth()->user(); $user = auth()->user();
$slackUrl = null === $user ? '' : (string)app('preferences')->getForUser(auth()->user(), 'slack_webhook_url', '')->data; $slackUrl = null === $user ? '' : (string)app('preferences')->getForUser(auth()->user(), 'slack_webhook_url', '')->data;
if (str_starts_with($slackUrl, 'https://hooks.slack.com/services/')) { if (UrlValidator::isValidWebhookURL($slackUrl)) {
return ['mail', 'slack']; return ['mail', 'slack'];
} }
return ['mail']; return ['mail'];

View File

@@ -0,0 +1,117 @@
<?php
/*
* NewAccessToken.php
* Copyright (c) 2022 james@firefly-iii.org
*
* This file is part of Firefly III (https://github.com/firefly-iii).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace FireflyIII\Notifications\User;
use FireflyIII\Support\Notifications\UrlValidator;
use FireflyIII\User;
use Illuminate\Bus\Queueable;
use Illuminate\Notifications\Messages\SlackMessage;
use Illuminate\Notifications\Notification;
/**
* Class RuleActionFailed
*/
class RuleActionFailed extends Notification
{
use Queueable;
private string $groupLink;
private string $groupTitle;
private string $message;
private string $ruleLink;
private string $ruleTitle;
/**
* Create a new notification instance.
*
* @return void
*/
public function __construct(array $params)
{
[$mainMessage, $groupTitle, $groupLink, $ruleTitle, $ruleLink] = $params;
$this->message = $mainMessage;
$this->groupTitle = $groupTitle;
$this->groupLink = $groupLink;
$this->ruleTitle = $ruleTitle;
$this->ruleLink = $ruleLink;
}
/**
* Get the array representation of the notification.
*
* @param mixed $notifiable
*
* @return array
*/
public function toArray($notifiable)
{
return [
//
];
}
/**
* Get the Slack representation of the notification.
*
* @param mixed $notifiable
*
* @return SlackMessage
*/
public function toSlack($notifiable)
{
$groupTitle = $this->groupTitle;
$groupLink = $this->groupLink;
$ruleTitle = $this->ruleTitle;
$ruleLink = $this->ruleLink;
return (new SlackMessage())->content($this->message)->attachment(function ($attachment) use ($groupTitle, $groupLink) {
$attachment->title((string)trans('rules.inspect_transaction', ['title' => $groupTitle]), $groupLink);
})->attachment(function ($attachment) use ($ruleTitle, $ruleLink) {
$attachment->title((string)trans('rules.inspect_rule', ['title' => $ruleTitle]), $ruleLink);
});
}
/**
* Get the notification's delivery channels.
*
* @param mixed $notifiable
*
* @return array
*/
public function via($notifiable)
{
/** @var User|null $user */
$user = auth()->user();
$slackUrl = null === $user ? '' : (string)app('preferences')->getForUser(auth()->user(), 'slack_webhook_url', '')->data;
if (UrlValidator::isValidWebhookURL($slackUrl)) {
app('log')->debug('Will send ruleActionFailed through Slack!');
return ['slack'];
}
app('log')->debug('Will NOT send ruleActionFailed through Slack');
return [];
}
}

View File

@@ -25,6 +25,7 @@ declare(strict_types=1);
namespace FireflyIII\Notifications\User; namespace FireflyIII\Notifications\User;
use FireflyIII\Exceptions\FireflyException; use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Support\Notifications\UrlValidator;
use FireflyIII\User; use FireflyIII\User;
use Illuminate\Bus\Queueable; use Illuminate\Bus\Queueable;
use Illuminate\Notifications\Messages\MailMessage; use Illuminate\Notifications\Messages\MailMessage;
@@ -126,7 +127,7 @@ class UserLogin extends Notification
/** @var User|null $user */ /** @var User|null $user */
$user = auth()->user(); $user = auth()->user();
$slackUrl = null === $user ? '' : (string)app('preferences')->getForUser(auth()->user(), 'slack_webhook_url', '')->data; $slackUrl = null === $user ? '' : (string)app('preferences')->getForUser(auth()->user(), 'slack_webhook_url', '')->data;
if (str_starts_with($slackUrl, 'https://hooks.slack.com/services/')) { if (UrlValidator::isValidWebhookURL($slackUrl)) {
return ['mail', 'slack']; return ['mail', 'slack'];
} }
return ['mail']; return ['mail'];

View File

@@ -32,6 +32,8 @@ use FireflyIII\Events\DetectedNewIPAddress;
use FireflyIII\Events\Model\BudgetLimit\Created; use FireflyIII\Events\Model\BudgetLimit\Created;
use FireflyIII\Events\Model\BudgetLimit\Deleted; use FireflyIII\Events\Model\BudgetLimit\Deleted;
use FireflyIII\Events\Model\BudgetLimit\Updated; use FireflyIII\Events\Model\BudgetLimit\Updated;
use FireflyIII\Events\Model\Rule\RuleActionFailedOnArray;
use FireflyIII\Events\Model\Rule\RuleActionFailedOnObject;
use FireflyIII\Events\NewVersionAvailable; use FireflyIII\Events\NewVersionAvailable;
use FireflyIII\Events\RegisteredUser; use FireflyIII\Events\RegisteredUser;
use FireflyIII\Events\RequestedNewPassword; use FireflyIII\Events\RequestedNewPassword;
@@ -170,6 +172,14 @@ class EventServiceProvider extends ServiceProvider
'FireflyIII\Handlers\Events\Model\BudgetLimitHandler@deleted', 'FireflyIII\Handlers\Events\Model\BudgetLimitHandler@deleted',
], ],
// rule actions
RuleActionFailedOnArray::class => [
'FireflyIII\Handlers\Events\Model\RuleHandler@ruleActionFailedOnArray',
],
RuleActionFailedOnObject::class => [
'FireflyIII\Handlers\Events\Model\RuleHandler@ruleActionFailedOnObject',
],
]; ];
/** /**

View File

@@ -39,6 +39,49 @@ class AvailableBudgetRepository implements AvailableBudgetRepositoryInterface
{ {
private User $user; private User $user;
/**
* @inheritDoc
*/
public function cleanup(): void
{
$exists = [];
$availableBudgets = $this->user->availableBudgets()->get();
/** @var AvailableBudget $availableBudget */
foreach ($availableBudgets as $availableBudget) {
$start = $availableBudget->start_date->format('Y-m-d');
$end = $availableBudget->end_date->format('Y-m-d');
$key = sprintf('%s-%s-%s', $availableBudget->transaction_currency_id, $start, $end);
if (array_key_exists($key, $exists)) {
app('log')->debug(sprintf('Found duplicate AB: %s %s, %s-%s. Has been deleted', $availableBudget->transaction_currency_id, $availableBudget->amount, $start, $end));
$availableBudget->delete();
}
$exists[$key] = true;
}
}
/**
* Return a list of all available budgets (in all currencies) (for the selected period).
*
* @param Carbon|null $start
* @param Carbon|null $end
*
* @return Collection
*/
public function get(?Carbon $start = null, ?Carbon $end = null): Collection
{
$query = $this->user->availableBudgets()->with(['transactionCurrency']);
if (null !== $start && null !== $end) {
$query->where(
static function (Builder $q1) use ($start, $end) {
$q1->where('start_date', '=', $start->format('Y-m-d'));
$q1->where('end_date', '=', $end->format('Y-m-d'));
}
);
}
return $query->get(['available_budgets.*']);
}
/** /**
* Delete all available budgets. * Delete all available budgets.
*/ */
@@ -122,29 +165,6 @@ class AvailableBudgetRepository implements AvailableBudgetRepositoryInterface
return $return; return $return;
} }
/**
* Return a list of all available budgets (in all currencies) (for the selected period).
*
* @param Carbon|null $start
* @param Carbon|null $end
*
* @return Collection
*/
public function get(?Carbon $start = null, ?Carbon $end = null): Collection
{
$query = $this->user->availableBudgets()->with(['transactionCurrency']);
if (null !== $start && null !== $end) {
$query->where(
static function (Builder $q1) use ($start, $end) {
$q1->where('start_date', '=', $start->format('Y-m-d'));
$q1->where('end_date', '=', $end->format('Y-m-d'));
}
);
}
return $query->get(['available_budgets.*']);
}
/** /**
* Returns all available budget objects. * Returns all available budget objects.
* *
@@ -180,6 +200,23 @@ class AvailableBudgetRepository implements AvailableBudgetRepositoryInterface
return $query->get(); return $query->get();
} }
/**
* Returns all available budget objects.
*
* @param Carbon $start
* @param Carbon $end
*
* @return Collection
*
*/
public function getAvailableBudgetsByExactDate(Carbon $start, Carbon $end): Collection
{
return $this->user->availableBudgets()
->where('start_date', '=', $start->format('Y-m-d'))
->where('end_date', '=', $end->format('Y-m-d'))
->get();
}
/** /**
* @inheritDoc * @inheritDoc
*/ */

View File

@@ -35,6 +35,11 @@ use Illuminate\Support\Collection;
*/ */
interface AvailableBudgetRepositoryInterface interface AvailableBudgetRepositoryInterface
{ {
/**
* @return void
*/
public function cleanup(): void;
/** /**
* Delete all available budgets. * Delete all available budgets.
*/ */
@@ -111,6 +116,14 @@ interface AvailableBudgetRepositoryInterface
*/ */
public function getAvailableBudgetsByDate(?Carbon $start, ?Carbon $end): Collection; public function getAvailableBudgetsByDate(?Carbon $start, ?Carbon $end): Collection;
/**
* @param Carbon $start
* @param Carbon $end
*
* @return Collection
*/
public function getAvailableBudgetsByExactDate(Carbon $start, Carbon $end): Collection;
/** /**
* Get by transaction currency and date. Should always result in one entry or NULL. * Get by transaction currency and date. Should always result in one entry or NULL.
* *

View File

@@ -294,7 +294,7 @@ trait RenderPartialViews
} catch (Throwable $e) { } catch (Throwable $e) {
Log::debug(sprintf('Throwable was thrown in getCurrentActions(): %s', $e->getMessage())); Log::debug(sprintf('Throwable was thrown in getCurrentActions(): %s', $e->getMessage()));
Log::error($e->getTraceAsString()); Log::error($e->getTraceAsString());
throw new FireflyException('Could not render', 0, $e); throw new FireflyException(sprintf('Could not render: %s', $e->getMessage()), 0, $e);
} }
++$index; ++$index;
@@ -349,7 +349,7 @@ trait RenderPartialViews
} catch (Throwable $e) { } catch (Throwable $e) {
Log::debug(sprintf('Throwable was thrown in getCurrentTriggers(): %s', $e->getMessage())); Log::debug(sprintf('Throwable was thrown in getCurrentTriggers(): %s', $e->getMessage()));
Log::error($e->getTraceAsString()); Log::error($e->getTraceAsString());
throw new FireflyException('Could not render', 0, $e); throw new FireflyException(sprintf('Could not render: %s', $e->getMessage()), 0, $e);
} }
++$index; ++$index;

View File

@@ -62,7 +62,7 @@ trait RuleManagement
} catch (Throwable $e) { } catch (Throwable $e) {
Log::error(sprintf('Throwable was thrown in getPreviousActions(): %s', $e->getMessage())); Log::error(sprintf('Throwable was thrown in getPreviousActions(): %s', $e->getMessage()));
Log::error($e->getTraceAsString()); Log::error($e->getTraceAsString());
throw new FireflyException('Could not render', 0, $e); throw new FireflyException(sprintf('Could not render: %s', $e->getMessage()), 0, $e);
} }
$index++; $index++;
} }
@@ -109,7 +109,7 @@ trait RuleManagement
} catch (Throwable $e) { } catch (Throwable $e) {
Log::debug(sprintf('Throwable was thrown in getPreviousTriggers(): %s', $e->getMessage())); Log::debug(sprintf('Throwable was thrown in getPreviousTriggers(): %s', $e->getMessage()));
Log::error($e->getTraceAsString()); Log::error($e->getTraceAsString());
throw new FireflyException('Could not render', 0, $e); throw new FireflyException(sprintf('Could not render: %s', $e->getMessage()), 0, $e);
} }
$index++; $index++;
} }
@@ -153,7 +153,7 @@ trait RuleManagement
} catch (Throwable $e) { } catch (Throwable $e) {
Log::debug(sprintf('Throwable was thrown in getPreviousTriggers(): %s', $e->getMessage())); Log::debug(sprintf('Throwable was thrown in getPreviousTriggers(): %s', $e->getMessage()));
Log::error($e->getTraceAsString()); Log::error($e->getTraceAsString());
throw new FireflyException('Could not render', 0, $e); throw new FireflyException(sprintf('Could not render: %s', $e->getMessage()), 0, $e);
} }
$index++; $index++;
} }

View File

@@ -0,0 +1,40 @@
<?php
declare(strict_types=1);
/*
* UrlValidator.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\Support\Notifications;
/**
* Class UrlValidator
*/
class UrlValidator
{
/**
* @param string $url
*
* @return bool
*/
public static function isValidWebhookURL(string $url): bool
{
return str_starts_with($url, 'https://hooks.slack.com/services/') || str_starts_with($url, 'https://discord.com/api/webhooks/');
}
}

View File

@@ -1004,151 +1004,151 @@ class OperatorQuerySearch implements SearchInterface
// //
case '-date_on': case '-date_on':
case 'date_on': case 'date_on':
$range = $this->parseDateRange($value); $range = $this->parseDateRange($operator, $value);
$this->setExactDateParams($range, $prohibited); $this->setExactDateParams($range, $prohibited);
return false; return false;
case 'date_before': case 'date_before':
case '-date_after': case '-date_after':
$range = $this->parseDateRange($value); $range = $this->parseDateRange($operator, $value);
$this->setDateBeforeParams($range); $this->setDateBeforeParams($range);
return false; return false;
case 'date_after': case 'date_after':
case '-date_before': case '-date_before':
$range = $this->parseDateRange($value); $range = $this->parseDateRange($operator, $value);
$this->setDateAfterParams($range); $this->setDateAfterParams($range);
return false; return false;
case 'interest_date_on': case 'interest_date_on':
case '-interest_date_on': case '-interest_date_on':
$range = $this->parseDateRange($value); $range = $this->parseDateRange($operator, $value);
$this->setExactMetaDateParams('interest_date', $range, $prohibited); $this->setExactMetaDateParams('interest_date', $range, $prohibited);
return false; return false;
case 'interest_date_before': case 'interest_date_before':
case '-interest_date_after': case '-interest_date_after':
$range = $this->parseDateRange($value); $range = $this->parseDateRange($operator, $value);
$this->setMetaDateBeforeParams('interest_date', $range); $this->setMetaDateBeforeParams('interest_date', $range);
return false; return false;
case 'interest_date_after': case 'interest_date_after':
case '-interest_date_before': case '-interest_date_before':
$range = $this->parseDateRange($value); $range = $this->parseDateRange($operator, $value);
$this->setMetaDateAfterParams('interest_date', $range); $this->setMetaDateAfterParams('interest_date', $range);
return false; return false;
case 'book_date_on': case 'book_date_on':
case '-book_date_on': case '-book_date_on':
$range = $this->parseDateRange($value); $range = $this->parseDateRange($operator, $value);
$this->setExactMetaDateParams('book_date', $range, $prohibited); $this->setExactMetaDateParams('book_date', $range, $prohibited);
return false; return false;
case 'book_date_before': case 'book_date_before':
case '-book_date_after': case '-book_date_after':
$range = $this->parseDateRange($value); $range = $this->parseDateRange($operator, $value);
$this->setMetaDateBeforeParams('book_date', $range); $this->setMetaDateBeforeParams('book_date', $range);
return false; return false;
case 'book_date_after': case 'book_date_after':
case '-book_date_before': case '-book_date_before':
$range = $this->parseDateRange($value); $range = $this->parseDateRange($operator, $value);
$this->setMetaDateAfterParams('book_date', $range); $this->setMetaDateAfterParams('book_date', $range);
return false; return false;
case 'process_date_on': case 'process_date_on':
case '-process_date_on': case '-process_date_on':
$range = $this->parseDateRange($value); $range = $this->parseDateRange($operator, $value);
$this->setExactMetaDateParams('process_date', $range, $prohibited); $this->setExactMetaDateParams('process_date', $range, $prohibited);
return false; return false;
case 'process_date_before': case 'process_date_before':
case '-process_date_after': case '-process_date_after':
$range = $this->parseDateRange($value); $range = $this->parseDateRange($operator, $value);
$this->setMetaDateBeforeParams('process_date', $range); $this->setMetaDateBeforeParams('process_date', $range);
return false; return false;
case 'process_date_after': case 'process_date_after':
case '-process_date_before': case '-process_date_before':
$range = $this->parseDateRange($value); $range = $this->parseDateRange($operator, $value);
$this->setMetaDateAfterParams('process_date', $range); $this->setMetaDateAfterParams('process_date', $range);
return false; return false;
case 'due_date_on': case 'due_date_on':
case '-due_date_on': case '-due_date_on':
$range = $this->parseDateRange($value); $range = $this->parseDateRange($operator, $value);
$this->setExactMetaDateParams('due_date', $range, $prohibited); $this->setExactMetaDateParams('due_date', $range, $prohibited);
return false; return false;
case 'due_date_before': case 'due_date_before':
case '-due_date_after': case '-due_date_after':
$range = $this->parseDateRange($value); $range = $this->parseDateRange($operator, $value);
$this->setMetaDateBeforeParams('due_date', $range); $this->setMetaDateBeforeParams('due_date', $range);
return false; return false;
case 'due_date_after': case 'due_date_after':
case '-due_date_before': case '-due_date_before':
$range = $this->parseDateRange($value); $range = $this->parseDateRange($operator, $value);
$this->setMetaDateAfterParams('due_date', $range); $this->setMetaDateAfterParams('due_date', $range);
return false; return false;
case 'payment_date_on': case 'payment_date_on':
case '-payment_date_on': case '-payment_date_on':
$range = $this->parseDateRange($value); $range = $this->parseDateRange($operator, $value);
$this->setExactMetaDateParams('payment_date', $range, $prohibited); $this->setExactMetaDateParams('payment_date', $range, $prohibited);
return false; return false;
case 'payment_date_before': case 'payment_date_before':
case '-payment_date_after': case '-payment_date_after':
$range = $this->parseDateRange($value); $range = $this->parseDateRange($operator, $value);
$this->setMetaDateBeforeParams('payment_date', $range); $this->setMetaDateBeforeParams('payment_date', $range);
return false; return false;
case 'payment_date_after': case 'payment_date_after':
case '-payment_date_before': case '-payment_date_before':
$range = $this->parseDateRange($value); $range = $this->parseDateRange($operator, $value);
$this->setMetaDateAfterParams('payment_date', $range); $this->setMetaDateAfterParams('payment_date', $range);
return false; return false;
case 'invoice_date_on': case 'invoice_date_on':
case '-invoice_date_on': case '-invoice_date_on':
$range = $this->parseDateRange($value); $range = $this->parseDateRange($operator, $value);
$this->setExactMetaDateParams('invoice_date', $range, $prohibited); $this->setExactMetaDateParams('invoice_date', $range, $prohibited);
return false; return false;
case 'invoice_date_before': case 'invoice_date_before':
case '-invoice_date_after': case '-invoice_date_after':
$range = $this->parseDateRange($value); $range = $this->parseDateRange($operator, $value);
$this->setMetaDateBeforeParams('invoice_date', $range); $this->setMetaDateBeforeParams('invoice_date', $range);
return false; return false;
case 'invoice_date_after': case 'invoice_date_after':
case '-invoice_date_before': case '-invoice_date_before':
$range = $this->parseDateRange($value); $range = $this->parseDateRange($operator, $value);
$this->setMetaDateAfterParams('invoice_date', $range); $this->setMetaDateAfterParams('invoice_date', $range);
return false; return false;
case 'created_at_on': case 'created_at_on':
case '-created_at_on': case '-created_at_on':
Log::debug(sprintf('Set "%s" using collector with value "%s"', $operator, $value)); Log::debug(sprintf('Set "%s" using collector with value "%s"', $operator, $value));
$range = $this->parseDateRange($value); $range = $this->parseDateRange($operator, $value);
$this->setExactObjectDateParams('created_at', $range, $prohibited); $this->setExactObjectDateParams('created_at', $range, $prohibited);
return false; return false;
case 'created_at_before': case 'created_at_before':
case '-created_at_after': case '-created_at_after':
Log::debug(sprintf('Set "%s" using collector with value "%s"', $operator, $value)); Log::debug(sprintf('Set "%s" using collector with value "%s"', $operator, $value));
$range = $this->parseDateRange($value); $range = $this->parseDateRange($operator, $value);
$this->setObjectDateBeforeParams('created_at', $range); $this->setObjectDateBeforeParams('created_at', $range);
return false; return false;
case 'created_at_after': case 'created_at_after':
case '-created_at_before': case '-created_at_before':
Log::debug(sprintf('Set "%s" using collector with value "%s"', $operator, $value)); Log::debug(sprintf('Set "%s" using collector with value "%s"', $operator, $value));
$range = $this->parseDateRange($value); $range = $this->parseDateRange($operator, $value);
$this->setObjectDateAfterParams('created_at', $range); $this->setObjectDateAfterParams('created_at', $range);
return false; return false;
case 'updated_at_on': case 'updated_at_on':
case '-updated_at_on': case '-updated_at_on':
Log::debug(sprintf('Set "%s" using collector with value "%s"', $operator, $value)); Log::debug(sprintf('Set "%s" using collector with value "%s"', $operator, $value));
$range = $this->parseDateRange($value); $range = $this->parseDateRange($operator, $value);
$this->setExactObjectDateParams('updated_at', $range, $prohibited); $this->setExactObjectDateParams('updated_at', $range, $prohibited);
return false; return false;
case 'updated_at_before': case 'updated_at_before':
case '-updated_at_after': case '-updated_at_after':
Log::debug(sprintf('Set "%s" using collector with value "%s"', $operator, $value)); Log::debug(sprintf('Set "%s" using collector with value "%s"', $operator, $value));
$range = $this->parseDateRange($value); $range = $this->parseDateRange($operator, $value);
$this->setObjectDateBeforeParams('updated_at', $range); $this->setObjectDateBeforeParams('updated_at', $range);
return false; return false;
case 'updated_at_after': case 'updated_at_after':
case '-updated_at_before': case '-updated_at_before':
Log::debug(sprintf('Set "%s" using collector with value "%s"', $operator, $value)); Log::debug(sprintf('Set "%s" using collector with value "%s"', $operator, $value));
$range = $this->parseDateRange($value); $range = $this->parseDateRange($operator, $value);
$this->setObjectDateAfterParams('updated_at', $range); $this->setObjectDateAfterParams('updated_at', $range);
return false; return false;
// //
@@ -1550,13 +1550,22 @@ class OperatorQuerySearch implements SearchInterface
* @return array * @return array
* @throws FireflyException * @throws FireflyException
*/ */
private function parseDateRange(string $value): array private function parseDateRange(string $type, string $value): array
{ {
$parser = new ParseDateString(); $parser = new ParseDateString();
if ($parser->isDateRange($value)) { if ($parser->isDateRange($value)) {
return $parser->parseRange($value); return $parser->parseRange($value);
} }
try {
$parsedDate = $parser->parseDate($value); $parsedDate = $parser->parseDate($value);
} catch (FireflyException $e) {
Log::debug(sprintf('Could not parse date "%s", will return empty array.', $value));
$this->invalidOperators[] = [
'type' => $type,
'value' => (string)$value,
];
return [];
}
return [ return [
'exact' => $parsedDate, 'exact' => $parsedDate,

View File

@@ -24,6 +24,7 @@ declare(strict_types=1);
namespace FireflyIII\TransactionRules\Actions; namespace FireflyIII\TransactionRules\Actions;
use DB; use DB;
use FireflyIII\Events\Model\Rule\RuleActionFailedOnArray;
use FireflyIII\Events\TriggeredAuditLog; use FireflyIII\Events\TriggeredAuditLog;
use FireflyIII\Factory\TagFactory; use FireflyIII\Factory\TagFactory;
use FireflyIII\Models\RuleAction; use FireflyIII\Models\RuleAction;
@@ -61,7 +62,7 @@ class AddTag implements ActionInterface
if (null === $tag) { if (null === $tag) {
// could not find, could not create tag. // could not find, could not create tag.
Log::error(sprintf('RuleAction AddTag. Could not find or create tag "%s"', $this->action->action_value)); event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.find_or_create_tag_failed', ['tag' => $this->action->action_value])));
return false; return false;
} }
@@ -78,12 +79,12 @@ class AddTag implements ActionInterface
// event for audit log entry // event for audit log entry
event(new TriggeredAuditLog($this->action->rule, $object, 'add_tag', null, $tag->tag)); event(new TriggeredAuditLog($this->action->rule, $object, 'add_tag', null, $tag->tag));
return true; return true;
} }
Log::debug( Log::debug(
sprintf('RuleAction AddTag fired but tag %d ("%s") was already added to journal %d.', $tag->id, $tag->tag, $journal['transaction_journal_id']) sprintf('RuleAction AddTag fired but tag %d ("%s") was already added to journal %d.', $tag->id, $tag->tag, $journal['transaction_journal_id'])
); );
event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.tag_already_added', ['tag' => $this->action->action_value])));
return false; return false;
} }

View File

@@ -24,6 +24,7 @@ declare(strict_types=1);
namespace FireflyIII\TransactionRules\Actions; namespace FireflyIII\TransactionRules\Actions;
use FireflyIII\Events\Model\Rule\RuleActionFailedOnArray;
use FireflyIII\Events\TriggeredAuditLog; use FireflyIII\Events\TriggeredAuditLog;
use FireflyIII\Models\Note; use FireflyIII\Models\Note;
use FireflyIII\Models\RuleAction; use FireflyIII\Models\RuleAction;
@@ -56,6 +57,7 @@ class AppendDescriptionToNotes implements ActionInterface
$object = TransactionJournal::where('user_id', $journal['user_id'])->find($journal['transaction_journal_id']); $object = TransactionJournal::where('user_id', $journal['user_id'])->find($journal['transaction_journal_id']);
if (null === $object) { if (null === $object) {
Log::error(sprintf('No journal #%d belongs to user #%d.', $journal['transaction_journal_id'], $journal['user_id'])); Log::error(sprintf('No journal #%d belongs to user #%d.', $journal['transaction_journal_id'], $journal['user_id']));
event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.journal_other_user')));
return false; return false;
} }
$note = $object->notes()->first(); $note = $object->notes()->first();

View File

@@ -24,6 +24,7 @@ declare(strict_types=1);
namespace FireflyIII\TransactionRules\Actions; namespace FireflyIII\TransactionRules\Actions;
use FireflyIII\Events\Model\Rule\RuleActionFailedOnArray;
use FireflyIII\Events\TriggeredAuditLog; use FireflyIII\Events\TriggeredAuditLog;
use FireflyIII\Models\Note; use FireflyIII\Models\Note;
use FireflyIII\Models\RuleAction; use FireflyIII\Models\RuleAction;
@@ -60,6 +61,7 @@ class AppendNotesToDescription implements ActionInterface
$object = TransactionJournal::where('user_id', $journal['user_id'])->find($journal['transaction_journal_id']); $object = TransactionJournal::where('user_id', $journal['user_id'])->find($journal['transaction_journal_id']);
if (null === $object) { if (null === $object) {
Log::error(sprintf('No journal #%d belongs to user #%d.', $journal['transaction_journal_id'], $journal['user_id'])); Log::error(sprintf('No journal #%d belongs to user #%d.', $journal['transaction_journal_id'], $journal['user_id']));
event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.journal_other_user')));
return false; return false;
} }
$note = $object->notes()->first(); $note = $object->notes()->first();
@@ -80,6 +82,7 @@ class AppendNotesToDescription implements ActionInterface
return true; return true;
} }
event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.new_notes_empty')));
return false; return false;
} }

View File

@@ -24,6 +24,7 @@ declare(strict_types=1);
namespace FireflyIII\TransactionRules\Actions; namespace FireflyIII\TransactionRules\Actions;
use DB; use DB;
use FireflyIII\Events\Model\Rule\RuleActionFailedOnArray;
use FireflyIII\Events\TriggeredAuditLog; use FireflyIII\Events\TriggeredAuditLog;
use FireflyIII\Models\RuleAction; use FireflyIII\Models\RuleAction;
use FireflyIII\Models\TransactionJournal; use FireflyIII\Models\TransactionJournal;
@@ -56,6 +57,7 @@ class ClearBudget implements ActionInterface
$budget = $object->budgets()->first(); $budget = $object->budgets()->first();
if (null === $budget) { if (null === $budget) {
Log::debug(sprintf('RuleAction ClearBudget, no budget in journal #%d.', $journal['transaction_journal_id'])); Log::debug(sprintf('RuleAction ClearBudget, no budget in journal #%d.', $journal['transaction_journal_id']));
event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.journal_already_no_budget')));
return false; return false;
} }

View File

@@ -24,6 +24,7 @@ declare(strict_types=1);
namespace FireflyIII\TransactionRules\Actions; namespace FireflyIII\TransactionRules\Actions;
use DB; use DB;
use FireflyIII\Events\Model\Rule\RuleActionFailedOnArray;
use FireflyIII\Events\TriggeredAuditLog; use FireflyIII\Events\TriggeredAuditLog;
use FireflyIII\Models\RuleAction; use FireflyIII\Models\RuleAction;
use FireflyIII\Models\TransactionJournal; use FireflyIII\Models\TransactionJournal;
@@ -56,6 +57,7 @@ class ClearCategory implements ActionInterface
$category = $object->categories()->first(); $category = $object->categories()->first();
if (null === $category) { if (null === $category) {
Log::debug(sprintf('RuleAction ClearCategory, no category in journal #%d.', $journal['transaction_journal_id'])); Log::debug(sprintf('RuleAction ClearCategory, no category in journal #%d.', $journal['transaction_journal_id']));
event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.journal_already_no_category')));
return false; return false;
} }

View File

@@ -24,6 +24,7 @@ declare(strict_types=1);
namespace FireflyIII\TransactionRules\Actions; namespace FireflyIII\TransactionRules\Actions;
use DB; use DB;
use FireflyIII\Events\Model\Rule\RuleActionFailedOnArray;
use FireflyIII\Events\TriggeredAuditLog; use FireflyIII\Events\TriggeredAuditLog;
use FireflyIII\Models\RuleAction; use FireflyIII\Models\RuleAction;
use FireflyIII\Models\TransactionJournal; use FireflyIII\Models\TransactionJournal;
@@ -55,6 +56,7 @@ class ClearNotes implements ActionInterface
$notes = $object->notes()->first(); $notes = $object->notes()->first();
if (null === $notes) { if (null === $notes) {
Log::debug(sprintf('RuleAction ClearNotes, journal #%d has no notes.', $journal['transaction_journal_id'])); Log::debug(sprintf('RuleAction ClearNotes, journal #%d has no notes.', $journal['transaction_journal_id']));
event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.journal_already_no_notes')));
return false; return false;
} }
$before = $notes->text; $before = $notes->text;

View File

@@ -24,6 +24,7 @@ declare(strict_types=1);
namespace FireflyIII\TransactionRules\Actions; namespace FireflyIII\TransactionRules\Actions;
use DB; use DB;
use FireflyIII\Events\Model\Rule\RuleActionFailedOnArray;
use FireflyIII\Events\TriggeredAuditLog; use FireflyIII\Events\TriggeredAuditLog;
use FireflyIII\Exceptions\FireflyException; use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Factory\AccountFactory; use FireflyIII\Factory\AccountFactory;
@@ -65,11 +66,13 @@ class ConvertToDeposit implements ActionInterface
$object = TransactionJournal::where('user_id', $journal['user_id'])->find($journal['transaction_journal_id']); $object = TransactionJournal::where('user_id', $journal['user_id'])->find($journal['transaction_journal_id']);
if (null === $object) { if (null === $object) {
Log::error(sprintf('Cannot find journal #%d, cannot convert to deposit.', $journal['transaction_journal_id'])); Log::error(sprintf('Cannot find journal #%d, cannot convert to deposit.', $journal['transaction_journal_id']));
event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.journal_not_found')));
return false; return false;
} }
$groupCount = TransactionJournal::where('transaction_group_id', $journal['transaction_group_id'])->count(); $groupCount = TransactionJournal::where('transaction_group_id', $journal['transaction_group_id'])->count();
if ($groupCount > 1) { if ($groupCount > 1) {
Log::error(sprintf('Group #%d has more than one transaction in it, cannot convert to deposit.', $journal['transaction_group_id'])); Log::error(sprintf('Group #%d has more than one transaction in it, cannot convert to deposit.', $journal['transaction_group_id']));
event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.split_group')));
return false; return false;
} }
@@ -77,7 +80,7 @@ class ConvertToDeposit implements ActionInterface
$type = $object->transactionType->type; $type = $object->transactionType->type;
if (TransactionType::DEPOSIT === $type) { if (TransactionType::DEPOSIT === $type) {
Log::error(sprintf('Journal #%d is already a deposit (rule #%d).', $journal['transaction_journal_id'], $this->action->rule_id)); Log::error(sprintf('Journal #%d is already a deposit (rule #%d).', $journal['transaction_journal_id'], $this->action->rule_id));
event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.is_already_deposit')));
return false; return false;
} }
@@ -89,6 +92,7 @@ class ConvertToDeposit implements ActionInterface
} catch (JsonException | FireflyException $e) { } catch (JsonException | FireflyException $e) {
Log::debug('Could not convert withdrawal to deposit.'); Log::debug('Could not convert withdrawal to deposit.');
Log::error($e->getMessage()); Log::error($e->getMessage());
event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.complex_error')));
return false; return false;
} }
@@ -104,13 +108,14 @@ class ConvertToDeposit implements ActionInterface
} catch (JsonException | FireflyException $e) { } catch (JsonException | FireflyException $e) {
Log::debug('Could not convert transfer to deposit.'); Log::debug('Could not convert transfer to deposit.');
Log::error($e->getMessage()); Log::error($e->getMessage());
event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.complex_error')));
return false; return false;
} }
event(new TriggeredAuditLog($this->action->rule, $object, 'update_transaction_type', TransactionType::TRANSFER, TransactionType::DEPOSIT)); event(new TriggeredAuditLog($this->action->rule, $object, 'update_transaction_type', TransactionType::TRANSFER, TransactionType::DEPOSIT));
return $res; return $res;
} }
event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.unsupported_transaction_type_deposit', ['type' => $type])));
return false; return false;
} }

View File

@@ -24,6 +24,8 @@ declare(strict_types=1);
namespace FireflyIII\TransactionRules\Actions; namespace FireflyIII\TransactionRules\Actions;
use DB; use DB;
use FireflyIII\Events\Model\Rule\RuleActionFailedOnArray;
use FireflyIII\Events\Model\Rule\RuleActionFailedOnObject;
use FireflyIII\Events\TriggeredAuditLog; use FireflyIII\Events\TriggeredAuditLog;
use FireflyIII\Exceptions\FireflyException; use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Models\Account; use FireflyIII\Models\Account;
@@ -62,11 +64,13 @@ class ConvertToTransfer implements ActionInterface
$object = TransactionJournal::where('user_id', $journal['user_id'])->find($journal['transaction_journal_id']); $object = TransactionJournal::where('user_id', $journal['user_id'])->find($journal['transaction_journal_id']);
if (null === $object) { if (null === $object) {
Log::error(sprintf('Cannot find journal #%d, cannot convert to transfer.', $journal['transaction_journal_id'])); Log::error(sprintf('Cannot find journal #%d, cannot convert to transfer.', $journal['transaction_journal_id']));
event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.journal_not_found')));
return false; return false;
} }
$groupCount = TransactionJournal::where('transaction_group_id', $journal['transaction_group_id'])->count(); $groupCount = TransactionJournal::where('transaction_group_id', $journal['transaction_group_id'])->count();
if ($groupCount > 1) { if ($groupCount > 1) {
Log::error(sprintf('Group #%d has more than one transaction in it, cannot convert to transfer.', $journal['transaction_group_id'])); Log::error(sprintf('Group #%d has more than one transaction in it, cannot convert to transfer.', $journal['transaction_group_id']));
event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.split_group')));
return false; return false;
} }
@@ -77,15 +81,19 @@ class ConvertToTransfer implements ActionInterface
Log::error( Log::error(
sprintf('Journal #%d is already a transfer so cannot be converted (rule #%d).', $object->id, $this->action->rule_id) sprintf('Journal #%d is already a transfer so cannot be converted (rule #%d).', $object->id, $this->action->rule_id)
); );
event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.is_already_transfer')));
return false; return false;
} }
if (TransactionType::DEPOSIT !== $type && TransactionType::WITHDRAWAL !== $type) {
event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.unsupported_transaction_type_transfer', ['type' => $type])));
return false;
}
// find the asset account in the action value. // find the asset account in the action value.
/** @var AccountRepositoryInterface $repository */ /** @var AccountRepositoryInterface $repository */
$repository = app(AccountRepositoryInterface::class); $repository = app(AccountRepositoryInterface::class);
$repository->setUser($user); $repository->setUser($user);
$opposing = null;
$expectedType = null; $expectedType = null;
if (TransactionType::WITHDRAWAL === $type) { if (TransactionType::WITHDRAWAL === $type) {
$expectedType = $this->getSourceType($journalId); $expectedType = $this->getSourceType($journalId);
@@ -107,6 +115,7 @@ class ConvertToTransfer implements ActionInterface
$this->action->rule_id $this->action->rule_id
) )
); );
event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.no_valid_opposing', ['name' => $this->action->action_value])));
return false; return false;
} }
@@ -118,9 +127,12 @@ class ConvertToTransfer implements ActionInterface
} catch (FireflyException $e) { } catch (FireflyException $e) {
Log::debug('Could not convert withdrawal to transfer.'); Log::debug('Could not convert withdrawal to transfer.');
Log::error($e->getMessage()); Log::error($e->getMessage());
event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.complex_error')));
return false; return false;
} }
if (false !== $res) {
event(new TriggeredAuditLog($this->action->rule, $object, 'update_transaction_type', TransactionType::WITHDRAWAL, TransactionType::TRANSFER)); event(new TriggeredAuditLog($this->action->rule, $object, 'update_transaction_type', TransactionType::WITHDRAWAL, TransactionType::TRANSFER));
}
return $res; return $res;
} }
if (TransactionType::DEPOSIT === $type) { if (TransactionType::DEPOSIT === $type) {
@@ -130,12 +142,15 @@ class ConvertToTransfer implements ActionInterface
} catch (FireflyException $e) { } catch (FireflyException $e) {
Log::debug('Could not convert deposit to transfer.'); Log::debug('Could not convert deposit to transfer.');
Log::error($e->getMessage()); Log::error($e->getMessage());
event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.complex_error')));
return false; return false;
} }
if (false !== $res) {
event(new TriggeredAuditLog($this->action->rule, $object, 'update_transaction_type', TransactionType::DEPOSIT, TransactionType::TRANSFER)); event(new TriggeredAuditLog($this->action->rule, $object, 'update_transaction_type', TransactionType::DEPOSIT, TransactionType::TRANSFER));
}
return $res; return $res;
} }
event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.unsupported_transaction_type_transfer', ['type' => $type])));
return false; return false;
} }
@@ -192,6 +207,7 @@ class ConvertToTransfer implements ActionInterface
[$journal->id, $opposing->name, $this->action->rule_id] [$journal->id, $opposing->name, $this->action->rule_id]
) )
); );
event(new RuleActionFailedOnObject($this->action, $journal, trans('rules.already_has_source_asset', ['name' => $opposing->name])));
return false; return false;
} }
@@ -250,6 +266,7 @@ class ConvertToTransfer implements ActionInterface
[$journal->id, $opposing->name, $this->action->rule_id] [$journal->id, $opposing->name, $this->action->rule_id]
) )
); );
event(new RuleActionFailedOnObject($this->action, $journal, trans('rules.already_has_destination_asset', ['name' => $opposing->name])));
return false; return false;
} }

View File

@@ -24,6 +24,7 @@ declare(strict_types=1);
namespace FireflyIII\TransactionRules\Actions; namespace FireflyIII\TransactionRules\Actions;
use DB; use DB;
use FireflyIII\Events\Model\Rule\RuleActionFailedOnArray;
use FireflyIII\Events\TriggeredAuditLog; use FireflyIII\Events\TriggeredAuditLog;
use FireflyIII\Exceptions\FireflyException; use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Factory\AccountFactory; use FireflyIII\Factory\AccountFactory;
@@ -65,21 +66,26 @@ class ConvertToWithdrawal implements ActionInterface
$object = TransactionJournal::where('user_id', $journal['user_id'])->find($journal['transaction_journal_id']); $object = TransactionJournal::where('user_id', $journal['user_id'])->find($journal['transaction_journal_id']);
if (null === $object) { if (null === $object) {
Log::error(sprintf('Cannot find journal #%d, cannot convert to withdrawal.', $journal['transaction_journal_id'])); Log::error(sprintf('Cannot find journal #%d, cannot convert to withdrawal.', $journal['transaction_journal_id']));
event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.journal_not_found')));
return false; return false;
} }
$groupCount = TransactionJournal::where('transaction_group_id', $journal['transaction_group_id'])->count(); $groupCount = TransactionJournal::where('transaction_group_id', $journal['transaction_group_id'])->count();
if ($groupCount > 1) { if ($groupCount > 1) {
Log::error(sprintf('Group #%d has more than one transaction in it, cannot convert to withdrawal.', $journal['transaction_group_id'])); Log::error(sprintf('Group #%d has more than one transaction in it, cannot convert to withdrawal.', $journal['transaction_group_id']));
event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.split_group')));
return false; return false;
} }
$type = $object->transactionType->type; $type = $object->transactionType->type;
if (TransactionType::WITHDRAWAL === $type) { if (TransactionType::WITHDRAWAL === $type) {
Log::error(sprintf('Journal #%d is already a withdrawal (rule #%d).', $journal['transaction_journal_id'], $this->action->rule_id)); Log::error(sprintf('Journal #%d is already a withdrawal (rule #%d).', $journal['transaction_journal_id'], $this->action->rule_id));
event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.is_already_withdrawal')));
return false;
}
if (TransactionType::DEPOSIT !== $type && TransactionType::TRANSFER !== $type) {
event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.unsupported_transaction_type_withdrawal', ['type' => $type])));
return false; return false;
} }
if (TransactionType::DEPOSIT === $type) { if (TransactionType::DEPOSIT === $type) {
Log::debug('Going to transform a deposit to a withdrawal.'); Log::debug('Going to transform a deposit to a withdrawal.');
try { try {
@@ -87,6 +93,7 @@ class ConvertToWithdrawal implements ActionInterface
} catch (JsonException | FireflyException $e) { } catch (JsonException | FireflyException $e) {
Log::debug('Could not convert transfer to deposit.'); Log::debug('Could not convert transfer to deposit.');
Log::error($e->getMessage()); Log::error($e->getMessage());
event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.complex_error')));
return false; return false;
} }
event(new TriggeredAuditLog($this->action->rule, $object, 'update_transaction_type', TransactionType::DEPOSIT, TransactionType::WITHDRAWAL)); event(new TriggeredAuditLog($this->action->rule, $object, 'update_transaction_type', TransactionType::DEPOSIT, TransactionType::WITHDRAWAL));
@@ -101,13 +108,14 @@ class ConvertToWithdrawal implements ActionInterface
} catch (JsonException | FireflyException $e) { } catch (JsonException | FireflyException $e) {
Log::debug('Could not convert transfer to deposit.'); Log::debug('Could not convert transfer to deposit.');
Log::error($e->getMessage()); Log::error($e->getMessage());
event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.complex_error')));
return false; return false;
} }
event(new TriggeredAuditLog($this->action->rule, $object, 'update_transaction_type', TransactionType::TRANSFER, TransactionType::WITHDRAWAL)); event(new TriggeredAuditLog($this->action->rule, $object, 'update_transaction_type', TransactionType::TRANSFER, TransactionType::WITHDRAWAL));
return $res; return $res;
} }
event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.unsupported_transaction_type_withdrawal', ['type' => $type])));
return false; return false;
} }

View File

@@ -24,6 +24,7 @@ declare(strict_types=1);
namespace FireflyIII\TransactionRules\Actions; namespace FireflyIII\TransactionRules\Actions;
use DB; use DB;
use FireflyIII\Events\Model\Rule\RuleActionFailedOnArray;
use FireflyIII\Events\TriggeredAuditLog; use FireflyIII\Events\TriggeredAuditLog;
use FireflyIII\Models\RuleAction; use FireflyIII\Models\RuleAction;
use FireflyIII\Models\TransactionJournal; use FireflyIII\Models\TransactionJournal;
@@ -73,6 +74,7 @@ class LinkToBill implements ActionInterface
$billName $billName
) )
); );
event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.already_linked_to_subscription', ['name' => $billName])));
return false; return false;
} }
@@ -97,7 +99,7 @@ class LinkToBill implements ActionInterface
$billName $billName
) )
); );
event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.cannot_find_subscription', ['name' => $billName])));
return false; return false;
} }
} }

View File

@@ -24,6 +24,7 @@ declare(strict_types=1);
namespace FireflyIII\TransactionRules\Actions; namespace FireflyIII\TransactionRules\Actions;
use FireflyIII\Events\Model\Rule\RuleActionFailedOnArray;
use FireflyIII\Events\TriggeredAuditLog; use FireflyIII\Events\TriggeredAuditLog;
use FireflyIII\Models\Note; use FireflyIII\Models\Note;
use FireflyIII\Models\RuleAction; use FireflyIII\Models\RuleAction;
@@ -57,6 +58,7 @@ class MoveDescriptionToNotes implements ActionInterface
$object = TransactionJournal::where('user_id', $journal['user_id'])->find($journal['transaction_journal_id']); $object = TransactionJournal::where('user_id', $journal['user_id'])->find($journal['transaction_journal_id']);
if (null === $object) { if (null === $object) {
Log::error(sprintf('No journal #%d belongs to user #%d.', $journal['transaction_journal_id'], $journal['user_id'])); Log::error(sprintf('No journal #%d belongs to user #%d.', $journal['transaction_journal_id'], $journal['user_id']));
event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.journal_other_user')));
return false; return false;
} }
$note = $object->notes()->first(); $note = $object->notes()->first();

View File

@@ -24,6 +24,7 @@ declare(strict_types=1);
namespace FireflyIII\TransactionRules\Actions; namespace FireflyIII\TransactionRules\Actions;
use FireflyIII\Events\Model\Rule\RuleActionFailedOnArray;
use FireflyIII\Events\TriggeredAuditLog; use FireflyIII\Events\TriggeredAuditLog;
use FireflyIII\Models\RuleAction; use FireflyIII\Models\RuleAction;
use FireflyIII\Models\TransactionJournal; use FireflyIII\Models\TransactionJournal;
@@ -63,16 +64,19 @@ class MoveNotesToDescription implements ActionInterface
$object = TransactionJournal::where('user_id', $journal['user_id'])->find($journal['transaction_journal_id']); $object = TransactionJournal::where('user_id', $journal['user_id'])->find($journal['transaction_journal_id']);
if (null === $object) { if (null === $object) {
Log::error(sprintf('No journal #%d belongs to user #%d.', $journal['transaction_journal_id'], $journal['user_id'])); Log::error(sprintf('No journal #%d belongs to user #%d.', $journal['transaction_journal_id'], $journal['user_id']));
event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.journal_other_user')));
return false; return false;
} }
$note = $object->notes()->first(); $note = $object->notes()->first();
if (null === $note) { if (null === $note) {
event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.no_notes_to_move')));
// nothing to move, return null // nothing to move, return null
return false; return false;
} }
if ('' === $note->text) { if ('' === $note->text) {
// nothing to move, return null // nothing to move, return null
$note->delete(); $note->delete();
event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.no_notes_to_move')));
return false; return false;
} }
$before = $object->description; $before = $object->description;

View File

@@ -24,6 +24,7 @@ declare(strict_types=1);
namespace FireflyIII\TransactionRules\Actions; namespace FireflyIII\TransactionRules\Actions;
use DB; use DB;
use FireflyIII\Events\Model\Rule\RuleActionFailedOnArray;
use FireflyIII\Events\TriggeredAuditLog; use FireflyIII\Events\TriggeredAuditLog;
use FireflyIII\Models\RuleAction; use FireflyIII\Models\RuleAction;
use FireflyIII\Models\TransactionJournal; use FireflyIII\Models\TransactionJournal;
@@ -55,6 +56,7 @@ class RemoveAllTags implements ActionInterface
$count = DB::table('tag_transaction_journal')->where('transaction_journal_id', $journal['transaction_journal_id'])->count(); $count = DB::table('tag_transaction_journal')->where('transaction_journal_id', $journal['transaction_journal_id'])->count();
if (0 === $count) { if (0 === $count) {
Log::debug(sprintf('RuleAction RemoveAllTags, journal #%d has no tags.', $journal['transaction_journal_id'])); Log::debug(sprintf('RuleAction RemoveAllTags, journal #%d has no tags.', $journal['transaction_journal_id']));
event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.no_tags_to_remove')));
return false; return false;
} }
Log::debug(sprintf('RuleAction RemoveAllTags removed all tags from journal %d.', $journal['transaction_journal_id'])); Log::debug(sprintf('RuleAction RemoveAllTags removed all tags from journal %d.', $journal['transaction_journal_id']));

View File

@@ -24,6 +24,7 @@ declare(strict_types=1);
namespace FireflyIII\TransactionRules\Actions; namespace FireflyIII\TransactionRules\Actions;
use DB; use DB;
use FireflyIII\Events\Model\Rule\RuleActionFailedOnArray;
use FireflyIII\Events\TriggeredAuditLog; use FireflyIII\Events\TriggeredAuditLog;
use FireflyIII\Models\RuleAction; use FireflyIII\Models\RuleAction;
use FireflyIII\Models\TransactionJournal; use FireflyIII\Models\TransactionJournal;
@@ -61,6 +62,7 @@ class RemoveTag implements ActionInterface
Log::debug( Log::debug(
sprintf('RuleAction RemoveTag tried to remove tag "%s" from journal #%d but no such tag exists.', $name, $journal['transaction_journal_id']) sprintf('RuleAction RemoveTag tried to remove tag "%s" from journal #%d but no such tag exists.', $name, $journal['transaction_journal_id'])
); );
event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.cannot_find_tag', ['tag' => $name])));
return false; return false;
} }
$count = DB::table('tag_transaction_journal')->where('transaction_journal_id', $journal['transaction_journal_id'])->where('tag_id', $tag->id)->count(); $count = DB::table('tag_transaction_journal')->where('transaction_journal_id', $journal['transaction_journal_id'])->where('tag_id', $tag->id)->count();
@@ -68,6 +70,7 @@ class RemoveTag implements ActionInterface
Log::debug( Log::debug(
sprintf('RuleAction RemoveTag tried to remove tag "%s" from journal #%d but no such tag is linked.', $name, $journal['transaction_journal_id']) sprintf('RuleAction RemoveTag tried to remove tag "%s" from journal #%d but no such tag is linked.', $name, $journal['transaction_journal_id'])
); );
event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.cannot_unlink_tag', ['tag' => $name])));
return false; return false;
} }

View File

@@ -24,6 +24,7 @@ declare(strict_types=1);
namespace FireflyIII\TransactionRules\Actions; namespace FireflyIII\TransactionRules\Actions;
use DB; use DB;
use FireflyIII\Events\Model\Rule\RuleActionFailedOnArray;
use FireflyIII\Events\TriggeredAuditLog; use FireflyIII\Events\TriggeredAuditLog;
use FireflyIII\Models\RuleAction; use FireflyIII\Models\RuleAction;
use FireflyIII\Models\TransactionJournal; use FireflyIII\Models\TransactionJournal;
@@ -65,7 +66,7 @@ class SetBudget implements ActionInterface
$search $search
) )
); );
event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.cannot_find_budget', ['name' => $search])));
return false; return false;
} }
@@ -78,7 +79,7 @@ class SetBudget implements ActionInterface
$journal['transaction_type_type'] $journal['transaction_type_type']
) )
); );
event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.cannot_set_budget', ['type' => $journal['transaction_type_type'], 'name' => $search])));
return false; return false;
} }

View File

@@ -24,6 +24,7 @@ declare(strict_types=1);
namespace FireflyIII\TransactionRules\Actions; namespace FireflyIII\TransactionRules\Actions;
use DB; use DB;
use FireflyIII\Events\Model\Rule\RuleActionFailedOnArray;
use FireflyIII\Events\TriggeredAuditLog; use FireflyIII\Events\TriggeredAuditLog;
use FireflyIII\Factory\CategoryFactory; use FireflyIII\Factory\CategoryFactory;
use FireflyIII\Models\RuleAction; use FireflyIII\Models\RuleAction;
@@ -57,7 +58,7 @@ class SetCategory implements ActionInterface
$search = $this->action->action_value; $search = $this->action->action_value;
if (null === $user) { if (null === $user) {
Log::error(sprintf('Journal has no valid user ID so action SetCategory("%s") cannot be applied', $search), $journal); Log::error(sprintf('Journal has no valid user ID so action SetCategory("%s") cannot be applied', $search), $journal);
event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.no_such_journal')));
return false; return false;
} }
@@ -73,7 +74,7 @@ class SetCategory implements ActionInterface
$search $search
) )
); );
event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.cannot_find_category', ['name' => $search])));
return false; return false;
} }

View File

@@ -24,6 +24,7 @@ declare(strict_types=1);
namespace FireflyIII\TransactionRules\Actions; namespace FireflyIII\TransactionRules\Actions;
use DB; use DB;
use FireflyIII\Events\Model\Rule\RuleActionFailedOnArray;
use FireflyIII\Events\TriggeredAuditLog; use FireflyIII\Events\TriggeredAuditLog;
use FireflyIII\Models\Account; use FireflyIII\Models\Account;
use FireflyIII\Models\RuleAction; use FireflyIII\Models\RuleAction;
@@ -65,7 +66,7 @@ class SetDestinationAccount implements ActionInterface
if (null === $object) { if (null === $object) {
Log::error('Could not find journal.'); Log::error('Could not find journal.');
event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.no_such_journal')));
return false; return false;
} }
$type = $object->transactionType->type; $type = $object->transactionType->type;
@@ -81,7 +82,7 @@ class SetDestinationAccount implements ActionInterface
$this->action->action_value $this->action->action_value
) )
); );
event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.cannot_find_asset', ['name' => $this->action->action_value])));
return false; return false;
} }
@@ -90,13 +91,13 @@ class SetDestinationAccount implements ActionInterface
$source = $object->transactions()->where('amount', '<', 0)->first(); $source = $object->transactions()->where('amount', '<', 0)->first();
if (null === $source) { if (null === $source) {
Log::error('Could not find source transaction.'); Log::error('Could not find source transaction.');
event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.cannot_find_source_transaction')));
return false; return false;
} }
// account must not be deleted (in the meantime): // account must not be deleted (in the meantime):
if (null === $source->account) { if (null === $source->account) {
Log::error('Could not find source transaction account.'); Log::error('Could not find source transaction account.');
event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.cannot_find_source_transaction_account')));
return false; return false;
} }
if (null !== $newAccount && (int)$newAccount->id === (int)$source->account_id) { if (null !== $newAccount && (int)$newAccount->id === (int)$source->account_id) {
@@ -108,6 +109,7 @@ class SetDestinationAccount implements ActionInterface
) )
); );
event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.already_has_destination', ['name' => $newAccount->name])));
return false; return false;
} }

View File

@@ -24,6 +24,7 @@ declare(strict_types=1);
namespace FireflyIII\TransactionRules\Actions; namespace FireflyIII\TransactionRules\Actions;
use DB; use DB;
use FireflyIII\Events\Model\Rule\RuleActionFailedOnArray;
use FireflyIII\Events\TriggeredAuditLog; use FireflyIII\Events\TriggeredAuditLog;
use FireflyIII\Models\Account; use FireflyIII\Models\Account;
use FireflyIII\Models\RuleAction; use FireflyIII\Models\RuleAction;
@@ -64,7 +65,7 @@ class SetSourceAccount implements ActionInterface
$this->repository = app(AccountRepositoryInterface::class); $this->repository = app(AccountRepositoryInterface::class);
if (null === $object) { if (null === $object) {
Log::error('Could not find journal.'); Log::error('Could not find journal.');
event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.no_such_journal')));
return false; return false;
} }
$type = $object->transactionType->type; $type = $object->transactionType->type;
@@ -76,7 +77,7 @@ class SetSourceAccount implements ActionInterface
Log::error( Log::error(
sprintf('Cant change source account of journal #%d because no asset account with name "%s" exists.', $object->id, $this->action->action_value) sprintf('Cant change source account of journal #%d because no asset account with name "%s" exists.', $object->id, $this->action->action_value)
); );
event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.cannot_find_asset', ['name' => $this->action->action_value])));
return false; return false;
} }
@@ -85,13 +86,13 @@ class SetSourceAccount implements ActionInterface
$destination = $object->transactions()->where('amount', '>', 0)->first(); $destination = $object->transactions()->where('amount', '>', 0)->first();
if (null === $destination) { if (null === $destination) {
Log::error('Could not find destination transaction.'); Log::error('Could not find destination transaction.');
event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.cannot_find_destination_transaction')));
return false; return false;
} }
// account must not be deleted (in the meantime): // account must not be deleted (in the meantime):
if (null === $destination->account) { if (null === $destination->account) {
Log::error('Could not find destination transaction account.'); Log::error('Could not find destination transaction account.');
event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.cannot_find_destination_transaction_account')));
return false; return false;
} }
if (null !== $newAccount && (int)$newAccount->id === (int)$destination->account_id) { if (null !== $newAccount && (int)$newAccount->id === (int)$destination->account_id) {
@@ -102,12 +103,12 @@ class SetSourceAccount implements ActionInterface
$destination->account_id $destination->account_id
) )
); );
event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.already_has_source', ['name' => $newAccount->name])));
return false; return false;
} }
// if this is a deposit, the new source account must be a revenue account and may be created: // if this is a deposit, the new source account must be a revenue account and may be created:
// or its a liability // or it's a liability
if (TransactionType::DEPOSIT === $type) { if (TransactionType::DEPOSIT === $type) {
$newAccount = $this->findDepositSourceAccount(); $newAccount = $this->findDepositSourceAccount();
} }

View File

@@ -24,6 +24,7 @@ declare(strict_types=1);
namespace FireflyIII\TransactionRules\Actions; namespace FireflyIII\TransactionRules\Actions;
use DB; use DB;
use FireflyIII\Events\Model\Rule\RuleActionFailedOnArray;
use FireflyIII\Events\TriggeredAuditLog; use FireflyIII\Events\TriggeredAuditLog;
use FireflyIII\Models\RuleAction; use FireflyIII\Models\RuleAction;
use FireflyIII\Models\Transaction; use FireflyIII\Models\Transaction;
@@ -59,18 +60,20 @@ class SwitchAccounts implements ActionInterface
$object = TransactionJournal::where('user_id', $journal['user_id'])->find($journal['transaction_journal_id']); $object = TransactionJournal::where('user_id', $journal['user_id'])->find($journal['transaction_journal_id']);
if (null === $object) { if (null === $object) {
Log::error(sprintf('Cannot find journal #%d, cannot switch accounts.', $journal['transaction_journal_id'])); Log::error(sprintf('Cannot find journal #%d, cannot switch accounts.', $journal['transaction_journal_id']));
event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.no_such_journal')));
return false; return false;
} }
$groupCount = TransactionJournal::where('transaction_group_id', $journal['transaction_group_id'])->count(); $groupCount = TransactionJournal::where('transaction_group_id', $journal['transaction_group_id'])->count();
if ($groupCount > 1) { if ($groupCount > 1) {
Log::error(sprintf('Group #%d has more than one transaction in it, cannot switch accounts.', $journal['transaction_group_id'])); Log::error(sprintf('Group #%d has more than one transaction in it, cannot switch accounts.', $journal['transaction_group_id']));
event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.split_group')));
return false; return false;
} }
$type = $object->transactionType->type; $type = $object->transactionType->type;
if (TransactionType::TRANSFER !== $type) { if (TransactionType::TRANSFER !== $type) {
Log::error(sprintf('Journal #%d is NOT a transfer (rule #%d), cannot switch accounts.', $journal['transaction_journal_id'], $this->action->rule_id)); Log::error(sprintf('Journal #%d is NOT a transfer (rule #%d), cannot switch accounts.', $journal['transaction_journal_id'], $this->action->rule_id));
event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.is_not_transfer')));
return false; return false;
} }
@@ -80,7 +83,7 @@ class SwitchAccounts implements ActionInterface
$destTransaction = $object->transactions()->where('amount', '>', 0)->first(); $destTransaction = $object->transactions()->where('amount', '>', 0)->first();
if (null === $sourceTransaction || null === $destTransaction) { if (null === $sourceTransaction || null === $destTransaction) {
Log::error(sprintf('Journal #%d has no source or destination transaction (rule #%d), cannot switch accounts.', $journal['transaction_journal_id'], $this->action->rule_id)); Log::error(sprintf('Journal #%d has no source or destination transaction (rule #%d), cannot switch accounts.', $journal['transaction_journal_id'], $this->action->rule_id));
event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.cannot_find_accounts')));
return false; return false;
} }
$sourceAccountId = (int)$sourceTransaction->account_id; $sourceAccountId = (int)$sourceTransaction->account_id;

View File

@@ -24,6 +24,7 @@ declare(strict_types=1);
namespace FireflyIII\TransactionRules\Actions; namespace FireflyIII\TransactionRules\Actions;
use FireflyIII\Events\Model\Rule\RuleActionFailedOnArray;
use FireflyIII\Events\TriggeredAuditLog; use FireflyIII\Events\TriggeredAuditLog;
use FireflyIII\Models\PiggyBank; use FireflyIII\Models\PiggyBank;
use FireflyIII\Models\RuleAction; use FireflyIII\Models\RuleAction;
@@ -63,14 +64,13 @@ class UpdatePiggybank implements ActionInterface
/** @var TransactionJournal $journalObj */ /** @var TransactionJournal $journalObj */
$journalObj = $user->transactionJournals()->find($journal['transaction_journal_id']); $journalObj = $user->transactionJournals()->find($journal['transaction_journal_id']);
$type = TransactionType::find((int)$journalObj->transaction_type_id); $type = TransactionType::find((int)$journalObj->transaction_type_id);
$journal['transaction_type_type'] = $type->type;
$piggyBank = $this->findPiggyBank($user); $piggyBank = $this->findPiggyBank($user);
if (null === $piggyBank) { if (null === $piggyBank) {
Log::info( Log::info(
sprintf('No piggy bank named "%s", cant execute action #%d of rule #%d', $this->action->action_value, $this->action->id, $this->action->rule_id) sprintf('No piggy bank named "%s", cant execute action #%d of rule #%d', $this->action->action_value, $this->action->id, $this->action->rule_id)
); );
event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.cannot_find_piggy', ['name' => $this->action->action_value])));
return false; return false;
} }
@@ -130,7 +130,7 @@ class UpdatePiggybank implements ActionInterface
$destination->account_id $destination->account_id
) )
); );
event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.no_link_piggy', ['name' => $this->action->action_value])));
return false; return false;
} }

View File

@@ -28,6 +28,7 @@ use Carbon\Carbon;
use FireflyIII\Exceptions\FireflyException; use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Models\Account; use FireflyIII\Models\Account;
use FireflyIII\Models\AccountMeta; use FireflyIII\Models\AccountMeta;
use FireflyIII\Models\AccountType;
use FireflyIII\Models\TransactionCurrency; use FireflyIII\Models\TransactionCurrency;
use FireflyIII\Repositories\Currency\CurrencyRepositoryInterface; use FireflyIII\Repositories\Currency\CurrencyRepositoryInterface;
use Illuminate\Support\Collection; use Illuminate\Support\Collection;
@@ -38,6 +39,7 @@ use Illuminate\Support\Collection;
class AccountTransformer extends AbstractTransformer class AccountTransformer extends AbstractTransformer
{ {
private array $accountMeta; private array $accountMeta;
private array $accountTypes;
private array $balances; private array $balances;
private array $convertedBalances; private array $convertedBalances;
private array $currencies; private array $currencies;
@@ -51,6 +53,7 @@ class AccountTransformer extends AbstractTransformer
{ {
$this->currencies = []; $this->currencies = [];
$this->accountMeta = []; $this->accountMeta = [];
$this->accountTypes = [];
$this->balances = app('steam')->balancesByAccounts($objects, $this->getDate()); $this->balances = app('steam')->balancesByAccounts($objects, $this->getDate());
$this->convertedBalances = app('steam')->balancesByAccountsConverted($objects, $this->getDate()); $this->convertedBalances = app('steam')->balancesByAccountsConverted($objects, $this->getDate());
$repository = app(CurrencyRepositoryInterface::class); $repository = app(CurrencyRepositoryInterface::class);
@@ -72,6 +75,15 @@ class AccountTransformer extends AbstractTransformer
$id = (int)$entry->account_id; $id = (int)$entry->account_id;
$this->accountMeta[$id][$entry->name] = $entry->data; $this->accountMeta[$id][$entry->name] = $entry->data;
} }
// get account types:
// select accounts.id, account_types.type from account_types left join accounts on accounts.account_type_id = account_types.id;
$accountTypes = AccountType::leftJoin('accounts', 'accounts.account_type_id', '=', 'account_types.id')
->whereIn('accounts.id', $accountIds)
->get(['accounts.id', 'account_types.type']);
/** @var AccountType $row */
foreach ($accountTypes as $row) {
$this->accountTypes[(int)$row->id] = (string)config(sprintf('firefly.shortNamesByFullName.%s', $row->type));
}
} }
/** /**
@@ -96,10 +108,13 @@ class AccountTransformer extends AbstractTransformer
*/ */
public function transform(Account $account): array public function transform(Account $account): array
{ {
//$fullType = $account->accountType->type;
//$accountType = (string) config(sprintf('firefly.shortNamesByFullName.%s', $fullType));
$id = (int)$account->id; $id = (int)$account->id;
// various meta
$accountRole = $this->accountMeta[$id]['account_role'] ?? null;
$accountType = $this->accountTypes[$id];
$order = (int)$account->order;
// no currency? use default // no currency? use default
$currency = $this->default; $currency = $this->default;
if (0 !== (int)$this->accountMeta[$id]['currency_id']) { if (0 !== (int)$this->accountMeta[$id]['currency_id']) {
@@ -109,16 +124,21 @@ class AccountTransformer extends AbstractTransformer
$balance = $this->balances[$id] ?? null; $balance = $this->balances[$id] ?? null;
$nativeBalance = $this->convertedBalances[$id]['native_balance'] ?? null; $nativeBalance = $this->convertedBalances[$id]['native_balance'] ?? null;
// no order for some accounts:
if (!in_array(strtolower($accountType), ['liability', 'liabilities', 'asset'], true)) {
$order = null;
}
return [ return [
'id' => (string)$account->id, 'id' => (string)$account->id,
'created_at' => $account->created_at->toAtomString(), 'created_at' => $account->created_at->toAtomString(),
'updated_at' => $account->updated_at->toAtomString(), 'updated_at' => $account->updated_at->toAtomString(),
'active' => $account->active, 'active' => $account->active,
//'order' => $order, 'order' => $order,
'name' => $account->name, 'name' => $account->name,
'iban' => '' === $account->iban ? null : $account->iban, 'iban' => '' === $account->iban ? null : $account->iban,
// 'type' => strtolower($accountType), 'type' => strtolower($accountType),
// 'account_role' => $accountRole, 'account_role' => $accountRole,
'currency_id' => (string)$currency->id, 'currency_id' => (string)$currency->id,
'currency_code' => $currency->code, 'currency_code' => $currency->code,
'currency_symbol' => $currency->symbol, 'currency_symbol' => $currency->symbol,

View File

@@ -302,7 +302,7 @@ class TransactionGroupTransformer extends AbstractTransformer
if (null === $string) { if (null === $string) {
return null; return null;
} }
Log::debug(sprintf('Now in date("%s")', $string)); // Log::debug(sprintf('Now in date("%s")', $string));
if (10 === strlen($string)) { if (10 === strlen($string)) {
return Carbon::createFromFormat('Y-m-d', $string, config('app.timezone')); return Carbon::createFromFormat('Y-m-d', $string, config('app.timezone'));
} }

View File

@@ -114,7 +114,11 @@ trait DepositValidation
// source can be any of the following types. // source can be any of the following types.
$validTypes = array_keys($this->combinations[$this->transactionType]); $validTypes = array_keys($this->combinations[$this->transactionType]);
if (null === $accountId && null === $accountName && false === $this->canCreateTypes($validTypes)) { if (null === $accountId &&
null === $accountName &&
null === $accountIban &&
null === $accountNumber &&
false === $this->canCreateTypes($validTypes)) {
// if both values are NULL return false, // if both values are NULL return false,
// because the source of a deposit can't be created. // because the source of a deposit can't be created.
// (this never happens). // (this never happens).
@@ -122,12 +126,12 @@ trait DepositValidation
$result = false; $result = false;
} }
// if there is an iban, it can only be in use by a revenue account or we will fail. // if there is an iban, it can only be in use by a revenue account, or we will fail.
if (null !== $accountIban && '' !== $accountIban) { if (null !== $accountIban && '' !== $accountIban) {
app('log')->debug('Check if there is not already an account with this IBAN'); app('log')->debug('Check if there is not already an account with this IBAN');
$existing = $this->findExistingAccount([AccountType::ASSET, AccountType::LOAN, AccountType::DEBT, AccountType::MORTGAGE], ['iban' => $accountIban]); $existing = $this->findExistingAccount([AccountType::ASSET, AccountType::LOAN, AccountType::DEBT, AccountType::MORTGAGE], ['iban' => $accountIban]);
if (null !== $existing) { if (null !== $existing) {
$this->destError = (string)trans('validation.deposit_src_iban_exists'); $this->sourceError = (string)trans('validation.deposit_src_iban_exists');
return false; return false;
} }
} }

View File

@@ -265,6 +265,7 @@ class AccountValidator
if (null !== $accountId && $accountId > 0) { if (null !== $accountId && $accountId > 0) {
$first = $this->accountRepository->find($accountId); $first = $this->accountRepository->find($accountId);
if ((null !== $first) && in_array($first->accountType->type, $validTypes, true)) { if ((null !== $first) && in_array($first->accountType->type, $validTypes, true)) {
app('log')->debug(sprintf('ID: Found %s account #%d ("%s", IBAN "%s")', $first->accountType->type, $first->id, $first->name, $first->iban ?? 'no iban'));
return $first; return $first;
} }
} }
@@ -273,6 +274,7 @@ class AccountValidator
if (null !== $accountIban && '' !== (string)$accountIban) { if (null !== $accountIban && '' !== (string)$accountIban) {
$first = $this->accountRepository->findByIbanNull($accountIban, $validTypes); $first = $this->accountRepository->findByIbanNull($accountIban, $validTypes);
if ((null !== $first) && in_array($first->accountType->type, $validTypes, true)) { if ((null !== $first) && in_array($first->accountType->type, $validTypes, true)) {
app('log')->debug(sprintf('Iban: Found %s account #%d ("%s", IBAN "%s")', $first->accountType->type, $first->id, $first->name, $first->iban ?? 'no iban'));
return $first; return $first;
} }
} }
@@ -281,14 +283,19 @@ class AccountValidator
if (null !== $accountNumber && '' !== (string)$accountNumber) { if (null !== $accountNumber && '' !== (string)$accountNumber) {
$first = $this->accountRepository->findByAccountNumber($accountNumber, $validTypes); $first = $this->accountRepository->findByAccountNumber($accountNumber, $validTypes);
if ((null !== $first) && in_array($first->accountType->type, $validTypes, true)) { if ((null !== $first) && in_array($first->accountType->type, $validTypes, true)) {
app('log')->debug(sprintf('Number: Found %s account #%d ("%s", IBAN "%s")', $first->accountType->type, $first->id, $first->name, $first->iban ?? 'no iban'));
return $first; return $first;
} }
} }
// find by name: // find by name:
if ('' !== (string)$accountName) { if ('' !== (string)$accountName) {
return $this->accountRepository->findByName($accountName, $validTypes); $first = $this->accountRepository->findByName($accountName, $validTypes);
if (null !== $first) {
app('log')->debug(sprintf('Name: Found %s account #%d ("%s", IBAN "%s")', $first->accountType->type, $first->id, $first->name, $first->iban ?? 'no iban'));
} }
}
app('log')->debug('Found nothing!');
return null; return null;
} }

View File

@@ -103,6 +103,7 @@ trait TransactionValidation
*/ */
protected function validateSingleAccount(Validator $validator, int $index, string $transactionType, array $transaction): void protected function validateSingleAccount(Validator $validator, int $index, string $transactionType, array $transaction): void
{ {
app('log')->debug(sprintf('Now in validateSingleAccount(%d)', $index));
/** @var AccountValidator $accountValidator */ /** @var AccountValidator $accountValidator */
$accountValidator = app(AccountValidator::class); $accountValidator = app(AccountValidator::class);

View File

@@ -3,6 +3,31 @@
All notable changes to this project will be documented in this file. All notable changes to this project will be documented in this file.
This project adheres to [Semantic Versioning](http://semver.org/). This project adheres to [Semantic Versioning](http://semver.org/).
## 6.0.21 - 2023-09-02
### Added
- Rules will now report failures if a Slack/Discord notification channel is configured
- Notifications can be sent to Discord
- Beta layout `v2`, activate with `FIRELY_III_LAYOUT=v2`
### Changed
- Audit log settings are changed, refer to the `.env.example`-file.
- Many URLs are new rendered as relative URLs.
### Fixed
- [Issue 7853](https://github.com/firefly-iii/firefly-iii/issues/7853) Left to spend on main page shows incorrect value
- [Issue 7883](https://github.com/firefly-iii/firefly-iii/issues/7883) Missing translation
- [Issue 7910](https://github.com/firefly-iii/firefly-iii/issues/7910) Type format error
- Home page respects account order
- JS errors for users using Firefly III in a subdir.
### API
- Bumped to v2.0.6 but only so the docs match again.
## 6.0.20 - 2023-08-13 ## 6.0.20 - 2023-08-13
### Fixed ### Fixed

View File

@@ -85,7 +85,7 @@
"diglactic/laravel-breadcrumbs": "^8.1", "diglactic/laravel-breadcrumbs": "^8.1",
"doctrine/dbal": "3.*", "doctrine/dbal": "3.*",
"gdbots/query-parser": "^3.0", "gdbots/query-parser": "^3.0",
"guzzlehttp/guzzle": "^7.7", "guzzlehttp/guzzle": "^7.8",
"jc5/google2fa-laravel": "^2.0", "jc5/google2fa-laravel": "^2.0",
"jc5/recovery": "^2", "jc5/recovery": "^2",
"laravel/framework": "^10", "laravel/framework": "^10",
@@ -111,7 +111,7 @@
}, },
"require-dev": { "require-dev": {
"barryvdh/laravel-ide-helper": "2.*", "barryvdh/laravel-ide-helper": "2.*",
"ergebnis/phpstan-rules": "^2.0", "ergebnis/phpstan-rules": "^2.1",
"fakerphp/faker": "1.*", "fakerphp/faker": "1.*",
"filp/whoops": "2.*", "filp/whoops": "2.*",
"mockery/mockery": "1.*", "mockery/mockery": "1.*",

670
composer.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -110,7 +110,7 @@ return [
'handle_debts' => true, 'handle_debts' => true,
// see cer.php for exchange rates feature flag. // see cer.php for exchange rates feature flag.
], ],
'version' => '6.0.20', 'version' => '6.0.21',
'api_version' => '2.0.6', 'api_version' => '2.0.6',
'db_version' => 20, 'db_version' => 20,
@@ -148,7 +148,7 @@ return [
'update_minimum_age' => 7, 'update_minimum_age' => 7,
// notifications // notifications
'available_notifications' => ['bill_reminder', 'new_access_token', 'transaction_creation', 'user_login'], 'available_notifications' => ['bill_reminder', 'new_access_token', 'transaction_creation', 'user_login', 'rule_action_failures'],
'admin_notifications' => ['admin_new_reg', 'user_new_reg', 'new_version', 'invite_created', 'invite_redeemed'], 'admin_notifications' => ['admin_new_reg', 'user_new_reg', 'new_version', 'invite_created', 'invite_redeemed'],
// enabled languages // enabled languages

View File

@@ -24,6 +24,30 @@ declare(strict_types=1);
use FireflyIII\Support\Logging\AuditLogger; use FireflyIII\Support\Logging\AuditLogger;
use Monolog\Handler\SyslogUdpHandler; use Monolog\Handler\SyslogUdpHandler;
// standard config for both log things:
$defaultChannels = ['daily', 'stdout'];
$auditChannels = ['audit_daily', 'audit_stdout'];
// validChannels is missing 'stack' because we already check for that one.
$validChannels = ['single', 'papertrail', 'stdout', 'daily', 'syslog', 'errorlog'];
$validAuditChannels = ['audit_papertrail', 'audit_stdout', 'audit_stdout', 'audit_daily', 'audit_syslog', 'audit_errorlog'];
// which settings did the user set, if any?
$defaultLogChannel = (string)envNonEmpty('LOG_CHANNEL', 'stack');
$auditLogChannel = (string)envNonEmpty('AUDIT_LOG_CHANNEL', '');
if ('stack' === $defaultLogChannel) {
$defaultChannels = ['daily', 'stdout'];
}
if (in_array($defaultLogChannel, $validChannels, true)) {
$defaultChannels = [$defaultLogChannel];
}
if (in_array($auditLogChannel, $validAuditChannels, true)) {
$auditChannels = [$auditLogChannel];
}
return [ return [
/* /*
|-------------------------------------------------------------------------- |--------------------------------------------------------------------------
@@ -53,14 +77,25 @@ return [
*/ */
'channels' => [ 'channels' => [
// default channels for 'stack' and audit logs: /*
* 'stack' and 'audit' are the two "generic" channels that
* are valid destinations for logs.
*/
'stack' => [ 'stack' => [
'driver' => 'stack', 'driver' => 'stack',
'channels' => ['daily', 'stdout'], 'channels' => $defaultChannels,
], ],
'audit' => [ 'audit' => [
'driver' => 'stack', 'driver' => 'stack',
'channels' => ['audit_daily', 'audit_stdout'], 'channels' => $auditChannels,
],
/*
* There are 6 valid destinations for the normal logs, listed below:
*/
'single' => [
'driver' => 'single',
'path' => storage_path('logs/laravel.log'),
'level' => envNonEmpty('APP_LOG_LEVEL', 'info'),
], ],
'papertrail' => [ 'papertrail' => [
'driver' => 'monolog', 'driver' => 'monolog',
@@ -71,36 +106,39 @@ return [
'port' => env('PAPERTRAIL_PORT'), 'port' => env('PAPERTRAIL_PORT'),
], ],
], ],
// single laravel log file:
'single' => [
'driver' => 'single',
'path' => storage_path('logs/laravel.log'),
'level' => envNonEmpty('APP_LOG_LEVEL', 'info'),
],
// stdout, used in stack 'stack' by default:
'stdout' => [ 'stdout' => [
'driver' => 'single', 'driver' => 'single',
'path' => 'php://stdout', 'path' => 'php://stdout',
'level' => envNonEmpty('APP_LOG_LEVEL', 'info'), 'level' => envNonEmpty('APP_LOG_LEVEL', 'info'),
], ],
// daily, used in stack 'stack' by default:
'daily' => [ 'daily' => [
'driver' => 'daily', 'driver' => 'daily',
'path' => storage_path('logs/ff3-' . PHP_SAPI . '.log'), 'path' => storage_path('logs/ff3-' . PHP_SAPI . '.log'),
'level' => envNonEmpty('APP_LOG_LEVEL', 'info'), 'level' => envNonEmpty('APP_LOG_LEVEL', 'info'),
'days' => 7, 'days' => 7,
], ],
'syslog' => [
'driver' => 'syslog',
'level' => envNonEmpty('APP_LOG_LEVEL', 'info'),
],
'errorlog' => [
'driver' => 'errorlog',
'level' => envNonEmpty('APP_LOG_LEVEL', 'info'),
],
// the audit log destinations: /*
'audit_daily' => [ * There are 5 valid destinations for the audit logs, listed below.
'driver' => 'daily', * The only one missing is "single".
'path' => storage_path('logs/ff3-audit.log'), */
'tap' => [AuditLogger::class], 'audit_papertrail' => [
'driver' => 'monolog',
'level' => envNonEmpty('AUDIT_LOG_LEVEL', 'info'), 'level' => envNonEmpty('AUDIT_LOG_LEVEL', 'info'),
'days' => 90, 'handler' => SyslogUdpHandler::class,
'tap' => [AuditLogger::class],
'handler_with' => [
'host' => env('PAPERTRAIL_HOST'),
'port' => env('PAPERTRAIL_PORT'),
],
], ],
'audit_stdout' => [ 'audit_stdout' => [
'driver' => 'single', 'driver' => 'single',
@@ -108,18 +146,25 @@ return [
'tap' => [AuditLogger::class], 'tap' => [AuditLogger::class],
'level' => envNonEmpty('AUDIT_LOG_LEVEL', 'info'), 'level' => envNonEmpty('AUDIT_LOG_LEVEL', 'info'),
], ],
'audit_daily' => [
// syslog destination 'driver' => 'daily',
'syslog' => [ 'path' => storage_path('logs/ff3-audit.log'),
'tap' => [AuditLogger::class],
'level' => envNonEmpty('AUDIT_LOG_LEVEL', 'info'),
'days' => 90,
],
'audit_syslog' => [
'driver' => 'syslog', 'driver' => 'syslog',
'level' => envNonEmpty('APP_LOG_LEVEL', 'info'), 'tap' => [AuditLogger::class],
'level' => envNonEmpty('AUDIT_LOG_LEVEL', 'info'),
],
'audit_errorlog' => [
'driver' => 'errorlog',
'tap' => [AuditLogger::class],
'level' => envNonEmpty('AUDIT_LOG_LEVEL', 'info'),
], ],
// errorlog destination
'errorlog' => [
'driver' => 'errorlog',
'level' => envNonEmpty('APP_LOG_LEVEL', 'info'),
],
], ],
]; ];

View File

@@ -105,9 +105,9 @@ export default {
"rule_trigger_destination_account_nr_contains_choice": "El n\u00famero \/ IBAN del compte de dest\u00ed cont\u00e9..", "rule_trigger_destination_account_nr_contains_choice": "El n\u00famero \/ IBAN del compte de dest\u00ed cont\u00e9..",
"rule_trigger_transaction_type_choice": "La transacci\u00f3 \u00e9s del tipus..", "rule_trigger_transaction_type_choice": "La transacci\u00f3 \u00e9s del tipus..",
"rule_trigger_category_is_choice": "La categoria \u00e9s..", "rule_trigger_category_is_choice": "La categoria \u00e9s..",
"rule_trigger_amount_less_choice": "Amount is less than or equal to ..", "rule_trigger_amount_less_choice": "La quantitat \u00e9s menor o igual a ..",
"rule_trigger_amount_is_choice": "La quantitat \u00e9s..", "rule_trigger_amount_is_choice": "La quantitat \u00e9s..",
"rule_trigger_amount_more_choice": "Amount is more than or equal to..", "rule_trigger_amount_more_choice": "La quantitat \u00e9s major o igual a..",
"rule_trigger_description_starts_choice": "La descripci\u00f3 comen\u00e7a per..", "rule_trigger_description_starts_choice": "La descripci\u00f3 comen\u00e7a per..",
"rule_trigger_description_ends_choice": "La descripci\u00f3 acaba amb..", "rule_trigger_description_ends_choice": "La descripci\u00f3 acaba amb..",
"rule_trigger_description_contains_choice": "La descripci\u00f3 cont\u00e9..", "rule_trigger_description_contains_choice": "La descripci\u00f3 cont\u00e9..",
@@ -140,7 +140,7 @@ export default {
"rule_trigger_external_id_is_choice": "L'ID externa \u00e9s..", "rule_trigger_external_id_is_choice": "L'ID externa \u00e9s..",
"rule_trigger_internal_reference_is_choice": "La refer\u00e8ncia interna \u00e9s..", "rule_trigger_internal_reference_is_choice": "La refer\u00e8ncia interna \u00e9s..",
"rule_trigger_journal_id_choice": "L'ID del llibre de transaccions \u00e9s..", "rule_trigger_journal_id_choice": "L'ID del llibre de transaccions \u00e9s..",
"rule_trigger_any_external_url_choice": "Transaction has an (any) external URL", "rule_trigger_any_external_url_choice": "La transaci\u00f3 t\u00e9 (cap) URL externa",
"rule_trigger_no_external_url_choice": "La transacci\u00f3 no t\u00e9 URL extern", "rule_trigger_no_external_url_choice": "La transacci\u00f3 no t\u00e9 URL extern",
"rule_trigger_id_choice": "L'ID de la transacci\u00f3 \u00e9s..", "rule_trigger_id_choice": "L'ID de la transacci\u00f3 \u00e9s..",
"rule_action_delete_transaction_choice": "ELIMINAR transacci\u00f3(!)", "rule_action_delete_transaction_choice": "ELIMINAR transacci\u00f3(!)",

View File

@@ -105,9 +105,9 @@ export default {
"rule_trigger_destination_account_nr_contains_choice": "\ub300\uc0c1 \uacc4\uc88c \ubc88\ud638 \/ IBAN\uc740...\ub97c \ud3ec\ud568\ud569\ub2c8\ub2e4", "rule_trigger_destination_account_nr_contains_choice": "\ub300\uc0c1 \uacc4\uc88c \ubc88\ud638 \/ IBAN\uc740...\ub97c \ud3ec\ud568\ud569\ub2c8\ub2e4",
"rule_trigger_transaction_type_choice": "\uac70\ub798\ub294 .. \uc720\ud615\uc785\ub2c8\ub2e4", "rule_trigger_transaction_type_choice": "\uac70\ub798\ub294 .. \uc720\ud615\uc785\ub2c8\ub2e4",
"rule_trigger_category_is_choice": "\uce74\ud14c\uace0\ub9ac\ub294 ..", "rule_trigger_category_is_choice": "\uce74\ud14c\uace0\ub9ac\ub294 ..",
"rule_trigger_amount_less_choice": "Amount is less than or equal to ..", "rule_trigger_amount_less_choice": "\uae08\uc561\uc774 .. \uc640 \uc791\uac70\ub098 \uac19\uc74c",
"rule_trigger_amount_is_choice": "\uae08\uc561\uc740..", "rule_trigger_amount_is_choice": "\uae08\uc561\uc740..",
"rule_trigger_amount_more_choice": "Amount is more than or equal to..", "rule_trigger_amount_more_choice": "\uae08\uc561\uc774 .. \uc640 \ud06c\uac70\ub098 \uac19\uc74c",
"rule_trigger_description_starts_choice": "\uc124\uba85\uc774 ..\ub85c \uc2dc\uc791\ud569\ub2c8\ub2e4", "rule_trigger_description_starts_choice": "\uc124\uba85\uc774 ..\ub85c \uc2dc\uc791\ud569\ub2c8\ub2e4",
"rule_trigger_description_ends_choice": "\uc124\uba85\uc774 ..\ub85c \ub05d\ub0a9\ub2c8\ub2e4", "rule_trigger_description_ends_choice": "\uc124\uba85\uc774 ..\ub85c \ub05d\ub0a9\ub2c8\ub2e4",
"rule_trigger_description_contains_choice": "\uc124\uba85\uc740 ..\ub97c \ud3ec\ud568\ud569\ub2c8\ub2e4", "rule_trigger_description_contains_choice": "\uc124\uba85\uc740 ..\ub97c \ud3ec\ud568\ud569\ub2c8\ub2e4",
@@ -140,7 +140,7 @@ export default {
"rule_trigger_external_id_is_choice": "\uc678\ubd80 ID\ub294..", "rule_trigger_external_id_is_choice": "\uc678\ubd80 ID\ub294..",
"rule_trigger_internal_reference_is_choice": "\ub0b4\ubd80 \ucc38\uc870\ub294..", "rule_trigger_internal_reference_is_choice": "\ub0b4\ubd80 \ucc38\uc870\ub294..",
"rule_trigger_journal_id_choice": "\uac70\ub798 \uc800\ub110 ID\ub294..", "rule_trigger_journal_id_choice": "\uac70\ub798 \uc800\ub110 ID\ub294..",
"rule_trigger_any_external_url_choice": "Transaction has an (any) external URL", "rule_trigger_any_external_url_choice": "\uac70\ub798\uc5d0 (\uc5b4\ub5a4) \uc678\ubd80 URL\uc774 \uc788\uc74c",
"rule_trigger_no_external_url_choice": "\uac70\ub798\uc5d0 \uc678\ubd80 URL\uc774 \uc5c6\uc2b5\ub2c8\ub2e4", "rule_trigger_no_external_url_choice": "\uac70\ub798\uc5d0 \uc678\ubd80 URL\uc774 \uc5c6\uc2b5\ub2c8\ub2e4",
"rule_trigger_id_choice": "\uac70\ub798 ID\ub294..", "rule_trigger_id_choice": "\uac70\ub798 ID\ub294..",
"rule_action_delete_transaction_choice": "\uac70\ub798 \uc0ad\uc81c(!)", "rule_action_delete_transaction_choice": "\uac70\ub798 \uc0ad\uc81c(!)",

View File

@@ -105,9 +105,9 @@ export default {
"rule_trigger_destination_account_nr_contains_choice": "Numer konta docelowego \/ IBAN zawiera..", "rule_trigger_destination_account_nr_contains_choice": "Numer konta docelowego \/ IBAN zawiera..",
"rule_trigger_transaction_type_choice": "Transakcja jest typu..", "rule_trigger_transaction_type_choice": "Transakcja jest typu..",
"rule_trigger_category_is_choice": "Kategoria to..", "rule_trigger_category_is_choice": "Kategoria to..",
"rule_trigger_amount_less_choice": "Amount is less than or equal to ..", "rule_trigger_amount_less_choice": "Kwota jest mniejsza lub r\u00f3wna ..",
"rule_trigger_amount_is_choice": "Kwota to..", "rule_trigger_amount_is_choice": "Kwota to..",
"rule_trigger_amount_more_choice": "Amount is more than or equal to..", "rule_trigger_amount_more_choice": "Kwota jest wi\u0119ksza lub r\u00f3wna..",
"rule_trigger_description_starts_choice": "Opis zaczyna si\u0119 od..", "rule_trigger_description_starts_choice": "Opis zaczyna si\u0119 od..",
"rule_trigger_description_ends_choice": "Opis ko\u0144czy si\u0119 na..", "rule_trigger_description_ends_choice": "Opis ko\u0144czy si\u0119 na..",
"rule_trigger_description_contains_choice": "Opis zawiera..", "rule_trigger_description_contains_choice": "Opis zawiera..",
@@ -140,7 +140,7 @@ export default {
"rule_trigger_external_id_is_choice": "Zewn\u0119trzne ID to..", "rule_trigger_external_id_is_choice": "Zewn\u0119trzne ID to..",
"rule_trigger_internal_reference_is_choice": "Wewn\u0119trzne odwo\u0142anie to..", "rule_trigger_internal_reference_is_choice": "Wewn\u0119trzne odwo\u0142anie to..",
"rule_trigger_journal_id_choice": "ID dziennika transakcji to..", "rule_trigger_journal_id_choice": "ID dziennika transakcji to..",
"rule_trigger_any_external_url_choice": "Transaction has an (any) external URL", "rule_trigger_any_external_url_choice": "Transakcja ma (dowolny) zewn\u0119trzny adres URL",
"rule_trigger_no_external_url_choice": "Transakcja nie ma zewn\u0119trznego adresu URL", "rule_trigger_no_external_url_choice": "Transakcja nie ma zewn\u0119trznego adresu URL",
"rule_trigger_id_choice": "Identyfikator transakcji to..", "rule_trigger_id_choice": "Identyfikator transakcji to..",
"rule_action_delete_transaction_choice": "USU\u0143 transakcj\u0119(!)", "rule_action_delete_transaction_choice": "USU\u0143 transakcj\u0119(!)",

View File

@@ -105,9 +105,9 @@ export default {
"rule_trigger_destination_account_nr_contains_choice": "N\u00famero da conta de destino (IBAN) cont\u00e9m..", "rule_trigger_destination_account_nr_contains_choice": "N\u00famero da conta de destino (IBAN) cont\u00e9m..",
"rule_trigger_transaction_type_choice": "Transa\u00e7\u00e3o \u00e9 do tipo..", "rule_trigger_transaction_type_choice": "Transa\u00e7\u00e3o \u00e9 do tipo..",
"rule_trigger_category_is_choice": "A categoria \u00e9..", "rule_trigger_category_is_choice": "A categoria \u00e9..",
"rule_trigger_amount_less_choice": "Amount is less than or equal to ..", "rule_trigger_amount_less_choice": "Valor \u00e9 menor ou igual a ..",
"rule_trigger_amount_is_choice": "Quantia \u00e9..", "rule_trigger_amount_is_choice": "Quantia \u00e9..",
"rule_trigger_amount_more_choice": "Amount is more than or equal to..", "rule_trigger_amount_more_choice": "Valor \u00e9 maior ou igual a..",
"rule_trigger_description_starts_choice": "Descri\u00e7\u00e3o come\u00e7a com..", "rule_trigger_description_starts_choice": "Descri\u00e7\u00e3o come\u00e7a com..",
"rule_trigger_description_ends_choice": "Descri\u00e7\u00e3o termina com..", "rule_trigger_description_ends_choice": "Descri\u00e7\u00e3o termina com..",
"rule_trigger_description_contains_choice": "Descri\u00e7\u00e3o cont\u00e9m..", "rule_trigger_description_contains_choice": "Descri\u00e7\u00e3o cont\u00e9m..",
@@ -140,7 +140,7 @@ export default {
"rule_trigger_external_id_is_choice": "ID externo \u00e9..", "rule_trigger_external_id_is_choice": "ID externo \u00e9..",
"rule_trigger_internal_reference_is_choice": "Refer\u00eancia interna \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_journal_id_choice": "ID do livro de transa\u00e7\u00e3o \u00e9..",
"rule_trigger_any_external_url_choice": "Transaction has an (any) external URL", "rule_trigger_any_external_url_choice": "A transa\u00e7\u00e3o tem uma URL externa (qualquer)",
"rule_trigger_no_external_url_choice": "A transa\u00e7\u00e3o n\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_trigger_id_choice": "O identificador da transa\u00e7\u00e3o \u00e9..",
"rule_action_delete_transaction_choice": "EXCLUIR transa\u00e7\u00e3o(!)", "rule_action_delete_transaction_choice": "EXCLUIR transa\u00e7\u00e3o(!)",

View File

@@ -50,7 +50,7 @@ export default {
"transactions": "Transakcije", "transactions": "Transakcije",
"title_expenses": "Stro\u0161ki", "title_expenses": "Stro\u0161ki",
"title_withdrawal": "Stro\u0161ki", "title_withdrawal": "Stro\u0161ki",
"title_revenue": "Dohodki \/ prihodki", "title_revenue": "Dohodki prihodki",
"title_deposit": "Dohodki\/prihodki", "title_deposit": "Dohodki\/prihodki",
"title_transfer": "Prenosi", "title_transfer": "Prenosi",
"title_transfers": "Prenosi", "title_transfers": "Prenosi",
@@ -60,7 +60,7 @@ export default {
"liabilities_accounts": "Obveznosti" "liabilities_accounts": "Obveznosti"
}, },
"firefly": { "firefly": {
"administration_index": "Financial administration", "administration_index": "Finan\u010dna administracija",
"actions": "Dejanja", "actions": "Dejanja",
"edit": "uredi", "edit": "uredi",
"delete": "izbri\u0161i", "delete": "izbri\u0161i",
@@ -81,87 +81,87 @@ export default {
"budgeted": "Prora\u010dun", "budgeted": "Prora\u010dun",
"spent": "Porabljeno", "spent": "Porabljeno",
"no_bill": "(ni ra\u010duna)", "no_bill": "(ni ra\u010duna)",
"rule_trigger_source_account_starts_choice": "Source account name starts with..", "rule_trigger_source_account_starts_choice": "Ime izvornega ra\u010duna se za\u010dne z ...",
"rule_trigger_source_account_ends_choice": "Source account name ends with..", "rule_trigger_source_account_ends_choice": "Ime izvornega ra\u010duna se kon\u010da s\/z ...",
"rule_trigger_source_account_is_choice": "Source account name is..", "rule_trigger_source_account_is_choice": "Ime izvornega ra\u010duna je ...",
"rule_trigger_source_account_contains_choice": "Source account name contains..", "rule_trigger_source_account_contains_choice": "Ime izvornega ra\u010duna vsebuje ...",
"rule_trigger_account_id_choice": "Either account ID is exactly..", "rule_trigger_account_id_choice": "ID ra\u010duna je natan\u010dno ...",
"rule_trigger_source_account_id_choice": "Source account ID is exactly..", "rule_trigger_source_account_id_choice": "ID izvornega ra\u010duna je to\u010dno ...",
"rule_trigger_destination_account_id_choice": "Destination account ID is exactly..", "rule_trigger_destination_account_id_choice": "ID ciljnega ra\u010duna je to\u010dno ...",
"rule_trigger_account_is_cash_choice": "Either account is cash", "rule_trigger_account_is_cash_choice": "Kateri koli ra\u010dun je gotovina",
"rule_trigger_source_is_cash_choice": "Source account is (cash) account", "rule_trigger_source_is_cash_choice": "Izvorni ra\u010dun je gotovinski ra\u010dun",
"rule_trigger_destination_is_cash_choice": "Destination account is (cash) account", "rule_trigger_destination_is_cash_choice": "Ciljni ra\u010dun je (gotovinski) ra\u010dun",
"rule_trigger_source_account_nr_starts_choice": "Source account number \/ IBAN starts with..", "rule_trigger_source_account_nr_starts_choice": "Izvorna \u0161tevilka ra\u010duna \/ IBAN se za\u010dne z ...",
"rule_trigger_source_account_nr_ends_choice": "Source account number \/ IBAN ends with..", "rule_trigger_source_account_nr_ends_choice": "Izvorna \u0161tevilka ra\u010duna \/ IBAN se kon\u010da z ...",
"rule_trigger_source_account_nr_is_choice": "Source account number \/ IBAN is..", "rule_trigger_source_account_nr_is_choice": "Izvorna \u0161tevilka ra\u010duna \/ IBAN je ...",
"rule_trigger_source_account_nr_contains_choice": "Source account number \/ IBAN contains..", "rule_trigger_source_account_nr_contains_choice": "Izvorna \u0161tevilka ra\u010duna \/ IBAN vsebuje ...",
"rule_trigger_destination_account_starts_choice": "Destination account name starts with..", "rule_trigger_destination_account_starts_choice": "Ime ciljnega ra\u010duna se za\u010dne z ...",
"rule_trigger_destination_account_ends_choice": "Destination account name ends with..", "rule_trigger_destination_account_ends_choice": "Ime ciljnega ra\u010duna se kon\u010da z ...",
"rule_trigger_destination_account_is_choice": "Destination account name is..", "rule_trigger_destination_account_is_choice": "Ime ciljnega ra\u010duna je ...",
"rule_trigger_destination_account_contains_choice": "Destination account name contains..", "rule_trigger_destination_account_contains_choice": "Ime ciljnega ra\u010duna vsebuje ...",
"rule_trigger_destination_account_nr_starts_choice": "Destination account number \/ IBAN starts with..", "rule_trigger_destination_account_nr_starts_choice": "\u0160tevilka ciljnega ra\u010duna \/ IBAN se za\u010dne s\/z ...",
"rule_trigger_destination_account_nr_ends_choice": "Destination account number \/ IBAN ends with..", "rule_trigger_destination_account_nr_ends_choice": "\u0160tevilka ciljnega ra\u010duna \/ IBAN se kon\u010da s\/z ...",
"rule_trigger_destination_account_nr_is_choice": "Destination account number \/ IBAN is..", "rule_trigger_destination_account_nr_is_choice": "\u0160tevilka ciljnega ra\u010duna \/ IBAN je ...",
"rule_trigger_destination_account_nr_contains_choice": "Destination account number \/ IBAN contains..", "rule_trigger_destination_account_nr_contains_choice": "\u0160tevilka ciljnega ra\u010duna \/ IBAN vsebuje ...",
"rule_trigger_transaction_type_choice": "Tip transakcije je..", "rule_trigger_transaction_type_choice": "Tip transakcije je..",
"rule_trigger_category_is_choice": "Kategorija je..", "rule_trigger_category_is_choice": "Kategorija je..",
"rule_trigger_amount_less_choice": "Amount is less than or equal to ..", "rule_trigger_amount_less_choice": "Znesek je manj\u0161i ali enak ...",
"rule_trigger_amount_is_choice": "Znesek je..", "rule_trigger_amount_is_choice": "Znesek je..",
"rule_trigger_amount_more_choice": "Amount is more than or equal to..", "rule_trigger_amount_more_choice": "Znesek je ve\u010dji ali enak ...",
"rule_trigger_description_starts_choice": "Opis se za\u010dne s\/z..", "rule_trigger_description_starts_choice": "Opis se za\u010dne s\/z..",
"rule_trigger_description_ends_choice": "Opis se kon\u010da z..", "rule_trigger_description_ends_choice": "Opis se kon\u010da z..",
"rule_trigger_description_contains_choice": "Opis vsebuje..", "rule_trigger_description_contains_choice": "Opis vsebuje..",
"rule_trigger_description_is_choice": "Opis je..", "rule_trigger_description_is_choice": "Opis je..",
"rule_trigger_date_on_choice": "Transaction date is..", "rule_trigger_date_on_choice": "Datum transakcije je ...",
"rule_trigger_date_before_choice": "Transaction date is before..", "rule_trigger_date_before_choice": "Datum transakcije je pred ...",
"rule_trigger_date_after_choice": "Transaction date is after..", "rule_trigger_date_after_choice": "Datum transakcije je po ...",
"rule_trigger_created_at_on_choice": "Transaction was made on..", "rule_trigger_created_at_on_choice": "Transakcija je bila izvedena dne ...",
"rule_trigger_updated_at_on_choice": "Transaction was last edited on..", "rule_trigger_updated_at_on_choice": "Transakcija je bila nazadnje urejena ...",
"rule_trigger_budget_is_choice": "Prora\u010dun je..", "rule_trigger_budget_is_choice": "Prora\u010dun je..",
"rule_trigger_tag_is_choice": "Any tag is..", "rule_trigger_tag_is_choice": "Vsaka oznaka je ...",
"rule_trigger_currency_is_choice": "Valuta transakcije je..", "rule_trigger_currency_is_choice": "Valuta transakcije je..",
"rule_trigger_foreign_currency_is_choice": "Transaction foreign currency is..", "rule_trigger_foreign_currency_is_choice": "Tuja valuta transakcije je ...",
"rule_trigger_has_attachments_choice": "Ima vsaj toliko priponk", "rule_trigger_has_attachments_choice": "Ima vsaj toliko priponk",
"rule_trigger_has_no_category_choice": "Nima kategorije", "rule_trigger_has_no_category_choice": "Nima kategorije",
"rule_trigger_has_any_category_choice": "Ima kategorijo", "rule_trigger_has_any_category_choice": "Ima kategorijo",
"rule_trigger_has_no_budget_choice": "Nima prora\u010duna", "rule_trigger_has_no_budget_choice": "Nima prora\u010duna",
"rule_trigger_has_any_budget_choice": "Ima (katerikoli) prora\u010dun", "rule_trigger_has_any_budget_choice": "Ima (katerikoli) prora\u010dun",
"rule_trigger_has_no_bill_choice": "Nima ra\u010duna", "rule_trigger_has_no_bill_choice": "Nima ra\u010duna",
"rule_trigger_has_any_bill_choice": "Has a (any) bill", "rule_trigger_has_any_bill_choice": "Ima (kateri koli) trajnik",
"rule_trigger_has_no_tag_choice": "Nima oznak", "rule_trigger_has_no_tag_choice": "Nima oznak",
"rule_trigger_has_any_tag_choice": "Ima eno ali ve\u010d oznak", "rule_trigger_has_any_tag_choice": "Ima eno ali ve\u010d oznak",
"rule_trigger_any_notes_choice": "Ima zaznamke", "rule_trigger_any_notes_choice": "Ima zaznamke",
"rule_trigger_no_notes_choice": "Nima zaznamkov", "rule_trigger_no_notes_choice": "Nima zaznamkov",
"rule_trigger_notes_is_choice": "Opombe so..", "rule_trigger_notes_is_choice": "Opombe so..",
"rule_trigger_notes_contains_choice": "Notes contain..", "rule_trigger_notes_contains_choice": "Opombe vsebujejo ...",
"rule_trigger_notes_starts_choice": "Notes start with..", "rule_trigger_notes_starts_choice": "Opombe se za\u010dnejo s\/z ...",
"rule_trigger_notes_ends_choice": "Notes end with..", "rule_trigger_notes_ends_choice": "Opombe se kon\u010dajo s\/z ...",
"rule_trigger_bill_is_choice": "Ra\u010dun je..", "rule_trigger_bill_is_choice": "Ra\u010dun je..",
"rule_trigger_external_id_is_choice": "External ID is..", "rule_trigger_external_id_is_choice": "Zunanji ID je ...",
"rule_trigger_internal_reference_is_choice": "Internal reference is..", "rule_trigger_internal_reference_is_choice": "Notranja referenca je ..",
"rule_trigger_journal_id_choice": "Transaction journal ID is..", "rule_trigger_journal_id_choice": "ID dnevnika transakcij je ...",
"rule_trigger_any_external_url_choice": "Transaction has an (any) external URL", "rule_trigger_any_external_url_choice": "Transakcija ima (kateri koli) zunanji URL",
"rule_trigger_no_external_url_choice": "Transaction has no external URL", "rule_trigger_no_external_url_choice": "Transakcija nima zunanjega URL-ja",
"rule_trigger_id_choice": "Transaction ID is..", "rule_trigger_id_choice": "ID transakcije je ...",
"rule_action_delete_transaction_choice": "DELETE transaction(!)", "rule_action_delete_transaction_choice": "IZBRI\u0160ITE transakcijo (!)",
"rule_action_set_category_choice": "Set category to ..", "rule_action_set_category_choice": "Nastavi kategorijo na ...",
"rule_action_clear_category_choice": "Po\u010disti kategorijo", "rule_action_clear_category_choice": "Po\u010disti kategorijo",
"rule_action_set_budget_choice": "Set budget to ..", "rule_action_set_budget_choice": "Nastavi prora\u010dun na ...",
"rule_action_clear_budget_choice": "Po\u010disti prora\u010dun", "rule_action_clear_budget_choice": "Po\u010disti prora\u010dun",
"rule_action_add_tag_choice": "Dodaj oznako ..", "rule_action_add_tag_choice": "Dodaj oznako ..",
"rule_action_remove_tag_choice": "Remove tag ..", "rule_action_remove_tag_choice": "Odstrani oznako ...",
"rule_action_remove_all_tags_choice": "Odstrani vse oznake", "rule_action_remove_all_tags_choice": "Odstrani vse oznake",
"rule_action_set_description_choice": "Set description to ..", "rule_action_set_description_choice": "Nastavi opis na ...",
"rule_action_update_piggy_choice": "Add \/ remove transaction amount in piggy bank ..", "rule_action_update_piggy_choice": "Add \/ remove transaction amount in piggy bank ..",
"rule_action_append_description_choice": "Append description with ..", "rule_action_append_description_choice": "Za opis dodaj ...",
"rule_action_prepend_description_choice": "Prepend description with ..", "rule_action_prepend_description_choice": "Pred opis dodaj ...",
"rule_action_set_source_account_choice": "Set source account to ..", "rule_action_set_source_account_choice": "Nastavi izvorni ra\u010dun na ...",
"rule_action_set_destination_account_choice": "Set destination account to ..", "rule_action_set_destination_account_choice": "Nastavi ciljni ra\u010dun na ...",
"rule_action_append_notes_choice": "Append notes with ..", "rule_action_append_notes_choice": "Za opombo dodaj ...",
"rule_action_prepend_notes_choice": "Prepend notes with ..", "rule_action_prepend_notes_choice": "Pred opombo dodaj ...",
"rule_action_clear_notes_choice": "Odstrani opombe", "rule_action_clear_notes_choice": "Odstrani opombe",
"rule_action_set_notes_choice": "Set notes to ..", "rule_action_set_notes_choice": "Nastavi opombe na ...",
"rule_action_link_to_bill_choice": "Link to a bill ..", "rule_action_link_to_bill_choice": "Pove\u017ei s trajnikom ...",
"rule_action_convert_deposit_choice": "Pretvori transakcijo v polog", "rule_action_convert_deposit_choice": "Pretvori transakcijo v polog",
"rule_action_convert_withdrawal_choice": "Pretvori transakcijo v odliv", "rule_action_convert_withdrawal_choice": "Pretvori transakcijo v odliv",
"rule_action_convert_transfer_choice": "Pretvori transakcijo v prenos", "rule_action_convert_transfer_choice": "Pretvori transakcijo v prenos",
@@ -185,7 +185,7 @@ export default {
"asset_accounts": "premo\u017eenjski ra\u010duni", "asset_accounts": "premo\u017eenjski ra\u010duni",
"expense_accounts": "Ra\u010duni stro\u0161kov", "expense_accounts": "Ra\u010duni stro\u0161kov",
"liabilities_accounts": "Obveznosti", "liabilities_accounts": "Obveznosti",
"undefined_accounts": "Accounts", "undefined_accounts": "Ra\u010duni",
"name": "Ime", "name": "Ime",
"revenue_accounts": "Ra\u010dun prihodkov", "revenue_accounts": "Ra\u010dun prihodkov",
"description": "Opis", "description": "Opis",
@@ -213,13 +213,13 @@ export default {
"transactions": "Transakcije", "transactions": "Transakcije",
"balance": "Stanje", "balance": "Stanje",
"budgets": "Prora\u010duni", "budgets": "Prora\u010duni",
"subscriptions": "Subscriptions", "subscriptions": "Naro\u010dnine",
"welcome_back": "Kaj dogaja?", "welcome_back": "Kaj dogaja?",
"bills_to_pay": "Trajnik za pla\u010dilo", "bills_to_pay": "Trajnik za pla\u010dilo",
"net_worth": "Neto vrednost", "net_worth": "Neto vrednost",
"pref_last365": "Zadnje leto", "pref_last365": "Zadnje leto",
"pref_last90": "Last 90 days", "pref_last90": "Zadnjih 90 dni",
"pref_last30": "Last 30 days", "pref_last30": "Zadnjih 30 dni",
"pref_last7": "Zadnjih 7 dni", "pref_last7": "Zadnjih 7 dni",
"pref_YTD": "Leto do datuma", "pref_YTD": "Leto do datuma",
"pref_QTD": "Quarter to date", "pref_QTD": "Quarter to date",

View File

@@ -338,7 +338,7 @@ page container: q-ma-xs (margin all, xs) AND q-mb-md to give the page content so
<q-footer bordered class="bg-grey-8 text-white"> <q-footer bordered class="bg-grey-8 text-white">
<q-toolbar> <q-toolbar>
<div> <div>
<small>Firefly III v v6.0.19 &copy; James Cole, AGPL-3.0-or-later.</small> <small>Firefly III v v6.0.21 &copy; James Cole, AGPL-3.0-or-later.</small>
</div> </div>
</q-toolbar> </q-toolbar>
</q-footer> </q-footer>

82
package-lock.json generated
View File

@@ -7,9 +7,10 @@
"dependencies": { "dependencies": {
"@fortawesome/fontawesome-free": "^6.4.0", "@fortawesome/fontawesome-free": "^6.4.0",
"@popperjs/core": "^2.11.8", "@popperjs/core": "^2.11.8",
"alpinejs": "^3.12.3", "alpinejs": "^3.13.0",
"bootstrap": "^5.3.0", "bootstrap": "^5.3.0",
"chart.js": "^4.3.3", "bootstrap5-autocomplete": "^1.1.20",
"chart.js": "^4.4.0",
"chartjs-adapter-date-fns": "^3.0.0", "chartjs-adapter-date-fns": "^3.0.0",
"chartjs-chart-sankey": "^0.12.0", "chartjs-chart-sankey": "^0.12.0",
"date-fns": "^2.30.0", "date-fns": "^2.30.0",
@@ -17,17 +18,17 @@
"store": "^2.0.12" "store": "^2.0.12"
}, },
"devDependencies": { "devDependencies": {
"axios": "^1.1.2", "axios": "^1.5.0",
"laravel-vite-plugin": "^0.7.5", "laravel-vite-plugin": "^0.8.0",
"sass": "^1.64.2", "sass": "^1.66.1",
"vite": "^4.0.0", "vite": "^4.0.0",
"vite-plugin-manifest-sri": "^0.1.0" "vite-plugin-manifest-sri": "^0.1.0"
} }
}, },
"node_modules/@babel/runtime": { "node_modules/@babel/runtime": {
"version": "7.22.10", "version": "7.22.11",
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.22.10.tgz", "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.22.11.tgz",
"integrity": "sha512-21t/fkKLMZI4pqP2wlmsQAWnYW1PDyKyyUV4vCi+B25ydmdaYTKXPwCj0BzSUnZf4seIiYvSA3jcZ3gdsMFkLQ==", "integrity": "sha512-ee7jVNlWN09+KftVOu9n7S8gQzD/Z6hN/I8VBRXW4P1+Xe7kJGXMwu8vds4aGIMHZnNbdpSWCfZZtinytpcAvA==",
"dependencies": { "dependencies": {
"regenerator-runtime": "^0.14.0" "regenerator-runtime": "^0.14.0"
}, },
@@ -424,9 +425,9 @@
"integrity": "sha512-oJ4F3TnvpXaQwZJNF3ZK+kLPHKarDmJjJ6jyzVNDKH9md1dptjC7lWR//jrGuLdek/U6iltWxqAnYOu8gCiOvA==" "integrity": "sha512-oJ4F3TnvpXaQwZJNF3ZK+kLPHKarDmJjJ6jyzVNDKH9md1dptjC7lWR//jrGuLdek/U6iltWxqAnYOu8gCiOvA=="
}, },
"node_modules/alpinejs": { "node_modules/alpinejs": {
"version": "3.12.3", "version": "3.13.0",
"resolved": "https://registry.npmjs.org/alpinejs/-/alpinejs-3.12.3.tgz", "resolved": "https://registry.npmjs.org/alpinejs/-/alpinejs-3.13.0.tgz",
"integrity": "sha512-fLz2dfYQ3xCk7Ip8LiIpV2W+9brUyex2TAE7Z0BCvZdUDklJE+n+a8gCgLWzfZ0GzZNZu7HUP8Z0z6Xbm6fsSA==", "integrity": "sha512-7FYR1Yz3evIjlJD1mZ3SYWSw+jlOmQGeQ1QiSufSQ6J84XMQFkzxm6OobiZ928SfqhGdoIp2SsABNsS4rXMMJw==",
"dependencies": { "dependencies": {
"@vue/reactivity": "~3.1.1" "@vue/reactivity": "~3.1.1"
} }
@@ -451,9 +452,9 @@
"dev": true "dev": true
}, },
"node_modules/axios": { "node_modules/axios": {
"version": "1.4.0", "version": "1.5.0",
"resolved": "https://registry.npmjs.org/axios/-/axios-1.4.0.tgz", "resolved": "https://registry.npmjs.org/axios/-/axios-1.5.0.tgz",
"integrity": "sha512-S4XCWMEmzvo64T9GfvQDOXgYRDJ/wsSZc7Jvdgx5u1sd0JwsuPLqb3SYmusag+edF6ziyMensPVqLTSc1PiSEA==", "integrity": "sha512-D4DdjDo5CY50Qms0qGQTTw6Q44jl7zRwY7bthds06pUGfChBCTcQs+N743eFWGEd6pRTMd6A+I87aWyFV5wiZQ==",
"dev": true, "dev": true,
"dependencies": { "dependencies": {
"follow-redirects": "^1.15.0", "follow-redirects": "^1.15.0",
@@ -462,9 +463,9 @@
} }
}, },
"node_modules/bignumber.js": { "node_modules/bignumber.js": {
"version": "9.1.1", "version": "9.1.2",
"resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.1.1.tgz", "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.1.2.tgz",
"integrity": "sha512-pHm4LsMJ6lzgNGVfZHjMoO8sdoRhOzOH4MLmY65Jg70bpxCKu5iOHNJyfF6OyvYw7t8Fpf35RuzUyqnQsj8Vig==", "integrity": "sha512-2/mKyZH9K85bzOEfhXDBFZTGd1CTs+5IHpeFQo9luiBG7hghdC851Pj2WAhb6E3R6b9tZj/XKhbg4fum+Kepug==",
"engines": { "engines": {
"node": "*" "node": "*"
} }
@@ -496,6 +497,11 @@
"@popperjs/core": "^2.11.8" "@popperjs/core": "^2.11.8"
} }
}, },
"node_modules/bootstrap5-autocomplete": {
"version": "1.1.20",
"resolved": "https://registry.npmjs.org/bootstrap5-autocomplete/-/bootstrap5-autocomplete-1.1.20.tgz",
"integrity": "sha512-lvX/Za+GnZ7fC1NqVXQr4DWA0eQMR+/uyFjTasdfNSwAMTFhBTIexCsEM+0pXiKdJJnZUXbfcxaNxW1I0rAwLw=="
},
"node_modules/braces": { "node_modules/braces": {
"version": "3.0.2", "version": "3.0.2",
"resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz",
@@ -509,9 +515,9 @@
} }
}, },
"node_modules/chart.js": { "node_modules/chart.js": {
"version": "4.3.3", "version": "4.4.0",
"resolved": "https://registry.npmjs.org/chart.js/-/chart.js-4.3.3.tgz", "resolved": "https://registry.npmjs.org/chart.js/-/chart.js-4.4.0.tgz",
"integrity": "sha512-aTk7pBw+x6sQYhon/NR3ikfUJuym/LdgpTlgZRe2PaEhjUMKBKyNaFCMVRAyTEWYFNO7qRu7iQVqOw/OqzxZxQ==", "integrity": "sha512-vQEj6d+z0dcsKLlQvbKIMYFHd3t8W/7L2vfJIbYcfyPcRx92CsHqECpueN8qVGNlKyDcr5wBrYAYKnfu/9Q1hQ==",
"dependencies": { "dependencies": {
"@kurkle/color": "^0.3.0" "@kurkle/color": "^0.3.0"
}, },
@@ -683,9 +689,9 @@
} }
}, },
"node_modules/fsevents": { "node_modules/fsevents": {
"version": "2.3.2", "version": "2.3.3",
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
"integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
"dev": true, "dev": true,
"hasInstallScript": true, "hasInstallScript": true,
"optional": true, "optional": true,
@@ -718,9 +724,9 @@
} }
}, },
"node_modules/immutable": { "node_modules/immutable": {
"version": "4.3.2", "version": "4.3.4",
"resolved": "https://registry.npmjs.org/immutable/-/immutable-4.3.2.tgz", "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.3.4.tgz",
"integrity": "sha512-oGXzbEDem9OOpDWZu88jGiYCvIsLHMvGw+8OXlpsvTFvIQplQbjg1B1cvKg8f7Hoch6+NGjpPsH1Fr+Mc2D1aA==", "integrity": "sha512-fsXeu4J4i6WNWSikpI88v/PcVflZz+6kMhUfIwc5SY+poQRPnaf5V7qds6SUyUN3cVxEzuCab7QIoLOQ+DQ1wA==",
"dev": true "dev": true
}, },
"node_modules/is-binary-path": { "node_modules/is-binary-path": {
@@ -766,9 +772,9 @@
} }
}, },
"node_modules/laravel-vite-plugin": { "node_modules/laravel-vite-plugin": {
"version": "0.7.8", "version": "0.8.0",
"resolved": "https://registry.npmjs.org/laravel-vite-plugin/-/laravel-vite-plugin-0.7.8.tgz", "resolved": "https://registry.npmjs.org/laravel-vite-plugin/-/laravel-vite-plugin-0.8.0.tgz",
"integrity": "sha512-HWYqpQYHR3kEQ1LsHX7gHJoNNf0bz5z5mDaHBLzS+PGLCTmYqlU5/SZyeEgObV7z7bC/cnStYcY9H1DI1D5Udg==", "integrity": "sha512-6VjLI+azBpeK6rWBiKcb/En5GnTdYpL0U4zS8gXYvb2/VSq4mlau5H3NWpSktUDBMM1b97LLgICx5zevi8IY0w==",
"dev": true, "dev": true,
"dependencies": { "dependencies": {
"picocolors": "^1.0.0", "picocolors": "^1.0.0",
@@ -853,9 +859,9 @@
} }
}, },
"node_modules/postcss": { "node_modules/postcss": {
"version": "8.4.27", "version": "8.4.29",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.27.tgz", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.29.tgz",
"integrity": "sha512-gY/ACJtJPSmUFPDCHtX78+01fHa64FaU4zaaWfuh1MhGJISufJAH4cun6k/8fwsHYeK4UQmENQK+tRLCFJE8JQ==", "integrity": "sha512-cbI+jaqIeu/VGqXEarWkRCCffhjgXc0qjBtXpqJhTBohMUjUQnbBr0xqX3vEKudc4iviTewcJo5ajcec5+wdJw==",
"dev": true, "dev": true,
"funding": [ "funding": [
{ {
@@ -904,9 +910,9 @@
"integrity": "sha512-srw17NI0TUWHuGa5CFGGmhfNIeja30WMBfbslPNhf6JrqQlLN5gcrvig1oqPxiVaXb0oW0XRKtH6Nngs5lKCIA==" "integrity": "sha512-srw17NI0TUWHuGa5CFGGmhfNIeja30WMBfbslPNhf6JrqQlLN5gcrvig1oqPxiVaXb0oW0XRKtH6Nngs5lKCIA=="
}, },
"node_modules/rollup": { "node_modules/rollup": {
"version": "3.28.0", "version": "3.28.1",
"resolved": "https://registry.npmjs.org/rollup/-/rollup-3.28.0.tgz", "resolved": "https://registry.npmjs.org/rollup/-/rollup-3.28.1.tgz",
"integrity": "sha512-d7zhvo1OUY2SXSM6pfNjgD5+d0Nz87CUp4mt8l/GgVP3oBsPwzNvSzyu1me6BSG9JIgWNTVcafIXBIyM8yQ3yw==", "integrity": "sha512-R9OMQmIHJm9znrU3m3cpE8uhN0fGdXiawME7aZIpQqvpS/85+Vt1Hq1/yVIcYfOmaQiHjvXkQAoJukvLpau6Yw==",
"dev": true, "dev": true,
"bin": { "bin": {
"rollup": "dist/bin/rollup" "rollup": "dist/bin/rollup"
@@ -920,9 +926,9 @@
} }
}, },
"node_modules/sass": { "node_modules/sass": {
"version": "1.65.1", "version": "1.66.1",
"resolved": "https://registry.npmjs.org/sass/-/sass-1.65.1.tgz", "resolved": "https://registry.npmjs.org/sass/-/sass-1.66.1.tgz",
"integrity": "sha512-9DINwtHmA41SEd36eVPQ9BJKpn7eKDQmUHmpI0y5Zv2Rcorrh0zS+cFrt050hdNbmmCNKTW3hV5mWfuegNRsEA==", "integrity": "sha512-50c+zTsZOJVgFfTgwwEzkjA3/QACgdNsKueWPyAR0mRINIvLAStVQBbPg14iuqEQ74NPDbXzJARJ/O4SI1zftA==",
"dev": true, "dev": true,
"dependencies": { "dependencies": {
"chokidar": ">=3.0.0 <4.0.0", "chokidar": ">=3.0.0 <4.0.0",

View File

@@ -6,18 +6,19 @@
"build": "vite build" "build": "vite build"
}, },
"devDependencies": { "devDependencies": {
"axios": "^1.1.2", "axios": "^1.5.0",
"laravel-vite-plugin": "^0.7.5", "laravel-vite-plugin": "^0.8.0",
"sass": "^1.64.2", "sass": "^1.66.1",
"vite": "^4.0.0", "vite": "^4.0.0",
"vite-plugin-manifest-sri": "^0.1.0" "vite-plugin-manifest-sri": "^0.1.0"
}, },
"dependencies": { "dependencies": {
"@fortawesome/fontawesome-free": "^6.4.0", "@fortawesome/fontawesome-free": "^6.4.0",
"@popperjs/core": "^2.11.8", "@popperjs/core": "^2.11.8",
"alpinejs": "^3.12.3", "alpinejs": "^3.13.0",
"bootstrap": "^5.3.0", "bootstrap": "^5.3.0",
"chart.js": "^4.3.3", "bootstrap5-autocomplete": "^1.1.20",
"chart.js": "^4.4.0",
"chartjs-adapter-date-fns": "^3.0.0", "chartjs-adapter-date-fns": "^3.0.0",
"chartjs-chart-sankey": "^0.12.0", "chartjs-chart-sankey": "^0.12.0",
"date-fns": "^2.30.0", "date-fns": "^2.30.0",

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