mirror of
https://github.com/firefly-iii/firefly-iii.git
synced 2026-01-08 11:31:20 +00:00
Compare commits
477 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2915fae942 | ||
|
|
f9b5468481 | ||
|
|
287f37eba5 | ||
|
|
26740668da | ||
|
|
bdd72f0d30 | ||
|
|
7e79f25949 | ||
|
|
2d917e166c | ||
|
|
ad4a811d0a | ||
|
|
ab1aa97af4 | ||
|
|
018941c5b3 | ||
|
|
cc1439fb7b | ||
|
|
f684a2900b | ||
|
|
83b721a322 | ||
|
|
a879528ed8 | ||
|
|
accbff3ccb | ||
|
|
0375f77b73 | ||
|
|
9d01162f42 | ||
|
|
3436dc1564 | ||
|
|
089293079f | ||
|
|
00e2cd7c04 | ||
|
|
f5abb933b0 | ||
|
|
848386bbaf | ||
|
|
5562a3c2ae | ||
|
|
9e8738660b | ||
|
|
178772d50d | ||
|
|
8d7f29c7c9 | ||
|
|
255f87f1c3 | ||
|
|
975354d081 | ||
|
|
ec6890ced1 | ||
|
|
290efb4b62 | ||
|
|
ab3ea76244 | ||
|
|
21d07aef15 | ||
|
|
6031cd02b9 | ||
|
|
2cdb4cab12 | ||
|
|
679217f2ef | ||
|
|
3aacc0a258 | ||
|
|
254cf6cc5b | ||
|
|
c06095259a | ||
|
|
30fd98c8d7 | ||
|
|
3a410f04fe | ||
|
|
4a0a58cdc1 | ||
|
|
c834f93539 | ||
|
|
35bdb84e57 | ||
|
|
36a3a09fcc | ||
|
|
2c07b490e8 | ||
|
|
5790311cbd | ||
|
|
fde3322117 | ||
|
|
5664e133d1 | ||
|
|
a1dc38a83f | ||
|
|
df974f93ee | ||
|
|
3dde64cdac | ||
|
|
6b4887df0f | ||
|
|
ca8ef3961e | ||
|
|
114e0e5b1e | ||
|
|
c7a247cbba | ||
|
|
3a641a7020 | ||
|
|
0977859bf5 | ||
|
|
a644253ee9 | ||
|
|
3403356be8 | ||
|
|
4a9ec0d0d4 | ||
|
|
250c5c0745 | ||
|
|
120b5880b1 | ||
|
|
18eff73795 | ||
|
|
89b9dbb1b4 | ||
|
|
dde4b3ab93 | ||
|
|
b1cc655ba5 | ||
|
|
5645a12d5b | ||
|
|
ce1a935c06 | ||
|
|
58b90ad6c9 | ||
|
|
4740e63e76 | ||
|
|
319932c734 | ||
|
|
7cf415288a | ||
|
|
fe67c1db8f | ||
|
|
09afbd89b8 | ||
|
|
824f7d827c | ||
|
|
3a8ca56f95 | ||
|
|
137e3fd083 | ||
|
|
1285944b1f | ||
|
|
cd8e9deab7 | ||
|
|
de424eac64 | ||
|
|
d106a09766 | ||
|
|
963e8ccd2c | ||
|
|
3474b8f2d0 | ||
|
|
e27b449ae9 | ||
|
|
7656818456 | ||
|
|
d214be1215 | ||
|
|
c8678c3ee5 | ||
|
|
108ee40c3f | ||
|
|
de51d205dc | ||
|
|
1f76246edc | ||
|
|
f91974b766 | ||
|
|
df85936145 | ||
|
|
f39aa0f52a | ||
|
|
180812394b | ||
|
|
291fa9597f | ||
|
|
6c4d8c25fc | ||
|
|
bf4b32a8e9 | ||
|
|
2e9dac7678 | ||
|
|
369314c1cb | ||
|
|
6ab86898af | ||
|
|
f3aa65d219 | ||
|
|
edf4f19de9 | ||
|
|
91eaaae6d7 | ||
|
|
ca0ae5a165 | ||
|
|
86d788d294 | ||
|
|
e345cea9be | ||
|
|
779f7841c2 | ||
|
|
15fdca46b8 | ||
|
|
97e099c70f | ||
|
|
d5a58bb763 | ||
|
|
2774df0d30 | ||
|
|
93a1799a4b | ||
|
|
f65f051c7c | ||
|
|
c46952dd4e | ||
|
|
1dad6b6118 | ||
|
|
e964621f2c | ||
|
|
46ae62f693 | ||
|
|
7d578f5852 | ||
|
|
467f257ad3 | ||
|
|
55186c0a49 | ||
|
|
1a0a4f7112 | ||
|
|
658290ae80 | ||
|
|
cb9f29c6fc | ||
|
|
d8f4955292 | ||
|
|
9803932324 | ||
|
|
a3a416b5e2 | ||
|
|
1453a318fe | ||
|
|
9ffa01d318 | ||
|
|
29b779c873 | ||
|
|
598307f676 | ||
|
|
aa9d546bf5 | ||
|
|
f2ef245eca | ||
|
|
458606381e | ||
|
|
244f174dc9 | ||
|
|
8a577f3197 | ||
|
|
2f1b24cc14 | ||
|
|
d5f836db7a | ||
|
|
ffd7c36cb2 | ||
|
|
dba543d539 | ||
|
|
9753c0f8eb | ||
|
|
c50b4d3d47 | ||
|
|
907f886cf0 | ||
|
|
218a2d2004 | ||
|
|
0e0f2c6833 | ||
|
|
e59090d3b6 | ||
|
|
0404be8bef | ||
|
|
337895cbaa | ||
|
|
743547096e | ||
|
|
81fb3df45e | ||
|
|
12624cab5b | ||
|
|
7d62ea88d2 | ||
|
|
abcc277430 | ||
|
|
4c4c9426ee | ||
|
|
88fdeb2bf2 | ||
|
|
f9ab868022 | ||
|
|
148d2cec8f | ||
|
|
5869f157f6 | ||
|
|
69a2902161 | ||
|
|
c19b048249 | ||
|
|
cd2c8acdb2 | ||
|
|
7d0d1c764f | ||
|
|
b5acf1d529 | ||
|
|
72b08384ad | ||
|
|
f51e48f282 | ||
|
|
2e6d1f3642 | ||
|
|
716af4ed93 | ||
|
|
31de86c9eb | ||
|
|
04a2cd1f1f | ||
|
|
bc0ef11a8c | ||
|
|
90c8420a4c | ||
|
|
657d5e0d74 | ||
|
|
03fa4d957c | ||
|
|
8d4530f1f2 | ||
|
|
6d15c503c3 | ||
|
|
75ddb34398 | ||
|
|
6fbfe6fb72 | ||
|
|
db500e911c | ||
|
|
c0c5ced6ad | ||
|
|
726270f8bc | ||
|
|
c3908450a0 | ||
|
|
f7ba05f465 | ||
|
|
dea7f7d5d6 | ||
|
|
ed7a0a2b9d | ||
|
|
ac6b4db0f8 | ||
|
|
b5d659c13c | ||
|
|
919cb5d1f2 | ||
|
|
e504d3cc35 | ||
|
|
1d67939e76 | ||
|
|
5d1e90d29c | ||
|
|
1678eba9cc | ||
|
|
cc142b2ba1 | ||
|
|
48cbffba14 | ||
|
|
d909bb1b25 | ||
|
|
3313e66fc2 | ||
|
|
085892a4c8 | ||
|
|
817e059230 | ||
|
|
7ce8891246 | ||
|
|
439b2589f9 | ||
|
|
e7b5cf66d2 | ||
|
|
b29fabf76c | ||
|
|
5d837c3ee4 | ||
|
|
4c0396ad1c | ||
|
|
97b62fce79 | ||
|
|
e904a38735 | ||
|
|
ae72187aed | ||
|
|
e4ebeefa61 | ||
|
|
417373ba70 | ||
|
|
36f9ded08e | ||
|
|
858925b8c8 | ||
|
|
d36c96fba9 | ||
|
|
2be6fb329e | ||
|
|
da56363ef9 | ||
|
|
90d73228f3 | ||
|
|
1c76fcd26b | ||
|
|
c26c4ddf15 | ||
|
|
537bdb62da | ||
|
|
b2ac2bd97a | ||
|
|
aee6ea56b5 | ||
|
|
ff5506c842 | ||
|
|
94c5340fbf | ||
|
|
0e11026b60 | ||
|
|
1d8e953ebc | ||
|
|
8708fba4bc | ||
|
|
bc1294ae61 | ||
|
|
308f05101e | ||
|
|
f368739303 | ||
|
|
1bca1b921b | ||
|
|
3e513e92b1 | ||
|
|
194fe178c0 | ||
|
|
5d10a19bfa | ||
|
|
8f52a68526 | ||
|
|
c67d0d59bb | ||
|
|
4477064f17 | ||
|
|
9dbe24b37c | ||
|
|
46a85295e8 | ||
|
|
63170324a8 | ||
|
|
4fd157b5f4 | ||
|
|
e5406a0ea3 | ||
|
|
9b64ba21fd | ||
|
|
18fcf07971 | ||
|
|
d3404c6570 | ||
|
|
1878b5287b | ||
|
|
7f74545586 | ||
|
|
a920894a8f | ||
|
|
2c912456ce | ||
|
|
ebda475972 | ||
|
|
05aace84e1 | ||
|
|
1cfa1faccc | ||
|
|
1caa393974 | ||
|
|
ef357ab6e5 | ||
|
|
c39c5492ea | ||
|
|
1a5d54f74f | ||
|
|
13dc6c7dfb | ||
|
|
4c7dee69c2 | ||
|
|
dc9cf7689d | ||
|
|
778a408c6c | ||
|
|
2b7f3061d0 | ||
|
|
92903e1ec3 | ||
|
|
c5a621010e | ||
|
|
0b5b636578 | ||
|
|
8fcdb91ba3 | ||
|
|
f67d5f1197 | ||
|
|
6e0e6203cc | ||
|
|
2694ce4148 | ||
|
|
7c02b032f6 | ||
|
|
deb7754cb9 | ||
|
|
624f3c60bd | ||
|
|
8a8e792faa | ||
|
|
bc836011bc | ||
|
|
107eedfb49 | ||
|
|
61a7dcda23 | ||
|
|
873ca4f438 | ||
|
|
29221c2901 | ||
|
|
eac9613df7 | ||
|
|
184d5d25a6 | ||
|
|
ae33411566 | ||
|
|
479ebcc3fa | ||
|
|
1ae572cf30 | ||
|
|
ac6e16688d | ||
|
|
69159e0271 | ||
|
|
cae35d6a5a | ||
|
|
df9b65e296 | ||
|
|
fb0b34a6a0 | ||
|
|
db3b822aef | ||
|
|
f4994ef151 | ||
|
|
8e27291417 | ||
|
|
aae9ad78e5 | ||
|
|
fb3efbfc66 | ||
|
|
0f8a66609a | ||
|
|
b3bb8c386f | ||
|
|
b3b5e0e155 | ||
|
|
99a0bf1286 | ||
|
|
650f0ee752 | ||
|
|
2ddcf1120f | ||
|
|
d45c74915c | ||
|
|
cd373791ac | ||
|
|
7b03b0c5fc | ||
|
|
57d6677131 | ||
|
|
05e73344eb | ||
|
|
fff8f96490 | ||
|
|
1ccfb34246 | ||
|
|
b30bd00993 | ||
|
|
6b2d9fe816 | ||
|
|
531114279d | ||
|
|
944aaff0fa | ||
|
|
6bafa6819d | ||
|
|
8d7091285a | ||
|
|
579e95219a | ||
|
|
4a746881e3 | ||
|
|
9122f1b642 | ||
|
|
8b033ed4a8 | ||
|
|
8e53a63243 | ||
|
|
4de01e7387 | ||
|
|
f85ab74e2b | ||
|
|
8bb7d5de3f | ||
|
|
68424e485c | ||
|
|
ac34285eed | ||
|
|
e605a82573 | ||
|
|
1061764426 | ||
|
|
8d93e410f7 | ||
|
|
9336cdcc5d | ||
|
|
7639c03646 | ||
|
|
2da55bff9c | ||
|
|
6ea5d45dec | ||
|
|
e9504a3899 | ||
|
|
7bff2fd1e5 | ||
|
|
694712e933 | ||
|
|
aa675559e6 | ||
|
|
2af19d675a | ||
|
|
4588d16fc2 | ||
|
|
5dbd3031be | ||
|
|
59c16eba77 | ||
|
|
8c21ec96d1 | ||
|
|
2c00a8353d | ||
|
|
19e7a76f85 | ||
|
|
f16a186faf | ||
|
|
456dd39ec4 | ||
|
|
5b69a697e4 | ||
|
|
20d279fee4 | ||
|
|
cd55d819af | ||
|
|
dce51da92f | ||
|
|
6118cf6041 | ||
|
|
4ae8bffd4d | ||
|
|
5ebe86d5b6 | ||
|
|
317d97a310 | ||
|
|
082aa1d8e3 | ||
|
|
2f2963676f | ||
|
|
f2d388f742 | ||
|
|
d79008495a | ||
|
|
43f1867fb8 | ||
|
|
043c28628c | ||
|
|
9540854c8a | ||
|
|
b951a6ca66 | ||
|
|
26561a395d | ||
|
|
0a884c8718 | ||
|
|
b393e3c662 | ||
|
|
c36062210f | ||
|
|
861381fbb6 | ||
|
|
b9019f9590 | ||
|
|
fcfa877e89 | ||
|
|
d61849eefe | ||
|
|
bc83bd6ec8 | ||
|
|
c18c94e565 | ||
|
|
77d077ec08 | ||
|
|
021d0e6359 | ||
|
|
7f4fd2c52f | ||
|
|
8ffe098e3b | ||
|
|
a2ca86ca74 | ||
|
|
deef018806 | ||
|
|
c3f8b2ea56 | ||
|
|
e3696b6055 | ||
|
|
b82b340668 | ||
|
|
a8b01c439b | ||
|
|
6ec686ca7e | ||
|
|
fdb664fecb | ||
|
|
09f838089b | ||
|
|
db0fe918b8 | ||
|
|
8ca696d547 | ||
|
|
b7ccb1ce5c | ||
|
|
5f0033e466 | ||
|
|
477adba3e1 | ||
|
|
eb15ca8ebd | ||
|
|
dbf5907c20 | ||
|
|
e95deb9443 | ||
|
|
e19c36b5c4 | ||
|
|
fdd589db4f | ||
|
|
cd4e45d8f5 | ||
|
|
e6e31b9cab | ||
|
|
b676b1fef9 | ||
|
|
58bfb35fa6 | ||
|
|
09131e8c36 | ||
|
|
1d6ca91c01 | ||
|
|
fa00ba2edd | ||
|
|
65114c8483 | ||
|
|
d033985141 | ||
|
|
5badfbee93 | ||
|
|
44a61842f0 | ||
|
|
70b61af572 | ||
|
|
53a53d393d | ||
|
|
6c95b5757b | ||
|
|
693b3e5a6f | ||
|
|
aac1338bdd | ||
|
|
2979137102 | ||
|
|
0068675f3b | ||
|
|
6c1a22ea7f | ||
|
|
2869b92e6b | ||
|
|
8a38ce1964 | ||
|
|
3a7c5566ed | ||
|
|
1bed8c2b08 | ||
|
|
e24500b306 | ||
|
|
6251914419 | ||
|
|
8d9e943d57 | ||
|
|
f71bc5d323 | ||
|
|
a8082b2b3f | ||
|
|
510b72303d | ||
|
|
4666d4bbaa | ||
|
|
025f1e9502 | ||
|
|
b4c05098a9 | ||
|
|
3c8ee6f7f7 | ||
|
|
8b34135b08 | ||
|
|
d4b7c6689f | ||
|
|
d72aadfb00 | ||
|
|
00ff8ae166 | ||
|
|
cffcf49b26 | ||
|
|
ad55aa8b85 | ||
|
|
accbdea942 | ||
|
|
22144b78ea | ||
|
|
c03ab269f0 | ||
|
|
0c6c4d5959 | ||
|
|
e4e1873770 | ||
|
|
3b3579025d | ||
|
|
5cf8f2f4f4 | ||
|
|
c4ff05b1ba | ||
|
|
2bf54d0b8e | ||
|
|
2a63888546 | ||
|
|
cb8294cbd2 | ||
|
|
c9a99be183 | ||
|
|
5d2e026e5a | ||
|
|
568fdad52a | ||
|
|
174fb65fed | ||
|
|
2e77c45ae8 | ||
|
|
1b674a0abf | ||
|
|
4beb8b1f6b | ||
|
|
3c4abb7b60 | ||
|
|
970c73c221 | ||
|
|
6797c1255f | ||
|
|
0ed159b553 | ||
|
|
faa8679804 | ||
|
|
e3d2253958 | ||
|
|
3f7b7996c0 | ||
|
|
9229ff54df | ||
|
|
204ab8f427 | ||
|
|
8ee6c0789d | ||
|
|
be188e7266 | ||
|
|
1a43b62750 | ||
|
|
eaa8321aa8 | ||
|
|
f01f0071dd | ||
|
|
ec69cc3590 | ||
|
|
102dac1eb8 | ||
|
|
d54c5f1f99 | ||
|
|
c2e0e00c47 | ||
|
|
f24e387363 | ||
|
|
09715b83e4 | ||
|
|
e4f197bbc9 | ||
|
|
de64cef48d | ||
|
|
b85329b371 | ||
|
|
215f9a4245 | ||
|
|
80ce7a64ba | ||
|
|
65f3dc0656 | ||
|
|
f9b6b841b6 | ||
|
|
d4f2d649e8 | ||
|
|
15b057fe68 | ||
|
|
9cb549f4a1 | ||
|
|
4d67578458 | ||
|
|
71faaf1c19 | ||
|
|
0187f29b4b | ||
|
|
9b6ccdd43a |
8
.github/CONTRIBUTING.md
vendored
8
.github/CONTRIBUTING.md
vendored
@@ -6,14 +6,6 @@
|
||||
|
||||
I am always interested in expanding Firefly III's many features. If you are requesting a new feature, please check out the list of [often requested features](https://firefly-iii.github.io/requested-features/).
|
||||
|
||||
## Bugs
|
||||
|
||||
First of all: thank you for reporting a bug instead of ditching the tool altogether. If you find a bug, please take the time and see if the [demo site](https://firefly-iii.nder.be/) is also suffering from this bug. Include as many log files and details as you think are necessary. Bugs have a lot of priority!
|
||||
|
||||
## Installation problems
|
||||
|
||||
Please take the time to read the [installation guide FAQ](https://firefly-iii.github.io/installation-guide-faq/) and make sure you search through closed issues for the problems other people have had. Your problem may be among them! If not, open an issue and I will help where I can.
|
||||
|
||||
## Pull requests
|
||||
|
||||
When contributing to Firefly III, please first discuss the change you wish to make via issue, email, or any other method. I can only accept pull requests against the `develop` branch, never the `master` branch.
|
||||
|
||||
11
.github/SUPPORT.md
vendored
Normal file
11
.github/SUPPORT.md
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
# Welcome to Firefly III on Github!
|
||||
|
||||
:+1::tada: Thank you for taking the time to contribute something to Firefly III!
|
||||
|
||||
## Bugs
|
||||
|
||||
First of all: thank you for reporting a bug instead of ditching the tool altogether. If you find a bug, please take the time and see if the [demo site](https://firefly-iii.nder.be/) is also suffering from this bug. Include as many log files and details as you think are necessary. Bugs have a lot of priority!
|
||||
|
||||
## Installation problems
|
||||
|
||||
Please take the time to read the [installation guide FAQ](https://firefly-iii.github.io/installation-guide-faq/) and make sure you search through closed issues for the problems other people have had. Your problem may be among them! If not, open an issue and I will help where I can.
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -5,3 +5,4 @@ Homestead.json
|
||||
Homestead.yaml
|
||||
.env
|
||||
public/google*.html
|
||||
report.html
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
language: php
|
||||
php:
|
||||
- 7.0
|
||||
- 7.1
|
||||
|
||||
cache:
|
||||
|
||||
73
CHANGELOG.md
73
CHANGELOG.md
@@ -2,6 +2,79 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
This project adheres to [Semantic Versioning](http://semver.org/).
|
||||
|
||||
## [4.6.4] - 2017-08-13
|
||||
### Added
|
||||
- PHP7.1 support
|
||||
- Routine to decrypt attachments from the command line, for issue #671
|
||||
- A routine that can check if your password has been stolen in the past.
|
||||
- Split transaction shows amount left to be split
|
||||
|
||||
|
||||
### Changed
|
||||
- Importer can (potentially) handle new import routines such as banks.
|
||||
- Importer can fall back from JSON errors
|
||||
|
||||
### Deprecated
|
||||
- Initial release.
|
||||
|
||||
### Removed
|
||||
- PHP7.0 support
|
||||
- Support for extended tag modes
|
||||
- Remove "time jumps" to non-empty periods
|
||||
|
||||
|
||||
### Fixed
|
||||
- #717, reported by @NiceGuyIT
|
||||
- #718, reported by @wtercato
|
||||
- #722, reported by @simonsmiley
|
||||
- #648, reported by @skibbipl
|
||||
- #730, reported by @ragnarkarlsson
|
||||
- #733, reported by @xpfgsyb
|
||||
- #735, reported by @kristophr
|
||||
- #739, reported by @skibbipl
|
||||
- #515, reported by @schwalberich
|
||||
- #743, reported by @simonsmiley
|
||||
- #746, reported by @tannie
|
||||
- #747, reported by @tannie
|
||||
|
||||
### Security
|
||||
- Initial release.
|
||||
|
||||
|
||||
|
||||
## [4.6.3.1] - 2017-07-23
|
||||
### Fixed
|
||||
- Hotfix to close issue #715
|
||||
|
||||
## [4.6.3] - 2017-07-23
|
||||
|
||||
This will be the last release to support PHP 7.0.
|
||||
|
||||
### Added
|
||||
- New guidelines and new introduction tour to aid new users.
|
||||
- Rules can now be applied at will to transactions, not just rule groups.
|
||||
|
||||
### Changed
|
||||
- Improved category overview.
|
||||
- Improved budget overview.
|
||||
- Improved budget report.
|
||||
- Improved command line import responsiveness and speed.
|
||||
- All code comparisons are now strict.
|
||||
- Improve search page.
|
||||
- Charts are easier to read thanks to @simonsmiley
|
||||
- Fixed #708.
|
||||
|
||||
### Fixed
|
||||
- Fixed bug where import would not respect default account. #694
|
||||
- Fixed various broken paths
|
||||
- Fixed several import inconsistencies.
|
||||
- Various bug fixes.
|
||||
|
||||
### Security
|
||||
- Initial release.
|
||||
|
||||
|
||||
|
||||
## [4.6.2] - 2017-07-08
|
||||
### Added
|
||||
- Links added to boxes, idea by @simonsmiley
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Firefly III: A personal finances manager
|
||||
|
||||
[](https://secure.php.net/downloads.php) [](https://packagist.org/packages/grumpydictator/firefly-iii) [](https://creativecommons.org/licenses/by-sa/4.0/) [](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=44UKUT455HUFA)
|
||||
[](https://secure.php.net/downloads.php) [](https://packagist.org/packages/grumpydictator/firefly-iii) [](https://creativecommons.org/licenses/by-sa/4.0/) [](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=44UKUT455HUFA)
|
||||
|
||||
[](https://i.nder.be/h2b37243) [](https://i.nder.be/hv70pbwc)
|
||||
|
||||
|
||||
@@ -14,10 +14,14 @@ declare(strict_types=1);
|
||||
namespace FireflyIII\Console\Commands;
|
||||
|
||||
use Artisan;
|
||||
use FireflyIII\Import\Logging\CommandHandler;
|
||||
use FireflyIII\Import\Routine\ImportRoutine;
|
||||
use FireflyIII\Repositories\ImportJob\ImportJobRepositoryInterface;
|
||||
use FireflyIII\Repositories\User\UserRepositoryInterface;
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Support\MessageBag;
|
||||
use Log;
|
||||
use Monolog\Formatter\LineFormatter;
|
||||
|
||||
/**
|
||||
* Class CreateImport
|
||||
@@ -51,6 +55,7 @@ class CreateImport extends Command
|
||||
|
||||
/**
|
||||
* @SuppressWarnings(PHPMD.ExcessiveMethodLength) // cannot be helped
|
||||
* @SuppressWarnings(PHPMD.CyclomaticComplexity) // it's five exactly.
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
@@ -73,30 +78,55 @@ class CreateImport extends Command
|
||||
return;
|
||||
}
|
||||
|
||||
$this->info(sprintf('Going to create a job to import file: %s', $file));
|
||||
$this->info(sprintf('Using configuration file: %s', $configuration));
|
||||
$this->info(sprintf('Import into user: #%d (%s)', $user->id, $user->email));
|
||||
$this->info(sprintf('Type of import: %s', $type));
|
||||
$this->line(sprintf('Going to create a job to import file: %s', $file));
|
||||
$this->line(sprintf('Using configuration file: %s', $configuration));
|
||||
$this->line(sprintf('Import into user: #%d (%s)', $user->id, $user->email));
|
||||
$this->line(sprintf('Type of import: %s', $type));
|
||||
|
||||
|
||||
/** @var ImportJobRepositoryInterface $jobRepository */
|
||||
$jobRepository = app(ImportJobRepositoryInterface::class);
|
||||
$jobRepository->setUser($user);
|
||||
$job = $jobRepository->create($type);
|
||||
$this->line(sprintf('Created job "%s"...', $job->key));
|
||||
$this->line(sprintf('Created job "%s"', $job->key));
|
||||
|
||||
|
||||
Artisan::call('firefly:encrypt-file', ['file' => $file, 'key' => $job->key]);
|
||||
$this->line('Stored import data...');
|
||||
|
||||
|
||||
$job->configuration = $configurationData;
|
||||
$job->status = 'configured';
|
||||
$job->save();
|
||||
$this->line('Stored configuration...');
|
||||
|
||||
|
||||
if ($this->option('start') === true) {
|
||||
$this->line('The import will start in a moment. This process is not visible...');
|
||||
Log::debug('Go for import!');
|
||||
Artisan::call('firefly:start-import', ['key' => $job->key]);
|
||||
$this->line('Done!');
|
||||
|
||||
// normally would refer to other firefly:start-import but that doesn't seem to work all to well...
|
||||
$monolog = Log::getMonolog();
|
||||
$handler = new CommandHandler($this);
|
||||
$formatter = new LineFormatter(null, null, false, true);
|
||||
$handler->setFormatter($formatter);
|
||||
$monolog->pushHandler($handler);
|
||||
|
||||
|
||||
// start the actual routine:
|
||||
/** @var ImportRoutine $routine */
|
||||
$routine = app(ImportRoutine::class);
|
||||
$routine->setJob($job);
|
||||
$routine->run();
|
||||
|
||||
// give feedback.
|
||||
/** @var MessageBag $error */
|
||||
foreach ($routine->errors as $index => $error) {
|
||||
$this->error(sprintf('Error importing line #%d: %s', $index, $error));
|
||||
}
|
||||
$this->line(
|
||||
sprintf('The import has finished. %d transactions have been imported out of %d records.', $routine->journals->count(), $routine->lines)
|
||||
);
|
||||
}
|
||||
|
||||
return;
|
||||
|
||||
104
app/Console/Commands/DecryptAttachment.php
Normal file
104
app/Console/Commands/DecryptAttachment.php
Normal file
@@ -0,0 +1,104 @@
|
||||
<?php
|
||||
/**
|
||||
* DecryptAttachment.php
|
||||
* Copyright (c) 2017 thegrumpydictator@gmail.com
|
||||
* This software may be modified and distributed under the terms of the Creative Commons Attribution-ShareAlike 4.0 International License.
|
||||
*
|
||||
* See the LICENSE file for details.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\Console\Commands;
|
||||
|
||||
use FireflyIII\Repositories\Attachment\AttachmentRepositoryInterface;
|
||||
use Illuminate\Console\Command;
|
||||
use Log;
|
||||
|
||||
/**
|
||||
* Class DecryptAttachment
|
||||
*
|
||||
* @package FireflyIII\Console\Commands
|
||||
*/
|
||||
class DecryptAttachment extends Command
|
||||
{
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Decrypts an attachment and dumps the content in a file in the given directory.';
|
||||
|
||||
/**
|
||||
* The name and signature of the console command.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $signature
|
||||
= 'firefly:decrypt-attachment {id:The ID of the attachment.} {name:The file name of the attachment.}
|
||||
{directory:Where the file must be stored.}';
|
||||
|
||||
|
||||
/**
|
||||
* Create a new command instance.
|
||||
*
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
/** @var AttachmentRepositoryInterface $repository */
|
||||
$repository = app(AttachmentRepositoryInterface::class);
|
||||
$attachmentId = intval($this->argument('id'));
|
||||
$attachment = $repository->findWithoutUser($attachmentId);
|
||||
$attachmentName = trim($this->argument('name'));
|
||||
$storagePath = realpath(trim($this->argument('directory')));
|
||||
if (is_null($attachment->id)) {
|
||||
$this->error(sprintf('No attachment with id #%d', $attachmentId));
|
||||
Log::error(sprintf('DecryptAttachment: No attachment with id #%d', $attachmentId));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if ($attachmentName !== $attachment->filename) {
|
||||
$this->error('File name does not match.');
|
||||
Log::error('DecryptAttachment: File name does not match.');
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (!is_dir($storagePath)) {
|
||||
$this->error(sprintf('Path "%s" is not a directory.', $storagePath));
|
||||
Log::error(sprintf('DecryptAttachment: Path "%s" is not a directory.', $storagePath));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (!is_writable($storagePath)) {
|
||||
$this->error(sprintf('Path "%s" is not writable.', $storagePath));
|
||||
Log::error(sprintf('DecryptAttachment: Path "%s" is not writable.', $storagePath));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$fullPath = $storagePath . DIRECTORY_SEPARATOR . $attachment->filename;
|
||||
$content = $repository->getContent($attachment);
|
||||
$this->line(sprintf('Going to write content for attachment #%d into file "%s"', $attachment->id, $fullPath));
|
||||
$result = file_put_contents($fullPath, $content);
|
||||
if ($result === false) {
|
||||
$this->error('Could not write to file.');
|
||||
|
||||
return;
|
||||
}
|
||||
$this->info(sprintf('%d bytes written. Exiting now..', $result));
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -36,6 +36,7 @@ use Steam;
|
||||
|
||||
/**
|
||||
* Class UpgradeDatabase
|
||||
* @SuppressWarnings(PHPMD.CouplingBetweenObjects) // it just touches a lot of things.
|
||||
*
|
||||
* @package FireflyIII\Console\Commands
|
||||
*/
|
||||
@@ -80,6 +81,8 @@ class UpgradeDatabase extends Command
|
||||
|
||||
/**
|
||||
* Moves the currency id info to the transaction instead of the journal.
|
||||
*
|
||||
* @SuppressWarnings(PHPMD.CyclomaticComplexity) // cannot be helped.
|
||||
*/
|
||||
private function currencyInfoToTransactions()
|
||||
{
|
||||
@@ -96,7 +99,6 @@ class UpgradeDatabase extends Command
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// read and use the foreign amounts when present.
|
||||
if ($journal->hasMeta('foreign_amount')) {
|
||||
$amount = Steam::positive($journal->getMeta('foreign_amount'));
|
||||
@@ -123,6 +125,8 @@ class UpgradeDatabase extends Command
|
||||
|
||||
/**
|
||||
* Migrate budget repetitions to new format.
|
||||
*
|
||||
* @SuppressWarnings(PHPMD.CyclomaticComplexity) // it's 5.
|
||||
*/
|
||||
private function migrateRepetitions()
|
||||
{
|
||||
@@ -150,6 +154,8 @@ class UpgradeDatabase extends Command
|
||||
|
||||
/**
|
||||
* Make sure there are only transfers linked to piggy bank events.
|
||||
*
|
||||
* @SuppressWarnings(PHPMD.CyclomaticComplexity) // cannot be helped.
|
||||
*/
|
||||
private function repairPiggyBanks()
|
||||
{
|
||||
@@ -157,6 +163,7 @@ class UpgradeDatabase extends Command
|
||||
if (!Schema::hasTable('piggy_bank_events')) {
|
||||
return;
|
||||
}
|
||||
|
||||
$set = PiggyBankEvent::with(['PiggyBank', 'TransactionJournal', 'TransactionJournal.TransactionType'])->get();
|
||||
/** @var PiggyBankEvent $event */
|
||||
foreach ($set as $event) {
|
||||
@@ -208,7 +215,7 @@ class UpgradeDatabase extends Command
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* Make sure all accounts have proper currency info.
|
||||
*/
|
||||
private function updateAccountCurrencies()
|
||||
{
|
||||
@@ -218,42 +225,38 @@ class UpgradeDatabase extends Command
|
||||
/** @var Account $account */
|
||||
foreach ($accounts as $account) {
|
||||
// get users preference, fall back to system pref.
|
||||
$defaultCurrencyCode = Preferences::getForUser($account->user, 'currencyPreference', config('firefly.default_currency', 'EUR'))->data;
|
||||
$defaultCurrency = TransactionCurrency::where('code', $defaultCurrencyCode)->first();
|
||||
$accountCurrency = intval($account->getMeta('currency_id'));
|
||||
$openingBalance = $account->getOpeningBalance();
|
||||
$openingBalanceCurrency = intval($openingBalance->transaction_currency_id);
|
||||
$defaultCurrencyCode = Preferences::getForUser($account->user, 'currencyPreference', config('firefly.default_currency', 'EUR'))->data;
|
||||
$defaultCurrency = TransactionCurrency::where('code', $defaultCurrencyCode)->first();
|
||||
$accountCurrency = intval($account->getMeta('currency_id'));
|
||||
$openingBalance = $account->getOpeningBalance();
|
||||
$obCurrency = intval($openingBalance->transaction_currency_id);
|
||||
|
||||
// both 0? set to default currency:
|
||||
if ($accountCurrency === 0 && $openingBalanceCurrency === 0) {
|
||||
if ($accountCurrency === 0 && $obCurrency === 0) {
|
||||
AccountMeta::create(['account_id' => $account->id, 'name' => 'currency_id', 'data' => $defaultCurrency->id]);
|
||||
$this->line(sprintf('Account #%d ("%s") now has a currency setting (%s).', $account->id, $account->name, $defaultCurrencyCode));
|
||||
continue;
|
||||
}
|
||||
|
||||
// opening balance 0, account not zero? just continue:
|
||||
if ($accountCurrency > 0 && $openingBalanceCurrency === 0) {
|
||||
continue;
|
||||
}
|
||||
// account is set to 0, opening balance is not?
|
||||
if ($accountCurrency === 0 && $openingBalanceCurrency > 0) {
|
||||
AccountMeta::create(['account_id' => $account->id, 'name' => 'currency_id', 'data' => $openingBalanceCurrency]);
|
||||
if ($accountCurrency === 0 && $obCurrency > 0) {
|
||||
AccountMeta::create(['account_id' => $account->id, 'name' => 'currency_id', 'data' => $obCurrency]);
|
||||
$this->line(sprintf('Account #%d ("%s") now has a currency setting (%s).', $account->id, $account->name, $defaultCurrencyCode));
|
||||
continue;
|
||||
}
|
||||
|
||||
// both are equal, just continue:
|
||||
if ($accountCurrency === $openingBalanceCurrency) {
|
||||
continue;
|
||||
}
|
||||
// do not match:
|
||||
if ($accountCurrency !== $openingBalanceCurrency) {
|
||||
if ($accountCurrency !== $obCurrency) {
|
||||
// update opening balance:
|
||||
$openingBalance->transaction_currency_id = $accountCurrency;
|
||||
$openingBalance->save();
|
||||
$this->line(sprintf('Account #%d ("%s") now has a correct currency for opening balance.', $account->id, $account->name));
|
||||
continue;
|
||||
}
|
||||
|
||||
// opening balance 0, account not zero? just continue:
|
||||
// both are equal, just continue:
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -50,10 +50,10 @@ class UpgradeFireflyInstructions extends Command
|
||||
public function handle()
|
||||
{
|
||||
|
||||
if ($this->argument('task') == 'update') {
|
||||
if ($this->argument('task') === 'update') {
|
||||
$this->updateInstructions();
|
||||
}
|
||||
if ($this->argument('task') == 'install') {
|
||||
if ($this->argument('task') === 'install') {
|
||||
$this->installInstructions();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,22 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* UseEncryption.php
|
||||
* Copyright (c) 2017 thegrumpydictator@gmail.com
|
||||
* This software may be modified and distributed under the terms of the Creative Commons Attribution-ShareAlike 4.0 International License.
|
||||
*
|
||||
* See the LICENSE file for details.
|
||||
*/
|
||||
|
||||
namespace FireflyIII\Console\Commands;
|
||||
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
/**
|
||||
* Class UseEncryption
|
||||
*/
|
||||
class UseEncryption extends Command
|
||||
{
|
||||
/**
|
||||
|
||||
@@ -259,7 +259,7 @@ class VerifyDatabase extends Command
|
||||
{
|
||||
$plural = str_plural($name);
|
||||
$class = sprintf('FireflyIII\Models\%s', ucfirst($name));
|
||||
$field = $name == 'tag' ? 'tag' : 'name';
|
||||
$field = $name === 'tag' ? 'tag' : 'name';
|
||||
$set = $class::leftJoin($name . '_transaction_journal', $plural . '.id', '=', $name . '_transaction_journal.' . $name . '_id')
|
||||
->leftJoin('users', $plural . '.user_id', '=', 'users.id')
|
||||
->distinct()
|
||||
|
||||
@@ -14,6 +14,7 @@ declare(strict_types=1);
|
||||
namespace FireflyIII\Console;
|
||||
|
||||
use FireflyIII\Console\Commands\CreateImport;
|
||||
use FireflyIII\Console\Commands\DecryptAttachment;
|
||||
use FireflyIII\Console\Commands\EncryptFile;
|
||||
use FireflyIII\Console\Commands\Import;
|
||||
use FireflyIII\Console\Commands\ScanAttachments;
|
||||
@@ -63,6 +64,7 @@ class Kernel extends ConsoleKernel
|
||||
ScanAttachments::class,
|
||||
UpgradeDatabase::class,
|
||||
UseEncryption::class,
|
||||
DecryptAttachment::class,
|
||||
];
|
||||
|
||||
/**
|
||||
|
||||
@@ -45,11 +45,11 @@ final class Entry
|
||||
|
||||
public $transaction_type;
|
||||
|
||||
public $source_account_id;
|
||||
public $source_account_name;
|
||||
public $asset_account_id;
|
||||
public $asset_account_name;
|
||||
|
||||
public $destination_account_id;
|
||||
public $destination_account_name;
|
||||
public $opposing_account_id;
|
||||
public $opposing_account_name;
|
||||
|
||||
public $budget_id;
|
||||
public $budget_name;
|
||||
@@ -71,24 +71,24 @@ final class Entry
|
||||
*/
|
||||
public static function fromObject($object): Entry
|
||||
{
|
||||
$entry = new self;
|
||||
$entry->journal_id = $object->transaction_journal_id;
|
||||
$entry->description = Steam::decrypt(intval($object->journal_encrypted), $object->journal_description);
|
||||
$entry->amount = $object->amount;
|
||||
$entry->date = $object->date;
|
||||
$entry->transaction_type = $object->transaction_type;
|
||||
$entry->currency_code = $object->transaction_currency_code;
|
||||
$entry->source_account_id = $object->account_id;
|
||||
$entry->source_account_name = Steam::decrypt(intval($object->account_name_encrypted), $object->account_name);
|
||||
$entry->destination_account_id = $object->opposing_account_id;
|
||||
$entry->destination_account_name = Steam::decrypt(intval($object->opposing_account_encrypted), $object->opposing_account_name);
|
||||
$entry->category_id = $object->category_id ?? '';
|
||||
$entry->category_name = $object->category_name ?? '';
|
||||
$entry->budget_id = $object->budget_id ?? '';
|
||||
$entry->budget_name = $object->budget_name ?? '';
|
||||
$entry = new self;
|
||||
$entry->journal_id = $object->transaction_journal_id;
|
||||
$entry->description = Steam::decrypt(intval($object->journal_encrypted), $object->journal_description);
|
||||
$entry->amount = $object->amount;
|
||||
$entry->date = $object->date;
|
||||
$entry->transaction_type = $object->transaction_type;
|
||||
$entry->currency_code = $object->transaction_currency_code;
|
||||
$entry->asset_account_id = $object->account_id;
|
||||
$entry->asset_account_name = Steam::decrypt(intval($object->account_name_encrypted), $object->account_name);
|
||||
$entry->opposing_account_id = $object->opposing_account_id;
|
||||
$entry->opposing_account_name = Steam::decrypt(intval($object->opposing_account_encrypted), $object->opposing_account_name);
|
||||
$entry->category_id = $object->category_id ?? '';
|
||||
$entry->category_name = $object->category_name ?? '';
|
||||
$entry->budget_id = $object->budget_id ?? '';
|
||||
$entry->budget_name = $object->budget_name ?? '';
|
||||
|
||||
// update description when transaction description is different:
|
||||
if (!is_null($object->description) && $object->description != $entry->description) {
|
||||
if (!is_null($object->description) && $object->description !== $entry->description) {
|
||||
$entry->description = $entry->description . ' (' . $object->description . ')';
|
||||
}
|
||||
|
||||
|
||||
@@ -110,7 +110,14 @@ class ChartJsGenerator implements GeneratorInterface
|
||||
];
|
||||
|
||||
// sort by value, keep keys.
|
||||
// different sort when values are positive and when they're negative.
|
||||
asort($data);
|
||||
$next = next($data);
|
||||
if (!is_bool($next) && bccomp($next, '0') === 1) {
|
||||
// next is positive, sort other way around.
|
||||
arsort($data);
|
||||
}
|
||||
unset($next);
|
||||
|
||||
$index = 0;
|
||||
foreach ($data as $key => $value) {
|
||||
|
||||
@@ -99,7 +99,7 @@ class AttachmentHelper implements AttachmentHelperInterface
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function saveAttachmentsForModel(Model $model, array $files = null): bool
|
||||
public function saveAttachmentsForModel(Model $model, ?array $files): bool
|
||||
{
|
||||
if (is_array($files)) {
|
||||
foreach ($files as $entry) {
|
||||
|
||||
@@ -55,6 +55,6 @@ interface AttachmentHelperInterface
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function saveAttachmentsForModel(Model $model, array $files = null): bool;
|
||||
public function saveAttachmentsForModel(Model $model, ?array $files): bool;
|
||||
|
||||
}
|
||||
|
||||
@@ -147,13 +147,13 @@ class BalanceLine
|
||||
if ($this->getBudget() instanceof BudgetModel && !is_null($this->getBudget()->id)) {
|
||||
return $this->getBudget()->name;
|
||||
}
|
||||
if ($this->getRole() == self::ROLE_DEFAULTROLE) {
|
||||
if ($this->getRole() === self::ROLE_DEFAULTROLE) {
|
||||
return strval(trans('firefly.no_budget'));
|
||||
}
|
||||
if ($this->getRole() == self::ROLE_TAGROLE) {
|
||||
if ($this->getRole() === self::ROLE_TAGROLE) {
|
||||
return strval(trans('firefly.coveredWithTags'));
|
||||
}
|
||||
if ($this->getRole() == self::ROLE_DIFFROLE) {
|
||||
if ($this->getRole() === self::ROLE_DIFFROLE) {
|
||||
return strval(trans('firefly.leftUnbalanced'));
|
||||
}
|
||||
|
||||
|
||||
@@ -96,7 +96,7 @@ class Bill
|
||||
{
|
||||
$set = $this->bills->sortBy(
|
||||
function (BillLine $bill) {
|
||||
$active = intval($bill->getBill()->active) == 0 ? 1 : 0;
|
||||
$active = intval($bill->getBill()->active) === 0 ? 1 : 0;
|
||||
$name = $bill->getBill()->name;
|
||||
|
||||
return $active . $name;
|
||||
|
||||
@@ -64,22 +64,22 @@ class JournalCollector implements JournalCollectorInterface
|
||||
'transaction_journals.bill_id',
|
||||
'bills.name as bill_name',
|
||||
'bills.name_encrypted as bill_name_encrypted',
|
||||
'transactions.id as id',
|
||||
|
||||
'transactions.id as id',
|
||||
'transactions.description as transaction_description',
|
||||
'transactions.account_id',
|
||||
'transactions.identifier',
|
||||
'transactions.transaction_journal_id',
|
||||
|
||||
'transactions.amount as transaction_amount',
|
||||
|
||||
'transactions.transaction_currency_id as transaction_currency_id',
|
||||
|
||||
'transaction_currencies.code as transaction_currency_code',
|
||||
'transaction_currencies.symbol as transaction_currency_symbol',
|
||||
'transaction_currencies.decimal_places as transaction_currency_dp',
|
||||
|
||||
'transactions.foreign_amount as transaction_foreign_amount',
|
||||
'transactions.foreign_currency_id as foreign_currency_id',
|
||||
|
||||
'foreign_currencies.code as foreign_currency_code',
|
||||
'foreign_currencies.symbol as foreign_currency_symbol',
|
||||
'foreign_currencies.decimal_places as foreign_currency_dp',
|
||||
@@ -89,8 +89,6 @@ class JournalCollector implements JournalCollectorInterface
|
||||
'account_types.type as account_type',
|
||||
];
|
||||
/** @var bool */
|
||||
private $filterInternalTransfers;
|
||||
/** @var bool */
|
||||
private $filterTransfers = false;
|
||||
/** @var array */
|
||||
private $filters = [InternalTransferFilter::class];
|
||||
@@ -401,6 +399,10 @@ class JournalCollector implements JournalCollectorInterface
|
||||
*/
|
||||
public function setPage(int $page): JournalCollectorInterface
|
||||
{
|
||||
if ($page < 1) {
|
||||
$page = 1;
|
||||
}
|
||||
|
||||
$this->page = $page;
|
||||
|
||||
if ($page > 0) {
|
||||
@@ -508,7 +510,8 @@ class JournalCollector implements JournalCollectorInterface
|
||||
->where('transaction_journals.user_id', $this->user->id)
|
||||
->orderBy('transaction_journals.date', 'DESC')
|
||||
->orderBy('transaction_journals.order', 'ASC')
|
||||
->orderBy('transaction_journals.id', 'DESC');
|
||||
->orderBy('transaction_journals.id', 'DESC')
|
||||
->orderBy('transaction_journals.description', 'DESC');
|
||||
|
||||
$this->query = $query;
|
||||
|
||||
@@ -674,6 +677,7 @@ class JournalCollector implements JournalCollectorInterface
|
||||
$this->query->leftJoin('account_types as opposing_account_types', 'opposing_accounts.account_type_id', '=', 'opposing_account_types.id');
|
||||
$this->query->whereNull('opposing.deleted_at');
|
||||
|
||||
$this->fields[] = 'opposing.id as opposing_id';
|
||||
$this->fields[] = 'opposing.account_id as opposing_account_id';
|
||||
$this->fields[] = 'opposing_accounts.name as opposing_account_name';
|
||||
$this->fields[] = 'opposing_accounts.encrypted as opposing_account_encrypted';
|
||||
|
||||
@@ -41,11 +41,13 @@ class TransferFilter implements FilterInterface
|
||||
continue;
|
||||
}
|
||||
// make property string:
|
||||
$journalId = $transaction->transaction_journal_id;
|
||||
$amount = Steam::positive($transaction->transaction_amount);
|
||||
$accountIds = [intval($transaction->account_id), intval($transaction->opposing_account_id)];
|
||||
$journalId = $transaction->transaction_journal_id;
|
||||
$amount = Steam::positive($transaction->transaction_amount);
|
||||
$accountIds = [intval($transaction->account_id), intval($transaction->opposing_account_id)];
|
||||
$transactionIds = [$transaction->id, intval($transaction->opposing_id)];
|
||||
sort($accountIds);
|
||||
$key = $journalId . '-' . join(',', $accountIds) . '-' . $amount;
|
||||
sort($transactionIds);
|
||||
$key = $journalId . '-' . join(',', $transactionIds) . '-' . join(',', $accountIds) . '-' . $amount;
|
||||
if (!isset($count[$key])) {
|
||||
// not yet counted? add to new set and count it:
|
||||
$new->push($transaction);
|
||||
|
||||
@@ -27,6 +27,7 @@ use Route;
|
||||
*/
|
||||
class Help implements HelpInterface
|
||||
{
|
||||
const CACHEKEY = 'help_%s_%s';
|
||||
/** @var string */
|
||||
protected $userAgent = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.71 Safari/537.36';
|
||||
|
||||
@@ -38,7 +39,7 @@ class Help implements HelpInterface
|
||||
*/
|
||||
public function getFromCache(string $route, string $language): string
|
||||
{
|
||||
$line = sprintf('help.%s.%s', $route, $language);
|
||||
$line = sprintf(self::CACHEKEY, $route, $language);
|
||||
|
||||
return Cache::get($line);
|
||||
}
|
||||
@@ -64,12 +65,12 @@ class Help implements HelpInterface
|
||||
return '';
|
||||
}
|
||||
|
||||
|
||||
Log::debug(sprintf('Status code is %d', $result->status_code));
|
||||
|
||||
if ($result->status_code === 200) {
|
||||
$content = trim($result->body);
|
||||
}
|
||||
|
||||
if (strlen($content) > 0) {
|
||||
Log::debug('Content is longer than zero. Expect something.');
|
||||
$converter = new CommonMarkConverter();
|
||||
@@ -98,7 +99,7 @@ class Help implements HelpInterface
|
||||
*/
|
||||
public function inCache(string $route, string $language): bool
|
||||
{
|
||||
$line = sprintf('help.%s.%s', $route, $language);
|
||||
$line = sprintf(self::CACHEKEY, $route, $language);
|
||||
$result = Cache::has($line);
|
||||
if ($result) {
|
||||
Log::debug(sprintf('Cache has this entry: %s', 'help.' . $route . '.' . $language));
|
||||
@@ -120,7 +121,7 @@ class Help implements HelpInterface
|
||||
*/
|
||||
public function putInCache(string $route, string $language, string $content)
|
||||
{
|
||||
$key = sprintf('help.%s.%s', $route, $language);
|
||||
$key = sprintf(self::CACHEKEY, $route, $language);
|
||||
if (strlen($content) > 0) {
|
||||
Log::debug(sprintf('Will store entry in cache: %s', $key));
|
||||
Cache::put($key, $content, 10080); // a week.
|
||||
|
||||
@@ -244,7 +244,7 @@ class BalanceReportHelper implements BalanceReportHelperInterface
|
||||
foreach ($accounts as $account) {
|
||||
$leftEntry = $tagsLeft->filter(
|
||||
function (Tag $tag) use ($account) {
|
||||
return $tag->account_id == $account->id;
|
||||
return $tag->account_id === $account->id;
|
||||
}
|
||||
);
|
||||
$left = '0';
|
||||
|
||||
@@ -56,7 +56,7 @@ class BudgetReportHelper implements BudgetReportHelperInterface
|
||||
/** @var Budget $budget */
|
||||
foreach ($set as $budget) {
|
||||
$budgetLimits = $this->repository->getBudgetLimits($budget, $start, $end);
|
||||
if ($budgetLimits->count() == 0) { // no budget limit(s) for this budget
|
||||
if ($budgetLimits->count() === 0) { // no budget limit(s) for this budget
|
||||
|
||||
$spent = $this->repository->spentInPeriod(new Collection([$budget]), $accounts, $start, $end);// spent for budget in time range
|
||||
if (bccomp($spent, '0') === -1) {
|
||||
|
||||
@@ -194,7 +194,7 @@ class AccountController extends Controller
|
||||
|
||||
return view(
|
||||
'accounts.edit', compact(
|
||||
'allCurrencies', 'currencySelectList', 'account', 'currency', 'subTitle', 'subTitleIcon', 'openingBalance', 'what', 'roles'
|
||||
'allCurrencies', 'currencySelectList', 'account', 'currency', 'subTitle', 'subTitleIcon', 'what', 'roles'
|
||||
)
|
||||
);
|
||||
}
|
||||
@@ -253,7 +253,7 @@ class AccountController extends Controller
|
||||
$currencyRepos = app(CurrencyRepositoryInterface::class);
|
||||
$range = Preferences::get('viewRange', '1M')->data;
|
||||
$subTitleIcon = config('firefly.subIconsByIdentifier.' . $account->accountType->type);
|
||||
$page = intval($request->get('page')) === 0 ? 1 : intval($request->get('page'));
|
||||
$page = intval($request->get('page'));
|
||||
$pageSize = intval(Preferences::get('transactionPageSize', 50)->data);
|
||||
$chartUri = route('chart.account.single', [$account->id]);
|
||||
$start = null;
|
||||
@@ -274,55 +274,31 @@ class AccountController extends Controller
|
||||
if (strlen($moment) > 0 && $moment !== 'all') {
|
||||
$start = new Carbon($moment);
|
||||
$end = Navigation::endOfPeriod($start, $range);
|
||||
$subTitle = trans(
|
||||
'firefly.journals_in_period_for_account', ['name' => $account->name, 'start' => $start->formatLocalized($this->monthAndDayFormat),
|
||||
'end' => $end->formatLocalized($this->monthAndDayFormat)]
|
||||
);
|
||||
$fStart = $start->formatLocalized($this->monthAndDayFormat);
|
||||
$fEnd = $end->formatLocalized($this->monthAndDayFormat);
|
||||
$subTitle = trans('firefly.journals_in_period_for_account', ['name' => $account->name, 'start' => $fStart, 'end' => $fEnd]);
|
||||
$chartUri = route('chart.account.period', [$account->id, $start->format('Y-m-d')]);
|
||||
$periods = $this->getPeriodOverview($account);
|
||||
}
|
||||
|
||||
// prep for current period
|
||||
// prep for current period view
|
||||
if (strlen($moment) === 0) {
|
||||
$start = clone session('start', Navigation::startOfPeriod(new Carbon, $range));
|
||||
$end = clone session('end', Navigation::endOfPeriod(new Carbon, $range));
|
||||
$subTitle = trans(
|
||||
'firefly.journals_in_period_for_account', ['name' => $account->name, 'start' => $start->formatLocalized($this->monthAndDayFormat),
|
||||
'end' => $end->formatLocalized($this->monthAndDayFormat)]
|
||||
);
|
||||
$fStart = $start->formatLocalized($this->monthAndDayFormat);
|
||||
$fEnd = $end->formatLocalized($this->monthAndDayFormat);
|
||||
$subTitle = trans('firefly.journals_in_period_for_account', ['name' => $account->name, 'start' => $fStart, 'end' => $fEnd]);
|
||||
$periods = $this->getPeriodOverview($account);
|
||||
}
|
||||
|
||||
$count = 0;
|
||||
$loop = 0;
|
||||
// grab journals, but be prepared to jump a period back to get the right ones:
|
||||
Log::info('Now at loop start.');
|
||||
while ($count === 0 && $loop < 3) {
|
||||
$loop++;
|
||||
$collector = app(JournalCollectorInterface::class);
|
||||
Log::info('Count is zero, search for journals.');
|
||||
$collector->setAccounts(new Collection([$account]))->setLimit($pageSize)->setPage($page);
|
||||
if (!is_null($start)) {
|
||||
$collector->setRange($start, $end);
|
||||
}
|
||||
$journals = $collector->getPaginatedJournals();
|
||||
$journals->setPath('accounts/show/' . $account->id . '/' . $moment);
|
||||
$count = $journals->getCollection()->count();
|
||||
if ($count === 0 && $loop < 3) {
|
||||
$start->subDay();
|
||||
$start = Navigation::startOfPeriod($start, $range);
|
||||
$end = Navigation::endOfPeriod($start, $range);
|
||||
Log::info(sprintf('Count is still zero, go back in time to "%s" and "%s"!', $start->format('Y-m-d'), $end->format('Y-m-d')));
|
||||
}
|
||||
// grab journals:
|
||||
$collector = app(JournalCollectorInterface::class);
|
||||
$collector->setAccounts(new Collection([$account]))->setLimit($pageSize)->setPage($page);
|
||||
if (!is_null($start)) {
|
||||
$collector->setRange($start, $end);
|
||||
}
|
||||
|
||||
if ($moment != 'all' && $loop > 1) {
|
||||
$subTitle = trans(
|
||||
'firefly.journals_in_period_for_account', ['name' => $account->name, 'start' => $start->formatLocalized($this->monthAndDayFormat),
|
||||
'end' => $end->formatLocalized($this->monthAndDayFormat)]
|
||||
);
|
||||
}
|
||||
|
||||
$journals = $collector->getPaginatedJournals();
|
||||
$journals->setPath(route('accounts.show', [$account->id, $moment]));
|
||||
|
||||
return view(
|
||||
'accounts.show',
|
||||
@@ -421,7 +397,7 @@ class AccountController extends Controller
|
||||
$start = $repository->oldestJournalDate($account);
|
||||
$range = Preferences::get('viewRange', '1M')->data;
|
||||
$start = Navigation::startOfPeriod($start, $range);
|
||||
$end = Navigation::endOfX(new Carbon, $range);
|
||||
$end = Navigation::endOfX(new Carbon, $range, null);
|
||||
$entries = new Collection;
|
||||
|
||||
// properties for cache
|
||||
|
||||
@@ -153,7 +153,7 @@ class AttachmentController extends Controller
|
||||
$image = 'images/page_green.png';
|
||||
|
||||
|
||||
if ($attachment->mime == 'application/pdf') {
|
||||
if ($attachment->mime === 'application/pdf') {
|
||||
$image = 'images/page_white_acrobat.png';
|
||||
}
|
||||
$file = public_path($image);
|
||||
|
||||
@@ -17,6 +17,7 @@ use Config;
|
||||
use FireflyConfig;
|
||||
use FireflyIII\Events\RegisteredUser;
|
||||
use FireflyIII\Http\Controllers\Controller;
|
||||
use FireflyIII\Http\Requests\UserRegistrationRequest;
|
||||
use FireflyIII\User;
|
||||
use Illuminate\Foundation\Auth\RegistersUsers;
|
||||
use Illuminate\Http\Request;
|
||||
@@ -52,11 +53,11 @@ class RegisterController extends Controller
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Request $request
|
||||
* @param UserRegistrationRequest|Request $request
|
||||
*
|
||||
* @return \Illuminate\Contracts\View\Factory|\Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector|\Illuminate\View\View
|
||||
*/
|
||||
public function register(Request $request)
|
||||
public function register(UserRegistrationRequest $request)
|
||||
{
|
||||
// is allowed to?
|
||||
$singleUserMode = FireflyConfig::get('single_user_mode', Config::get('firefly.configuration.single_user_mode'))->data;
|
||||
|
||||
@@ -175,7 +175,7 @@ class BillController extends Controller
|
||||
*/
|
||||
public function rescan(Request $request, BillRepositoryInterface $repository, Bill $bill)
|
||||
{
|
||||
if (intval($bill->active) == 0) {
|
||||
if (intval($bill->active) === 0) {
|
||||
$request->session()->flash('warning', strval(trans('firefly.cannot_scan_inactive_bill')));
|
||||
|
||||
return redirect(URL::previous());
|
||||
@@ -206,7 +206,7 @@ class BillController extends Controller
|
||||
/** @var Carbon $date */
|
||||
$date = session('start');
|
||||
$year = $date->year;
|
||||
$page = intval($request->get('page')) == 0 ? 1 : intval($request->get('page'));
|
||||
$page = intval($request->get('page'));
|
||||
$pageSize = intval(Preferences::get('transactionPageSize', 50)->data);
|
||||
$yearAverage = $repository->getYearAverage($bill, $date);
|
||||
$overallAverage = $repository->getOverallAverage($bill);
|
||||
@@ -217,7 +217,7 @@ class BillController extends Controller
|
||||
$collector->setAllAssetAccounts()->setBills(new Collection([$bill]))->setLimit($pageSize)->setPage($page)->withBudgetInformation()
|
||||
->withCategoryInformation();
|
||||
$journals = $collector->getPaginatedJournals();
|
||||
$journals->setPath('/bills/show/' . $bill->id);
|
||||
$journals->setPath(route('bills.show', [$bill->id]));
|
||||
|
||||
$bill->nextExpectedMatch = $repository->nextExpectedMatch($bill, new Carbon);
|
||||
$hideBill = true;
|
||||
|
||||
@@ -81,7 +81,7 @@ class BudgetController extends Controller
|
||||
/** @var Carbon $end */
|
||||
$end = session('end', Carbon::now()->endOfMonth());
|
||||
$budgetLimit = $this->repository->updateLimitAmount($budget, $start, $end, $amount);
|
||||
if ($amount == 0) {
|
||||
if ($amount === 0) {
|
||||
$budgetLimit = null;
|
||||
}
|
||||
Preferences::mark();
|
||||
@@ -293,37 +293,15 @@ class BudgetController extends Controller
|
||||
);
|
||||
}
|
||||
|
||||
$page = intval($request->get('page')) == 0 ? 1 : intval($request->get('page'));
|
||||
$page = intval($request->get('page'));
|
||||
$pageSize = intval(Preferences::get('transactionPageSize', 50)->data);
|
||||
|
||||
$count = 0;
|
||||
$loop = 0;
|
||||
// grab journals, but be prepared to jump a period back to get the right ones:
|
||||
Log::info('Now at no-budget loop start.');
|
||||
while ($count === 0 && $loop < 3) {
|
||||
$loop++;
|
||||
Log::info(sprintf('Count is zero, search for journals between %s and %s.', $start->format('Y-m-d'), $end->format('Y-m-d')));
|
||||
/** @var JournalCollectorInterface $collector */
|
||||
$collector = app(JournalCollectorInterface::class);
|
||||
$collector->setAllAssetAccounts()->setRange($start, $end)->setTypes([TransactionType::WITHDRAWAL])->setLimit($pageSize)->setPage($page)
|
||||
->withoutBudget()->withOpposingAccount();
|
||||
$journals = $collector->getPaginatedJournals();
|
||||
$journals->setPath('/budgets/list/no-budget');
|
||||
$count = $journals->getCollection()->count();
|
||||
if ($count === 0 && $loop < 3) {
|
||||
$start->subDay();
|
||||
$start = Navigation::startOfPeriod($start, $range);
|
||||
$end = Navigation::endOfPeriod($start, $range);
|
||||
Log::info(sprintf('Count is still zero, go back in time to "%s" and "%s"!', $start->format('Y-m-d'), $end->format('Y-m-d')));
|
||||
}
|
||||
}
|
||||
|
||||
if ($moment != 'all' && $loop > 1) {
|
||||
$subTitle = trans(
|
||||
'firefly.without_budget_between',
|
||||
['start' => $start->formatLocalized($this->monthAndDayFormat), 'end' => $end->formatLocalized($this->monthAndDayFormat)]
|
||||
);
|
||||
}
|
||||
/** @var JournalCollectorInterface $collector */
|
||||
$collector = app(JournalCollectorInterface::class);
|
||||
$collector->setAllAssetAccounts()->setRange($start, $end)->setTypes([TransactionType::WITHDRAWAL])->setLimit($pageSize)->setPage($page)
|
||||
->withoutBudget()->withOpposingAccount();
|
||||
$journals = $collector->getPaginatedJournals();
|
||||
$journals->setPath(route('budgets.no-budget'));
|
||||
|
||||
return view('budgets.no-budget', compact('journals', 'subTitle', 'moment', 'periods', 'start', 'end'));
|
||||
}
|
||||
@@ -357,7 +335,7 @@ class BudgetController extends Controller
|
||||
/** @var Carbon $start */
|
||||
$start = session('first', Carbon::create()->startOfYear());
|
||||
$end = new Carbon;
|
||||
$page = intval($request->get('page')) == 0 ? 1 : intval($request->get('page'));
|
||||
$page = intval($request->get('page'));
|
||||
$pageSize = intval(Preferences::get('transactionPageSize', 50)->data);
|
||||
$limits = $this->getLimits($budget, $start, $end);
|
||||
$repetition = null;
|
||||
@@ -366,7 +344,7 @@ class BudgetController extends Controller
|
||||
$collector = app(JournalCollectorInterface::class);
|
||||
$collector->setAllAssetAccounts()->setRange($start, $end)->setBudget($budget)->setLimit($pageSize)->setPage($page)->withCategoryInformation();
|
||||
$journals = $collector->getPaginatedJournals();
|
||||
$journals->setPath('/budgets/show/' . $budget->id);
|
||||
$journals->setPath(route('budgets.show', [$budget->id]));
|
||||
|
||||
|
||||
$subTitle = trans('firefly.all_journals_for_budget', ['name' => $budget->name]);
|
||||
@@ -384,11 +362,11 @@ class BudgetController extends Controller
|
||||
*/
|
||||
public function showByBudgetLimit(Request $request, Budget $budget, BudgetLimit $budgetLimit)
|
||||
{
|
||||
if ($budgetLimit->budget->id != $budget->id) {
|
||||
if ($budgetLimit->budget->id !== $budget->id) {
|
||||
throw new FireflyException('This budget limit is not part of this budget.');
|
||||
}
|
||||
|
||||
$page = intval($request->get('page')) == 0 ? 1 : intval($request->get('page'));
|
||||
$page = intval($request->get('page'));
|
||||
$pageSize = intval(Preferences::get('transactionPageSize', 50)->data);
|
||||
$subTitle = trans(
|
||||
'firefly.budget_in_period', [
|
||||
@@ -404,7 +382,7 @@ class BudgetController extends Controller
|
||||
$collector->setAllAssetAccounts()->setRange($budgetLimit->start_date, $budgetLimit->end_date)
|
||||
->setBudget($budget)->setLimit($pageSize)->setPage($page)->withCategoryInformation();
|
||||
$journals = $collector->getPaginatedJournals();
|
||||
$journals->setPath('/budgets/show/' . $budget->id . '/' . $budgetLimit->id);
|
||||
$journals->setPath(route('budgets.show', [$budget->id, $budgetLimit->id]));
|
||||
|
||||
|
||||
$start = session('first', Carbon::create()->startOfYear());
|
||||
@@ -567,7 +545,7 @@ class BudgetController extends Controller
|
||||
$start = $first->date ?? new Carbon;
|
||||
$range = Preferences::get('viewRange', '1M')->data;
|
||||
$start = Navigation::startOfPeriod($start, $range);
|
||||
$end = Navigation::endOfX(new Carbon, $range);
|
||||
$end = Navigation::endOfX(new Carbon, $range, null);
|
||||
$entries = new Collection;
|
||||
|
||||
// properties for cache
|
||||
|
||||
@@ -164,10 +164,12 @@ class CategoryController extends Controller
|
||||
public function noCategory(Request $request, JournalRepositoryInterface $repository, string $moment = '')
|
||||
{
|
||||
// default values:
|
||||
$range = Preferences::get('viewRange', '1M')->data;
|
||||
$start = null;
|
||||
$end = null;
|
||||
$periods = new Collection;
|
||||
$range = Preferences::get('viewRange', '1M')->data;
|
||||
$start = null;
|
||||
$end = null;
|
||||
$periods = new Collection;
|
||||
$page = intval($request->get('page'));
|
||||
$pageSize = intval(Preferences::get('transactionPageSize', 50)->data);
|
||||
|
||||
// prep for "all" view.
|
||||
if ($moment === 'all') {
|
||||
@@ -199,37 +201,13 @@ class CategoryController extends Controller
|
||||
);
|
||||
}
|
||||
|
||||
$page = intval($request->get('page')) == 0 ? 1 : intval($request->get('page'));
|
||||
$pageSize = intval(Preferences::get('transactionPageSize', 50)->data);
|
||||
|
||||
$count = 0;
|
||||
$loop = 0;
|
||||
// grab journals, but be prepared to jump a period back to get the right ones:
|
||||
Log::info('Now at no-cat loop start.');
|
||||
while ($count === 0 && $loop < 3) {
|
||||
$loop++;
|
||||
Log::info('Count is zero, search for journals.');
|
||||
/** @var JournalCollectorInterface $collector */
|
||||
$collector = app(JournalCollectorInterface::class);
|
||||
$collector->setAllAssetAccounts()->setRange($start, $end)->setLimit($pageSize)->setPage($page)->withoutCategory()->withOpposingAccount();
|
||||
$collector->removeFilter(InternalTransferFilter::class);
|
||||
$journals = $collector->getPaginatedJournals();
|
||||
$journals->setPath('/categories/list/no-category');
|
||||
$count = $journals->getCollection()->count();
|
||||
if ($count === 0 && $loop < 3) {
|
||||
$start->subDay();
|
||||
$start = Navigation::startOfPeriod($start, $range);
|
||||
$end = Navigation::endOfPeriod($start, $range);
|
||||
Log::info(sprintf('Count is still zero, go back in time to "%s" and "%s"!', $start->format('Y-m-d'), $end->format('Y-m-d')));
|
||||
}
|
||||
}
|
||||
|
||||
if ($moment != 'all' && $loop > 1) {
|
||||
$subTitle = trans(
|
||||
'firefly.without_category_between',
|
||||
['start' => $start->formatLocalized($this->monthAndDayFormat), 'end' => $end->formatLocalized($this->monthAndDayFormat)]
|
||||
);
|
||||
}
|
||||
/** @var JournalCollectorInterface $collector */
|
||||
$collector = app(JournalCollectorInterface::class);
|
||||
$collector->setAllAssetAccounts()->setRange($start, $end)->setLimit($pageSize)->setPage($page)->withoutCategory()->withOpposingAccount();
|
||||
$collector->removeFilter(InternalTransferFilter::class);
|
||||
$journals = $collector->getPaginatedJournals();
|
||||
$journals->setPath(route('categories.no-category'));
|
||||
|
||||
return view('categories.no-category', compact('journals', 'subTitle', 'moment', 'periods', 'start', 'end'));
|
||||
}
|
||||
@@ -247,10 +225,8 @@ class CategoryController extends Controller
|
||||
// default values:
|
||||
$subTitle = $category->name;
|
||||
$subTitleIcon = 'fa-bar-chart';
|
||||
$page = intval($request->get('page')) == 0 ? 1 : intval($request->get('page'));
|
||||
$page = intval($request->get('page'));
|
||||
$pageSize = intval(Preferences::get('transactionPageSize', 50)->data);
|
||||
$count = 0;
|
||||
$loop = 0;
|
||||
$range = Preferences::get('viewRange', '1M')->data;
|
||||
$start = null;
|
||||
$end = null;
|
||||
@@ -287,34 +263,15 @@ class CategoryController extends Controller
|
||||
'end' => $end->formatLocalized($this->monthAndDayFormat)]
|
||||
);
|
||||
}
|
||||
// grab journals, but be prepared to jump a period back to get the right ones:
|
||||
Log::info('Now at category loop start.');
|
||||
while ($count === 0 && $loop < 3) {
|
||||
$loop++;
|
||||
Log::info('Count is zero, search for journals.');
|
||||
/** @var JournalCollectorInterface $collector */
|
||||
$collector = app(JournalCollectorInterface::class);
|
||||
$collector->setAllAssetAccounts()->setRange($start, $end)->setLimit($pageSize)->setPage($page)->withOpposingAccount()
|
||||
->setCategory($category)->withBudgetInformation()->withCategoryInformation();
|
||||
$collector->removeFilter(InternalTransferFilter::class);
|
||||
$journals = $collector->getPaginatedJournals();
|
||||
$journals->setPath('categories/show/' . $category->id);
|
||||
$count = $journals->getCollection()->count();
|
||||
if ($count === 0 && $loop < 3) {
|
||||
$start->subDay();
|
||||
$start = Navigation::startOfPeriod($start, $range);
|
||||
$end = Navigation::endOfPeriod($start, $range);
|
||||
Log::info(sprintf('Count is still zero, go back in time to "%s" and "%s"!', $start->format('Y-m-d'), $end->format('Y-m-d')));
|
||||
}
|
||||
}
|
||||
|
||||
if ($moment != 'all' && $loop > 1) {
|
||||
$subTitle = trans(
|
||||
'firefly.journals_in_period_for_category',
|
||||
['name' => $category->name, 'start' => $start->formatLocalized($this->monthAndDayFormat),
|
||||
'end' => $end->formatLocalized($this->monthAndDayFormat)]
|
||||
);
|
||||
}
|
||||
/** @var JournalCollectorInterface $collector */
|
||||
$collector = app(JournalCollectorInterface::class);
|
||||
$collector->setAllAssetAccounts()->setRange($start, $end)->setLimit($pageSize)->setPage($page)->withOpposingAccount()
|
||||
->setCategory($category)->withBudgetInformation()->withCategoryInformation();
|
||||
$collector->removeFilter(InternalTransferFilter::class);
|
||||
$journals = $collector->getPaginatedJournals();
|
||||
$journals->setPath(route('categories.show', [$category->id]));
|
||||
|
||||
|
||||
return view('categories.show', compact('category', 'moment', 'journals', 'periods', 'subTitle', 'subTitleIcon', 'start', 'end'));
|
||||
}
|
||||
@@ -382,7 +339,7 @@ class CategoryController extends Controller
|
||||
$start = $first->date ?? new Carbon;
|
||||
$range = Preferences::get('viewRange', '1M')->data;
|
||||
$start = Navigation::startOfPeriod($start, $range);
|
||||
$end = Navigation::endOfX(new Carbon, $range);
|
||||
$end = Navigation::endOfX(new Carbon, $range, null);
|
||||
$entries = new Collection;
|
||||
|
||||
// properties for cache
|
||||
@@ -463,12 +420,12 @@ class CategoryController extends Controller
|
||||
$accountRepository = app(AccountRepositoryInterface::class);
|
||||
$accounts = $accountRepository->getAccountsByType([AccountType::DEFAULT, AccountType::ASSET]);
|
||||
$first = $repository->firstUseDate($category);
|
||||
if ($first->year == 1900) {
|
||||
if ($first->year === 1900) {
|
||||
$first = new Carbon;
|
||||
}
|
||||
$range = Preferences::get('viewRange', '1M')->data;
|
||||
$first = Navigation::startOfPeriod($first, $range);
|
||||
$end = Navigation::endOfX(new Carbon, $range);
|
||||
$end = Navigation::endOfX(new Carbon, $range, null);
|
||||
$entries = new Collection;
|
||||
|
||||
// properties for entries with their amounts.
|
||||
|
||||
@@ -124,7 +124,7 @@ class BudgetController extends Controller
|
||||
*/
|
||||
public function budgetLimit(Budget $budget, BudgetLimit $budgetLimit)
|
||||
{
|
||||
if ($budgetLimit->budget->id != $budget->id) {
|
||||
if ($budgetLimit->budget->id !== $budget->id) {
|
||||
throw new FireflyException('This budget limit is not part of this budget.');
|
||||
}
|
||||
|
||||
@@ -163,7 +163,7 @@ class BudgetController extends Controller
|
||||
*
|
||||
* @return \Illuminate\Http\JsonResponse
|
||||
*/
|
||||
public function expenseAsset(Budget $budget, BudgetLimit $budgetLimit = null)
|
||||
public function expenseAsset(Budget $budget, ?BudgetLimit $budgetLimit)
|
||||
{
|
||||
$cache = new CacheProperties;
|
||||
$cache->addProperty($budget->id);
|
||||
@@ -208,7 +208,7 @@ class BudgetController extends Controller
|
||||
*
|
||||
* @return \Illuminate\Http\JsonResponse
|
||||
*/
|
||||
public function expenseCategory(Budget $budget, BudgetLimit $budgetLimit = null)
|
||||
public function expenseCategory(Budget $budget, ?BudgetLimit $budgetLimit)
|
||||
{
|
||||
$cache = new CacheProperties;
|
||||
$cache->addProperty($budget->id);
|
||||
@@ -255,7 +255,7 @@ class BudgetController extends Controller
|
||||
*
|
||||
* @return \Illuminate\Http\JsonResponse
|
||||
*/
|
||||
public function expenseExpense(Budget $budget, BudgetLimit $budgetLimit = null)
|
||||
public function expenseExpense(Budget $budget, ?BudgetLimit $budgetLimit)
|
||||
{
|
||||
$cache = new CacheProperties;
|
||||
$cache->addProperty($budget->id);
|
||||
|
||||
@@ -67,7 +67,7 @@ class CategoryController extends Controller
|
||||
|
||||
$start = $repository->firstUseDate($category);
|
||||
|
||||
if ($start->year == 1900) {
|
||||
if ($start->year === 1900) {
|
||||
$start = new Carbon;
|
||||
}
|
||||
|
||||
|
||||
@@ -247,7 +247,7 @@ class CategoryReportController extends Controller
|
||||
// remove all empty entries to prevent cluttering:
|
||||
$newSet = [];
|
||||
foreach ($chartData as $key => $entry) {
|
||||
if (!array_sum($entry['entries']) == 0) {
|
||||
if (!array_sum($entry['entries']) === 0) {
|
||||
$newSet[$key] = $chartData[$key];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -231,7 +231,7 @@ class TagReportController extends Controller
|
||||
// remove all empty entries to prevent cluttering:
|
||||
$newSet = [];
|
||||
foreach ($chartData as $key => $entry) {
|
||||
if (!array_sum($entry['entries']) == 0) {
|
||||
if (!array_sum($entry['entries']) === 0) {
|
||||
$newSet[$key] = $chartData[$key];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,10 +18,13 @@ use FireflyIII\Models\AccountType;
|
||||
use FireflyIII\Models\Transaction;
|
||||
use FireflyIII\Models\TransactionJournal;
|
||||
use FireflyIII\Models\TransactionType;
|
||||
use FireflyIII\Support\Facades\Preferences;
|
||||
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
|
||||
use Illuminate\Foundation\Bus\DispatchesJobs;
|
||||
use Illuminate\Foundation\Validation\ValidatesRequests;
|
||||
use Illuminate\Routing\Controller as BaseController;
|
||||
use Log;
|
||||
use Route;
|
||||
use Session;
|
||||
use URL;
|
||||
use View;
|
||||
@@ -47,22 +50,50 @@ class Controller extends BaseController
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
// for transaction lists:
|
||||
View::share('hideBudgets', false);
|
||||
View::share('hideCategories', false);
|
||||
View::share('hideBills', false);
|
||||
View::share('hideTags', false);
|
||||
|
||||
// is site a demo site?
|
||||
$isDemoSite = FireflyConfig::get('is_demo_site', config('firefly.configuration.is_demo_site'))->data;
|
||||
View::share('IS_DEMO_SITE', $isDemoSite);
|
||||
View::share('DEMO_USERNAME', env('DEMO_USERNAME', ''));
|
||||
View::share('DEMO_PASSWORD', env('DEMO_PASSWORD', ''));
|
||||
|
||||
// translations:
|
||||
|
||||
$this->middleware(
|
||||
function ($request, $next) {
|
||||
// translations for specific strings:
|
||||
$this->monthFormat = (string)trans('config.month');
|
||||
$this->monthAndDayFormat = (string)trans('config.month_and_day');
|
||||
$this->dateTimeFormat = (string)trans('config.date_time');
|
||||
|
||||
// get shown-intro-preference:
|
||||
if (auth()->check()) {
|
||||
// some routes have a "what" parameter, which indicates a special page:
|
||||
$specificPage = is_null(Route::current()->parameter('what')) ? '' : '_' . Route::current()->parameter('what');
|
||||
$page = str_replace('.', '_', Route::currentRouteName());
|
||||
|
||||
// indicator if user has seen the help for this page ( + special page):
|
||||
$key = 'shown_demo_' . $page . $specificPage;
|
||||
// is there an intro for this route?
|
||||
$intro = config('intro.' . $page);
|
||||
$specialIntro = config('intro.' . $page . $specificPage);
|
||||
$shownDemo = true;
|
||||
|
||||
// either must be array and either must be > 0
|
||||
if ((is_array($intro) || is_array($specialIntro)) && (count($intro) > 0 || count($specialIntro) > 0)) {
|
||||
$shownDemo = Preferences::get($key, false)->data;
|
||||
Log::debug(sprintf('Check if user has already seen intro with key "%s". Result is %d', $key, $shownDemo));
|
||||
}
|
||||
|
||||
View::share('shownDemo', $shownDemo);
|
||||
View::share('current_route_name', $page);
|
||||
View::share('original_route_name', Route::currentRouteName());
|
||||
}
|
||||
|
||||
return $next($request);
|
||||
}
|
||||
);
|
||||
|
||||
@@ -25,62 +25,95 @@ use Response;
|
||||
*/
|
||||
class HelpController extends Controller
|
||||
{
|
||||
|
||||
/** @var HelpInterface */
|
||||
private $help;
|
||||
|
||||
/**
|
||||
* HelpController constructor.
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
|
||||
$this->middleware(
|
||||
function ($request, $next) {
|
||||
$this->help = app(HelpInterface::class);
|
||||
|
||||
return $next($request);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param HelpInterface $help
|
||||
* @param $route
|
||||
*
|
||||
* @return \Illuminate\Http\JsonResponse
|
||||
*/
|
||||
public function show(HelpInterface $help, string $route)
|
||||
public function show(string $route)
|
||||
{
|
||||
|
||||
$language = Preferences::get('language', config('firefly.default_language', 'en_US'))->data;
|
||||
$content = '<p>' . strval(trans('firefly.route_has_no_help')) . '</p>';
|
||||
$html = $this->getHelpText($route, $language);
|
||||
|
||||
if (!$help->hasRoute($route)) {
|
||||
return Response::json(['html' => $html]);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $route
|
||||
* @param string $language
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private function getHelpText(string $route, string $language): string
|
||||
{
|
||||
// get language and default variables.
|
||||
|
||||
$content = '<p>' . strval(trans('firefly.route_has_no_help')) . '</p>';
|
||||
|
||||
// if no such route, log error and return default text.
|
||||
if (!$this->help->hasRoute($route)) {
|
||||
Log::error('No such route: ' . $route);
|
||||
|
||||
return Response::json($content);
|
||||
return $content;
|
||||
}
|
||||
|
||||
if ($help->inCache($route, $language)) {
|
||||
$content = $help->getFromCache($route, $language);
|
||||
// help content may be cached:
|
||||
if ($this->help->inCache($route, $language)) {
|
||||
$content = $this->help->getFromCache($route, $language);
|
||||
Log::debug(sprintf('Help text %s was in cache.', $language));
|
||||
|
||||
return Response::json($content);
|
||||
return $content;
|
||||
}
|
||||
|
||||
$content = $help->getFromGithub($route, $language);
|
||||
$notYourLanguage = '<p><em>' . strval(trans('firefly.help_may_not_be_your_language')) . '</em></p>';
|
||||
// get help content from Github:
|
||||
$content = $this->help->getFromGithub($route, $language);
|
||||
|
||||
// get backup language content (try English):
|
||||
// content will have 0 length when Github failed. Try en_US when it does:
|
||||
if (strlen($content) === 0) {
|
||||
$language = 'en_US';
|
||||
if ($help->inCache($route, $language)) {
|
||||
|
||||
// also check cache first:
|
||||
if ($this->help->inCache($route, $language)) {
|
||||
Log::debug(sprintf('Help text %s was in cache.', $language));
|
||||
$content = $notYourLanguage . $help->getFromCache($route, $language);
|
||||
}
|
||||
if (!$help->inCache($route, $language)) {
|
||||
$content = trim($notYourLanguage . $help->getFromGithub($route, $language));
|
||||
$content = $this->help->getFromCache($route, $language);
|
||||
|
||||
return $content;
|
||||
}
|
||||
|
||||
$content = $this->help->getFromGithub($route, $language);
|
||||
|
||||
}
|
||||
|
||||
if ($content === $notYourLanguage) {
|
||||
$content = '<p>' . strval(trans('firefly.route_has_no_help')) . '</p>';
|
||||
// help still empty?
|
||||
if (strlen($content) !== 0) {
|
||||
$this->help->putInCache($route, $language, $content);
|
||||
|
||||
return $content;
|
||||
|
||||
}
|
||||
|
||||
$help->putInCache($route, $language, $content);
|
||||
|
||||
return Response::json($content);
|
||||
|
||||
return '<p>' . strval(trans('firefly.route_has_no_help')) . '</p>';
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -21,9 +21,11 @@ use FireflyIII\Models\AccountType;
|
||||
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
|
||||
use FireflyIII\Repositories\Bill\BillRepositoryInterface;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Routing\Route;
|
||||
use Illuminate\Support\Collection;
|
||||
use Log;
|
||||
use Preferences;
|
||||
use Route as RouteFacade;
|
||||
use Session;
|
||||
use View;
|
||||
|
||||
@@ -107,7 +109,7 @@ class HomeController extends Controller
|
||||
$types = config('firefly.accountTypesByIdentifier.asset');
|
||||
$count = $repository->count($types);
|
||||
|
||||
if ($count == 0) {
|
||||
if ($count === 0) {
|
||||
return redirect(route('new-user.index'));
|
||||
}
|
||||
|
||||
@@ -120,7 +122,6 @@ class HomeController extends Controller
|
||||
$start = session('start', Carbon::now()->startOfMonth());
|
||||
/** @var Carbon $end */
|
||||
$end = session('end', Carbon::now()->endOfMonth());
|
||||
$showTour = Preferences::get('tour', true)->data;
|
||||
$accounts = $repository->getAccountsById($frontPage->data);
|
||||
$showDepositsFrontpage = Preferences::get('showDepositsFrontpage', false)->data;
|
||||
|
||||
@@ -137,10 +138,34 @@ class HomeController extends Controller
|
||||
}
|
||||
|
||||
return view(
|
||||
'index', compact('count', 'showTour', 'title', 'subTitle', 'mainTitleIcon', 'transactions', 'showDepositsFrontpage', 'billCount')
|
||||
'index', compact('count', 'subTitle', 'transactions', 'showDepositsFrontpage', 'billCount')
|
||||
);
|
||||
}
|
||||
|
||||
public function routes()
|
||||
{
|
||||
$set = RouteFacade::getRoutes();
|
||||
$ignore = ['chart.', 'javascript.', 'json.', 'report-data.', 'popup.', 'debugbar.'];
|
||||
/** @var Route $route */
|
||||
foreach ($set as $route) {
|
||||
$name = $route->getName();
|
||||
if (!is_null($name) && in_array('GET', $route->methods()) && strlen($name) > 0) {
|
||||
$found = false;
|
||||
foreach ($ignore as $string) {
|
||||
if (strpos($name, $string) !== false) {
|
||||
$found = true;
|
||||
}
|
||||
}
|
||||
if (!$found) {
|
||||
echo 'touch ' . $route->getName() . '.md;';
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
return ' ';
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector
|
||||
*/
|
||||
|
||||
49
app/Http/Controllers/Import/BankController.php
Normal file
49
app/Http/Controllers/Import/BankController.php
Normal file
@@ -0,0 +1,49 @@
|
||||
<?php
|
||||
/**
|
||||
* BankController.php
|
||||
* Copyright (c) 2017 thegrumpydictator@gmail.com
|
||||
* This software may be modified and distributed under the terms of the Creative Commons Attribution-ShareAlike 4.0 International License.
|
||||
*
|
||||
* See the LICENSE file for details.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\Http\Controllers\Import;
|
||||
|
||||
|
||||
use FireflyIII\Http\Controllers\Controller;
|
||||
use FireflyIII\Support\Import\Prerequisites\PrerequisitesInterface;
|
||||
|
||||
class BankController extends Controller
|
||||
{
|
||||
|
||||
public function postPrerequisites()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $bank
|
||||
*/
|
||||
public function prerequisites(string $bank)
|
||||
{
|
||||
$class = config(sprintf('firefly.import_pre.%s', $bank));
|
||||
/** @var PrerequisitesInterface $object */
|
||||
$object = app($class);
|
||||
$object->setUser(auth()->user());
|
||||
|
||||
if ($object->hasPrerequisites()) {
|
||||
$view = $object->getView();
|
||||
$parameters = $object->getViewParameters();
|
||||
|
||||
return view($view, $parameters);
|
||||
}
|
||||
|
||||
if (!$object->hasPrerequisites()) {
|
||||
echo 'redirect to import form.';
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
306
app/Http/Controllers/Import/FileController.php
Normal file
306
app/Http/Controllers/Import/FileController.php
Normal file
@@ -0,0 +1,306 @@
|
||||
<?php
|
||||
/**
|
||||
* FileController.php
|
||||
* Copyright (c) 2017 thegrumpydictator@gmail.com
|
||||
* This software may be modified and distributed under the terms of the Creative Commons Attribution-ShareAlike 4.0 International License.
|
||||
*
|
||||
* See the LICENSE file for details.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
|
||||
namespace FireflyIII\Http\Controllers\Import;
|
||||
|
||||
use FireflyIII\Exceptions\FireflyException;
|
||||
use FireflyIII\Http\Controllers\Controller;
|
||||
use FireflyIII\Http\Requests\ImportUploadRequest;
|
||||
use FireflyIII\Import\Configurator\ConfiguratorInterface;
|
||||
use FireflyIII\Import\Routine\ImportRoutine;
|
||||
use FireflyIII\Models\ImportJob;
|
||||
use FireflyIII\Repositories\ImportJob\ImportJobRepositoryInterface;
|
||||
use FireflyIII\Repositories\Tag\TagRepositoryInterface;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Http\Response as LaravelResponse;
|
||||
use Log;
|
||||
use Response;
|
||||
use Session;
|
||||
use View;
|
||||
|
||||
|
||||
/**
|
||||
* Class FileController.
|
||||
*
|
||||
* @package FireflyIII\Http\Controllers\Import
|
||||
*/
|
||||
class FileController extends Controller
|
||||
{
|
||||
/** @var ImportJobRepositoryInterface */
|
||||
public $repository;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
|
||||
$this->middleware(
|
||||
function ($request, $next) {
|
||||
View::share('mainTitleIcon', 'fa-archive');
|
||||
View::share('title', trans('firefly.import_index_title'));
|
||||
$this->repository = app(ImportJobRepositoryInterface::class);
|
||||
|
||||
return $next($request);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* This is step 3. This repeats until the job is configured.
|
||||
*
|
||||
* @param ImportJob $job
|
||||
*
|
||||
* @return View
|
||||
* @throws FireflyException
|
||||
*/
|
||||
public function configure(ImportJob $job)
|
||||
{
|
||||
// create configuration class:
|
||||
$configurator = $this->makeConfigurator($job);
|
||||
|
||||
// is the job already configured?
|
||||
if ($configurator->isJobConfigured()) {
|
||||
$this->repository->updateStatus($job, 'configured');
|
||||
|
||||
return redirect(route('import.file.status', [$job->key]));
|
||||
}
|
||||
$view = $configurator->getNextView();
|
||||
$data = $configurator->getNextData();
|
||||
$subTitle = trans('firefly.import_config_bread_crumb');
|
||||
$subTitleIcon = 'fa-wrench';
|
||||
|
||||
return view($view, compact('data', 'job', 'subTitle', 'subTitleIcon'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a JSON file of the job's configuration and send it to the user.
|
||||
*
|
||||
* @param ImportJob $job
|
||||
*
|
||||
* @return LaravelResponse
|
||||
*/
|
||||
public function download(ImportJob $job)
|
||||
{
|
||||
Log::debug('Now in download()', ['job' => $job->key]);
|
||||
$config = $job->configuration;
|
||||
|
||||
// This is CSV import specific:
|
||||
$config['column-roles-complete'] = false;
|
||||
$config['column-mapping-complete'] = false;
|
||||
$config['initial-config-complete'] = false;
|
||||
$config['delimiter'] = $config['delimiter'] === "\t" ? 'tab' : $config['delimiter'];
|
||||
|
||||
$result = json_encode($config, JSON_PRETTY_PRINT);
|
||||
$name = sprintf('"%s"', addcslashes('import-configuration-' . date('Y-m-d') . '.json', '"\\'));
|
||||
|
||||
/** @var LaravelResponse $response */
|
||||
$response = response($result, 200);
|
||||
$response->header('Content-disposition', 'attachment; filename=' . $name)
|
||||
->header('Content-Type', 'application/json')
|
||||
->header('Content-Description', 'File Transfer')
|
||||
->header('Connection', 'Keep-Alive')
|
||||
->header('Expires', '0')
|
||||
->header('Cache-Control', 'must-revalidate, post-check=0, pre-check=0')
|
||||
->header('Pragma', 'public')
|
||||
->header('Content-Length', strlen($result));
|
||||
|
||||
return $response;
|
||||
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* This is step 1. Upload a file.
|
||||
*
|
||||
* @return View
|
||||
*/
|
||||
public function index()
|
||||
{
|
||||
$subTitle = trans('firefly.import_index_sub_title');
|
||||
$subTitleIcon = 'fa-home';
|
||||
$importFileTypes = [];
|
||||
$defaultImportType = config('firefly.default_import_format');
|
||||
|
||||
foreach (array_keys(config('firefly.import_formats')) as $type) {
|
||||
$importFileTypes[$type] = trans('firefly.import_file_type_' . $type);
|
||||
}
|
||||
|
||||
return view('import.file.index', compact('subTitle', 'subTitleIcon', 'importFileTypes', 'defaultImportType'));
|
||||
}
|
||||
|
||||
/**
|
||||
* This is step 2. It creates an Import Job. Stores the import.
|
||||
*
|
||||
* @param ImportUploadRequest $request
|
||||
*
|
||||
* @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector
|
||||
*/
|
||||
public function initialize(ImportUploadRequest $request)
|
||||
{
|
||||
Log::debug('Now in initialize()');
|
||||
|
||||
// create import job:
|
||||
$type = $request->get('import_file_type');
|
||||
$job = $this->repository->create($type);
|
||||
Log::debug('Created new job', ['key' => $job->key, 'id' => $job->id]);
|
||||
|
||||
// process file:
|
||||
$this->repository->processFile($job, $request->files->get('import_file'));
|
||||
|
||||
// process config, if present:
|
||||
if ($request->files->has('configuration_file')) {
|
||||
$this->repository->processConfiguration($job, $request->files->get('configuration_file'));
|
||||
}
|
||||
|
||||
$this->repository->updateStatus($job, 'initialized');
|
||||
|
||||
return redirect(route('import.file.configure', [$job->key]));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
* Show status of import job in JSON.
|
||||
*
|
||||
* @param ImportJob $job
|
||||
*
|
||||
* @return \Illuminate\Http\JsonResponse
|
||||
*/
|
||||
public function json(ImportJob $job)
|
||||
{
|
||||
$result = [
|
||||
'started' => false,
|
||||
'finished' => false,
|
||||
'running' => false,
|
||||
'errors' => array_values($job->extended_status['errors']),
|
||||
'percentage' => 0,
|
||||
'show_percentage' => false,
|
||||
'steps' => $job->extended_status['steps'],
|
||||
'done' => $job->extended_status['done'],
|
||||
'statusText' => trans('firefly.import_status_job_' . $job->status),
|
||||
'status' => $job->status,
|
||||
'finishedText' => '',
|
||||
];
|
||||
|
||||
if ($job->extended_status['steps'] !== 0) {
|
||||
$result['percentage'] = round(($job->extended_status['done'] / $job->extended_status['steps']) * 100, 0);
|
||||
$result['show_percentage'] = true;
|
||||
}
|
||||
|
||||
if ($job->status === 'finished') {
|
||||
$tagId = $job->extended_status['tag'];
|
||||
/** @var TagRepositoryInterface $repository */
|
||||
$repository = app(TagRepositoryInterface::class);
|
||||
$tag = $repository->find($tagId);
|
||||
$result['finished'] = true;
|
||||
$result['finishedText'] = trans('firefly.import_status_finished_job', ['link' => route('tags.show', [$tag->id, 'all']), 'tag' => $tag->tag]);
|
||||
}
|
||||
|
||||
if ($job->status === 'running') {
|
||||
$result['started'] = true;
|
||||
$result['running'] = true;
|
||||
}
|
||||
|
||||
return Response::json($result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Step 4. Save the configuration.
|
||||
*
|
||||
* @param Request $request
|
||||
* @param ImportJob $job
|
||||
*
|
||||
* @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector
|
||||
*/
|
||||
public function postConfigure(Request $request, ImportJob $job)
|
||||
{
|
||||
Log::debug('Now in postConfigure()', ['job' => $job->key]);
|
||||
$configurator = $this->makeConfigurator($job);
|
||||
|
||||
// is the job already configured?
|
||||
if ($configurator->isJobConfigured()) {
|
||||
return redirect(route('import.file.status', [$job->key]));
|
||||
}
|
||||
$data = $request->all();
|
||||
$configurator->configureJob($data);
|
||||
|
||||
// get possible warning from configurator:
|
||||
$warning = $configurator->getWarningMessage();
|
||||
|
||||
if (strlen($warning) > 0) {
|
||||
Session::flash('warning', $warning);
|
||||
}
|
||||
|
||||
// return to configure
|
||||
return redirect(route('import.file.configure', [$job->key]));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ImportJob $job
|
||||
*
|
||||
* @return \Illuminate\Http\JsonResponse
|
||||
* @throws FireflyException
|
||||
*/
|
||||
public function start(ImportJob $job)
|
||||
{
|
||||
/** @var ImportRoutine $routine */
|
||||
$routine = app(ImportRoutine::class);
|
||||
$routine->setJob($job);
|
||||
$result = $routine->run();
|
||||
if ($result) {
|
||||
return Response::json(['run' => 'ok']);
|
||||
}
|
||||
|
||||
throw new FireflyException('Job did not complete succesfully.');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ImportJob $job
|
||||
*
|
||||
* @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector|View
|
||||
*/
|
||||
public function status(ImportJob $job)
|
||||
{
|
||||
$statuses = ['configured', 'running', 'finished'];
|
||||
if (!in_array($job->status, $statuses)) {
|
||||
return redirect(route('import.file.configure', [$job->key]));
|
||||
}
|
||||
$subTitle = trans('firefly.import_status_sub_title');
|
||||
$subTitleIcon = 'fa-star';
|
||||
|
||||
return view('import.file.status', compact('job', 'subTitle', 'subTitleIcon'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ImportJob $job
|
||||
*
|
||||
* @return ConfiguratorInterface
|
||||
* @throws FireflyException
|
||||
*/
|
||||
private function makeConfigurator(ImportJob $job): ConfiguratorInterface
|
||||
{
|
||||
$type = $job->file_type;
|
||||
$key = sprintf('firefly.import_configurators.%s', $type);
|
||||
$className = config($key);
|
||||
if (is_null($className)) {
|
||||
throw new FireflyException('Cannot find configurator class for this job.'); // @codeCoverageIgnore
|
||||
}
|
||||
/** @var ConfiguratorInterface $configurator */
|
||||
$configurator = app($className);
|
||||
$configurator->setJob($job);
|
||||
|
||||
|
||||
return $configurator;
|
||||
}
|
||||
}
|
||||
@@ -12,17 +12,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\Http\Controllers;
|
||||
|
||||
use FireflyIII\Exceptions\FireflyException;
|
||||
use FireflyIII\Http\Requests\ImportUploadRequest;
|
||||
use FireflyIII\Import\Configurator\ConfiguratorInterface;
|
||||
use FireflyIII\Import\Routine\ImportRoutine;
|
||||
use FireflyIII\Models\ImportJob;
|
||||
use FireflyIII\Repositories\ImportJob\ImportJobRepositoryInterface;
|
||||
use FireflyIII\Repositories\Tag\TagRepositoryInterface;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Http\Response as LaravelResponse;
|
||||
use Log;
|
||||
use Response;
|
||||
use View;
|
||||
|
||||
/**
|
||||
@@ -53,72 +43,9 @@ class ImportController extends Controller
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* This is step 3. This repeats until the job is configured.
|
||||
*
|
||||
* @param ImportJob $job
|
||||
*
|
||||
* @return View
|
||||
* @throws FireflyException
|
||||
*/
|
||||
public function configure(ImportJob $job)
|
||||
{
|
||||
// create configuration class:
|
||||
$configurator = $this->makeConfigurator($job);
|
||||
|
||||
// is the job already configured?
|
||||
if ($configurator->isJobConfigured()) {
|
||||
$this->repository->updateStatus($job, 'configured');
|
||||
|
||||
return redirect(route('import.status', [$job->key]));
|
||||
}
|
||||
$view = $configurator->getNextView();
|
||||
$data = $configurator->getNextData();
|
||||
$subTitle = trans('firefly.import_config_bread_crumb');
|
||||
$subTitleIcon = 'fa-wrench';
|
||||
|
||||
return view($view, compact('data', 'job', 'subTitle', 'subTitleIcon'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a JSON file of the job's configuration and send it to the user.
|
||||
*
|
||||
* @param ImportJob $job
|
||||
*
|
||||
* @return LaravelResponse
|
||||
*/
|
||||
public function download(ImportJob $job)
|
||||
{
|
||||
Log::debug('Now in download()', ['job' => $job->key]);
|
||||
$config = $job->configuration;
|
||||
|
||||
// TODO this is CSV import specific:
|
||||
$config['column-roles-complete'] = false;
|
||||
$config['column-mapping-complete'] = false;
|
||||
$config['initial-config-complete'] = false;
|
||||
$config['delimiter'] = $config['delimiter'] === "\t" ? 'tab' : $config['delimiter'];
|
||||
|
||||
$result = json_encode($config, JSON_PRETTY_PRINT);
|
||||
$name = sprintf('"%s"', addcslashes('import-configuration-' . date('Y-m-d') . '.json', '"\\'));
|
||||
|
||||
/** @var LaravelResponse $response */
|
||||
$response = response($result, 200);
|
||||
$response->header('Content-disposition', 'attachment; filename=' . $name)
|
||||
->header('Content-Type', 'application/json')
|
||||
->header('Content-Description', 'File Transfer')
|
||||
->header('Connection', 'Keep-Alive')
|
||||
->header('Expires', '0')
|
||||
->header('Cache-Control', 'must-revalidate, post-check=0, pre-check=0')
|
||||
->header('Pragma', 'public')
|
||||
->header('Content-Length', strlen($result));
|
||||
|
||||
return $response;
|
||||
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* This is step 1. Upload a file.
|
||||
* General import index
|
||||
*
|
||||
* @return View
|
||||
*/
|
||||
@@ -136,160 +63,4 @@ class ImportController extends Controller
|
||||
return view('import.index', compact('subTitle', 'subTitleIcon', 'importFileTypes', 'defaultImportType'));
|
||||
}
|
||||
|
||||
/**
|
||||
* This is step 2. It creates an Import Job. Stores the import.
|
||||
*
|
||||
* @param ImportUploadRequest $request
|
||||
*
|
||||
* @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector
|
||||
*/
|
||||
public function initialize(ImportUploadRequest $request)
|
||||
{
|
||||
Log::debug('Now in initialize()');
|
||||
|
||||
// create import job:
|
||||
$type = $request->get('import_file_type');
|
||||
$job = $this->repository->create($type);
|
||||
Log::debug('Created new job', ['key' => $job->key, 'id' => $job->id]);
|
||||
|
||||
// process file:
|
||||
$this->repository->processFile($job, $request->files->get('import_file'));
|
||||
|
||||
// process config, if present:
|
||||
if ($request->files->has('configuration_file')) {
|
||||
$this->repository->processConfiguration($job, $request->files->get('configuration_file'));
|
||||
}
|
||||
|
||||
$this->repository->updateStatus($job, 'initialized');
|
||||
|
||||
return redirect(route('import.configure', [$job->key]));
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* Show status of import job in JSON.
|
||||
*
|
||||
* @param ImportJob $job
|
||||
*
|
||||
* @return \Illuminate\Http\JsonResponse
|
||||
*/
|
||||
public function json(ImportJob $job)
|
||||
{
|
||||
$result = [
|
||||
'started' => false,
|
||||
'finished' => false,
|
||||
'running' => false,
|
||||
'errors' => array_values($job->extended_status['errors']),
|
||||
'percentage' => 0,
|
||||
'show_percentage' => false,
|
||||
'steps' => $job->extended_status['steps'],
|
||||
'done' => $job->extended_status['done'],
|
||||
'statusText' => trans('firefly.import_status_job_' . $job->status),
|
||||
'status' => $job->status,
|
||||
'finishedText' => '',
|
||||
];
|
||||
|
||||
if ($job->extended_status['steps'] !== 0) {
|
||||
$result['percentage'] = round(($job->extended_status['done'] / $job->extended_status['steps']) * 100, 0);
|
||||
$result['show_percentage'] = true;
|
||||
}
|
||||
|
||||
if ($job->status === 'finished') {
|
||||
$tagId = $job->extended_status['tag'];
|
||||
/** @var TagRepositoryInterface $repository */
|
||||
$repository = app(TagRepositoryInterface::class);
|
||||
$tag = $repository->find($tagId);
|
||||
$result['finished'] = true;
|
||||
$result['finishedText'] = trans('firefly.import_status_finished_job', ['link' => route('tags.show', [$tag->id, 'all']), 'tag' => $tag->tag]);
|
||||
}
|
||||
|
||||
if ($job->status === 'running') {
|
||||
$result['started'] = true;
|
||||
$result['running'] = true;
|
||||
}
|
||||
|
||||
return Response::json($result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Step 4. Save the configuration.
|
||||
*
|
||||
* @param Request $request
|
||||
* @param ImportJob $job
|
||||
*
|
||||
* @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector
|
||||
*/
|
||||
public function postConfigure(Request $request, ImportJob $job)
|
||||
{
|
||||
Log::debug('Now in postConfigure()', ['job' => $job->key]);
|
||||
$configurator = $this->makeConfigurator($job);
|
||||
|
||||
// is the job already configured?
|
||||
if ($configurator->isJobConfigured()) {
|
||||
return redirect(route('import.status', [$job->key]));
|
||||
}
|
||||
$data = $request->all();
|
||||
$configurator->configureJob($data);
|
||||
|
||||
// return to configure
|
||||
return redirect(route('import.configure', [$job->key]));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ImportJob $job
|
||||
*
|
||||
* @return \Illuminate\Http\JsonResponse
|
||||
* @throws FireflyException
|
||||
*/
|
||||
public function start(ImportJob $job)
|
||||
{
|
||||
/** @var ImportRoutine $routine */
|
||||
$routine = app(ImportRoutine::class);
|
||||
$routine->setJob($job);
|
||||
$result = $routine->run();
|
||||
if ($result) {
|
||||
return Response::json(['run' => 'ok']);
|
||||
}
|
||||
|
||||
throw new FireflyException('Job did not complete succesfully.');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ImportJob $job
|
||||
*
|
||||
* @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector|View
|
||||
*/
|
||||
public function status(ImportJob $job)
|
||||
{
|
||||
$statuses = ['configured', 'running', 'finished'];
|
||||
if (!in_array($job->status, $statuses)) {
|
||||
return redirect(route('import.configure', [$job->key]));
|
||||
}
|
||||
$subTitle = trans('firefly.import_status_sub_title');
|
||||
$subTitleIcon = 'fa-star';
|
||||
|
||||
return view('import.status', compact('job', 'subTitle', 'subTitleIcon'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ImportJob $job
|
||||
*
|
||||
* @return ConfiguratorInterface
|
||||
* @throws FireflyException
|
||||
*/
|
||||
private function makeConfigurator(ImportJob $job): ConfiguratorInterface
|
||||
{
|
||||
$type = $job->file_type;
|
||||
$key = sprintf('firefly.import_configurators.%s', $type);
|
||||
$className = config($key);
|
||||
if (is_null($className)) {
|
||||
throw new FireflyException('Cannot find configurator class for this job.'); // @codeCoverageIgnore
|
||||
}
|
||||
/** @var ConfiguratorInterface $configurator */
|
||||
$configurator = app($className);
|
||||
$configurator->setJob($job);
|
||||
|
||||
|
||||
return $configurator;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@ declare(strict_types=1);
|
||||
namespace FireflyIII\Http\Controllers;
|
||||
|
||||
use Amount;
|
||||
use Carbon\Carbon;
|
||||
use FireflyIII\Exceptions\FireflyException;
|
||||
use FireflyIII\Models\Account;
|
||||
use FireflyIII\Models\AccountType;
|
||||
@@ -19,9 +20,9 @@ use FireflyIII\Models\TransactionCurrency;
|
||||
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
|
||||
use FireflyIII\Repositories\Currency\CurrencyRepositoryInterface;
|
||||
use Illuminate\Http\Request;
|
||||
use Log;
|
||||
use Navigation;
|
||||
use Preferences;
|
||||
use Session;
|
||||
|
||||
/**
|
||||
* Class JavascriptController
|
||||
@@ -88,11 +89,6 @@ class JavascriptController extends Controller
|
||||
*/
|
||||
public function variables(Request $request)
|
||||
{
|
||||
$picker = $this->getDateRangePicker();
|
||||
$start = Session::get('start');
|
||||
$end = Session::get('end');
|
||||
$linkTitle = sprintf('%s - %s', $start->formatLocalized($this->monthAndDayFormat), $end->formatLocalized($this->monthAndDayFormat));
|
||||
$firstDate = session('first')->format('Y-m-d');
|
||||
$localeconv = localeconv();
|
||||
$accounting = Amount::getJsConfig($localeconv);
|
||||
$localeconv = localeconv();
|
||||
@@ -100,15 +96,16 @@ class JavascriptController extends Controller
|
||||
$localeconv['frac_digits'] = $defaultCurrency->decimal_places;
|
||||
$pref = Preferences::get('language', config('firefly.default_language', 'en_US'));
|
||||
$lang = $pref->data;
|
||||
$data = [
|
||||
'picker' => $picker,
|
||||
'linkTitle' => $linkTitle,
|
||||
'firstDate' => $firstDate,
|
||||
'currencyCode' => Amount::getCurrencyCode(),
|
||||
'currencySymbol' => Amount::getCurrencySymbol(),
|
||||
'accounting' => $accounting,
|
||||
'localeconv' => $localeconv,
|
||||
'language' => $lang,
|
||||
$dateRange = $this->getDateRangeConfig();
|
||||
|
||||
$data = [
|
||||
'currencyCode' => Amount::getCurrencyCode(),
|
||||
'currencySymbol' => Amount::getCurrencySymbol(),
|
||||
'accounting' => $accounting,
|
||||
'localeconv' => $localeconv,
|
||||
'language' => $lang,
|
||||
'dateRangeTitle' => $dateRange['title'],
|
||||
'dateRangeConfig' => $dateRange['configuration'],
|
||||
];
|
||||
$request->session()->keep(['two-factor-secret']);
|
||||
|
||||
@@ -119,40 +116,73 @@ class JavascriptController extends Controller
|
||||
|
||||
/**
|
||||
* @return array
|
||||
* @throws FireflyException
|
||||
*/
|
||||
private function getDateRangePicker(): array
|
||||
private function getDateRangeConfig(): array
|
||||
{
|
||||
$viewRange = Preferences::get('viewRange', '1M')->data;
|
||||
$start = Session::get('start');
|
||||
$end = Session::get('end');
|
||||
$start = session('start');
|
||||
$end = session('end');
|
||||
$first = session('first');
|
||||
$title = sprintf('%s - %s', $start->formatLocalized($this->monthAndDayFormat), $end->formatLocalized($this->monthAndDayFormat));
|
||||
$isCustom = session('is_custom_range');
|
||||
$ranges = [
|
||||
// first range is the current range:
|
||||
$title => [$start, $end],
|
||||
];
|
||||
Log::debug(sprintf('viewRange is %s', $viewRange));
|
||||
|
||||
$prevStart = clone $start;
|
||||
$prevEnd = clone $start;
|
||||
$nextStart = clone $end;
|
||||
$nextEnd = clone $end;
|
||||
if ($viewRange === 'custom') {
|
||||
$days = $start->diffInDays($end);
|
||||
$prevStart->subDays($days);
|
||||
$nextEnd->addDays($days);
|
||||
unset($days);
|
||||
// get the format for the ranges:
|
||||
$format = $this->getFormatByRange($viewRange);
|
||||
|
||||
// when current range is a custom range, add the current period as the next range.
|
||||
if ($isCustom) {
|
||||
Log::debug('Custom is true.');
|
||||
$index = $start->formatLocalized($format);
|
||||
$customPeriodStart = Navigation::startOfPeriod($start, $viewRange);
|
||||
$customPeriodEnd = Navigation::endOfPeriod($customPeriodStart, $viewRange);
|
||||
$ranges[$index] = [$customPeriodStart, $customPeriodEnd];
|
||||
}
|
||||
// then add previous range and next range
|
||||
$previousDate = Navigation::subtractPeriod($start, $viewRange);
|
||||
$index = $previousDate->formatLocalized($format);
|
||||
$previousStart = Navigation::startOfPeriod($previousDate, $viewRange);
|
||||
$previousEnd = Navigation::endOfPeriod($previousStart, $viewRange);
|
||||
$ranges[$index] = [$previousStart, $previousEnd];
|
||||
|
||||
if ($viewRange !== 'custom') {
|
||||
$prevStart = Navigation::subtractPeriod($start, $viewRange);// subtract for previous period
|
||||
$prevEnd = Navigation::endOfPeriod($prevStart, $viewRange);
|
||||
$nextStart = Navigation::addPeriod($start, $viewRange, 0); // add for previous period
|
||||
$nextEnd = Navigation::endOfPeriod($nextStart, $viewRange);
|
||||
}
|
||||
$nextDate = Navigation::addPeriod($start, $viewRange, 0);
|
||||
$index = $nextDate->formatLocalized($format);
|
||||
$nextStart = Navigation::startOfPeriod($nextDate, $viewRange);
|
||||
$nextEnd = Navigation::endOfPeriod($nextStart, $viewRange);
|
||||
$ranges[$index] = [$nextStart, $nextEnd];
|
||||
|
||||
$ranges = [];
|
||||
$ranges['current'] = [$start->format('Y-m-d'), $end->format('Y-m-d')];
|
||||
$ranges['previous'] = [$prevStart->format('Y-m-d'), $prevEnd->format('Y-m-d')];
|
||||
$ranges['next'] = [$nextStart->format('Y-m-d'), $nextEnd->format('Y-m-d')];
|
||||
// everything
|
||||
$index = strval(trans('firefly.everything'));
|
||||
$ranges[$index] = [$first, new Carbon];
|
||||
|
||||
$return = [
|
||||
'title' => $title,
|
||||
'configuration' => [
|
||||
'apply' => strval(trans('firefly.apply')),
|
||||
'cancel' => strval(trans('firefly.cancel')),
|
||||
'from' => strval(trans('firefly.from')),
|
||||
'to' => strval(trans('firefly.to')),
|
||||
'customRange' => strval(trans('firefly.customRange')),
|
||||
'start' => $start->format('Y-m-d'),
|
||||
'end' => $end->format('Y-m-d'),
|
||||
'ranges' => $ranges,
|
||||
],
|
||||
];
|
||||
|
||||
|
||||
return $return;
|
||||
|
||||
}
|
||||
|
||||
private function getFormatByRange(string $viewRange): string
|
||||
{
|
||||
switch ($viewRange) {
|
||||
default:
|
||||
throw new FireflyException('The date picker does not yet support "' . $viewRange . '".'); // @codeCoverageIgnore
|
||||
throw new FireflyException(sprintf('The date picker does not yet support "%s".', $viewRange)); // @codeCoverageIgnore
|
||||
case '1D':
|
||||
case 'custom':
|
||||
$format = (string)trans('config.month_and_day');
|
||||
@@ -174,18 +204,6 @@ class JavascriptController extends Controller
|
||||
break;
|
||||
}
|
||||
|
||||
$current = $start->formatLocalized($format);
|
||||
$next = $nextStart->formatLocalized($format);
|
||||
$prev = $prevStart->formatLocalized($format);
|
||||
|
||||
return [
|
||||
'start' => $start->format('Y-m-d'),
|
||||
'end' => $end->format('Y-m-d'),
|
||||
'current' => $current,
|
||||
'previous' => $prev,
|
||||
'next' => $next,
|
||||
'ranges' => $ranges,
|
||||
];
|
||||
return $format;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
166
app/Http/Controllers/Json/IntroController.php
Normal file
166
app/Http/Controllers/Json/IntroController.php
Normal file
@@ -0,0 +1,166 @@
|
||||
<?php
|
||||
/**
|
||||
* IntroController.php
|
||||
* Copyright (c) 2017 thegrumpydictator@gmail.com
|
||||
* This software may be modified and distributed under the terms of the Creative Commons Attribution-ShareAlike 4.0 International License.
|
||||
*
|
||||
* See the LICENSE file for details.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\Http\Controllers\Json;
|
||||
|
||||
use FireflyIII\Support\Facades\Preferences;
|
||||
use Log;
|
||||
use Response;
|
||||
|
||||
/**
|
||||
* Class IntroController
|
||||
*
|
||||
* @package FireflyIII\Http\Controllers\Json
|
||||
*/
|
||||
class IntroController
|
||||
{
|
||||
|
||||
/**
|
||||
* @param string $route
|
||||
* @param string $specificPage
|
||||
*
|
||||
* @return \Illuminate\Http\JsonResponse
|
||||
*/
|
||||
public function getIntroSteps(string $route, string $specificPage = '')
|
||||
{
|
||||
$steps = $this->getBasicSteps($route);
|
||||
$specificSteps = $this->getSpecificSteps($route, $specificPage);
|
||||
if (count($specificSteps) === 0) {
|
||||
return Response::json($steps);
|
||||
}
|
||||
if ($this->hasOutroStep($route)) {
|
||||
// save last step:
|
||||
$lastStep = $steps[count($steps) - 1];
|
||||
// remove last step:
|
||||
array_pop($steps);
|
||||
// merge arrays and add last step again
|
||||
$steps = array_merge($steps, $specificSteps);
|
||||
$steps[] = $lastStep;
|
||||
|
||||
}
|
||||
if (!$this->hasOutroStep($route)) {
|
||||
$steps = array_merge($steps, $specificSteps);
|
||||
}
|
||||
|
||||
return Response::json($steps);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $route
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function hasOutroStep(string $route): bool
|
||||
{
|
||||
$routeKey = str_replace('.', '_', $route);
|
||||
$elements = config(sprintf('intro.%s', $routeKey));
|
||||
if (!is_array($elements)) {
|
||||
return false;
|
||||
}
|
||||
$keys = array_keys($elements);
|
||||
|
||||
return in_array('outro', $keys);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $route
|
||||
* @param string $specialPage
|
||||
*
|
||||
* @return \Illuminate\Http\JsonResponse
|
||||
*/
|
||||
public function postEnable(string $route, string $specialPage = '')
|
||||
{
|
||||
$route = str_replace('.', '_', $route);
|
||||
$key = 'shown_demo_' . $route;
|
||||
if ($specialPage !== '') {
|
||||
$key .= '_' . $specialPage;
|
||||
}
|
||||
Log::debug(sprintf('Going to mark the following route as NOT done: %s with special "%s" (%s)', $route, $specialPage, $key));
|
||||
Preferences::set($key, false);
|
||||
|
||||
return Response::json(['message' => trans('firefly.intro_boxes_after_refresh')]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $route
|
||||
* @param string $specialPage
|
||||
*
|
||||
* @return \Illuminate\Http\JsonResponse
|
||||
*/
|
||||
public function postFinished(string $route, string $specialPage = '')
|
||||
{
|
||||
$key = 'shown_demo_' . $route;
|
||||
if ($specialPage !== '') {
|
||||
$key .= '_' . $specialPage;
|
||||
}
|
||||
Log::debug(sprintf('Going to mark the following route as done: %s with special "%s" (%s)', $route, $specialPage, $key));
|
||||
Preferences::set($key, true);
|
||||
|
||||
return Response::json(['result' => sprintf('Reported demo watched for route "%s".', $route)]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $route
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function getBasicSteps(string $route): array
|
||||
{
|
||||
$routeKey = str_replace('.', '_', $route);
|
||||
$elements = config(sprintf('intro.%s', $routeKey));
|
||||
$steps = [];
|
||||
if (is_array($elements) && count($elements) > 0) {
|
||||
foreach ($elements as $key => $options) {
|
||||
$currentStep = $options;
|
||||
|
||||
// get the text:
|
||||
$currentStep['intro'] = trans('intro.' . $route . '_' . $key);
|
||||
|
||||
|
||||
// save in array:
|
||||
$steps[] = $currentStep;
|
||||
}
|
||||
}
|
||||
|
||||
return $steps;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $route
|
||||
* @param string $specificPage
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function getSpecificSteps(string $route, string $specificPage): array
|
||||
{
|
||||
$steps = [];
|
||||
|
||||
// user is on page with specific instructions:
|
||||
if (strlen($specificPage) > 0) {
|
||||
$routeKey = str_replace('.', '_', $route);
|
||||
$elements = config(sprintf('intro.%s', $routeKey . '_' . $specificPage));
|
||||
if (is_array($elements) && count($elements) > 0) {
|
||||
foreach ($elements as $key => $options) {
|
||||
$currentStep = $options;
|
||||
|
||||
// get the text:
|
||||
$currentStep['intro'] = trans('intro.' . $route . '_' . $specificPage . '_' . $key);
|
||||
|
||||
// save in array:
|
||||
$steps[] = $currentStep;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $steps;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -15,7 +15,6 @@ namespace FireflyIII\Http\Controllers;
|
||||
|
||||
use Amount;
|
||||
use Carbon\Carbon;
|
||||
use FireflyIII\Exceptions\FireflyException;
|
||||
use FireflyIII\Helpers\Collector\JournalCollectorInterface;
|
||||
use FireflyIII\Models\AccountType;
|
||||
use FireflyIII\Models\TransactionType;
|
||||
@@ -27,7 +26,6 @@ use FireflyIII\Repositories\Journal\JournalRepositoryInterface;
|
||||
use FireflyIII\Repositories\Tag\TagRepositoryInterface;
|
||||
use FireflyIII\Support\CacheProperties;
|
||||
use Illuminate\Http\Request;
|
||||
use Preferences;
|
||||
use Response;
|
||||
|
||||
/**
|
||||
@@ -237,16 +235,6 @@ class JsonController extends Controller
|
||||
return Response::json($return);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \Illuminate\Http\JsonResponse
|
||||
*/
|
||||
public function endTour()
|
||||
{
|
||||
Preferences::set('tour', false);
|
||||
|
||||
return Response::json('true');
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a JSON list of all beneficiaries.
|
||||
*
|
||||
@@ -293,34 +281,6 @@ class JsonController extends Controller
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public function tour()
|
||||
{
|
||||
$pref = Preferences::get('tour', true);
|
||||
if (!$pref) {
|
||||
throw new FireflyException('Cannot find preference for tour. Exit.'); // @codeCoverageIgnore
|
||||
}
|
||||
$headers = ['main-content', 'sidebar-toggle', 'account-menu', 'budget-menu', 'report-menu', 'transaction-menu', 'option-menu', 'main-content-end'];
|
||||
$steps = [];
|
||||
foreach ($headers as $header) {
|
||||
$steps[] = [
|
||||
'element' => '#' . $header,
|
||||
'title' => trans('help.' . $header . '-title'),
|
||||
'content' => trans('help.' . $header . '-text'),
|
||||
];
|
||||
}
|
||||
$steps[0]['orphan'] = true;// orphan and backdrop for first element.
|
||||
$steps[0]['backdrop'] = true;
|
||||
$steps[1]['placement'] = 'left';// sidebar position left:
|
||||
$steps[7]['orphan'] = true; // final in the center again.
|
||||
$steps[7]['backdrop'] = true;
|
||||
$template = view('json.tour')->render();
|
||||
|
||||
return Response::json(['steps' => $steps, 'template' => $template]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param JournalCollectorInterface $collector
|
||||
* @param string $what
|
||||
@@ -365,7 +325,7 @@ class JsonController extends Controller
|
||||
$keys = array_keys(config('firefly.rule-triggers'));
|
||||
$triggers = [];
|
||||
foreach ($keys as $key) {
|
||||
if ($key != 'user_action') {
|
||||
if ($key !== 'user_action') {
|
||||
$triggers[$key] = trans('firefly.rule_trigger_' . $key . '_choice');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -54,7 +54,6 @@ class NewUserController extends Controller
|
||||
View::share('title', trans('firefly.welcome'));
|
||||
View::share('mainTitleIcon', 'fa-fire');
|
||||
|
||||
|
||||
$types = config('firefly.accountTypesByIdentifier.asset');
|
||||
$count = $repository->count($types);
|
||||
|
||||
@@ -74,30 +73,13 @@ class NewUserController extends Controller
|
||||
*/
|
||||
public function submit(NewUserFormRequest $request, AccountRepositoryInterface $repository)
|
||||
{
|
||||
$count = 1;
|
||||
// create normal asset account:
|
||||
$this->createAssetAccount($request, $repository);
|
||||
|
||||
// create savings account
|
||||
$savingBalance = strval($request->get('savings_balance')) === '' ? '0' : strval($request->get('savings_balance'));
|
||||
if (bccomp($savingBalance, '0') !== 0) {
|
||||
$this->createSavingsAccount($request, $repository);
|
||||
$count++;
|
||||
}
|
||||
$this->createSavingsAccount($request, $repository);
|
||||
|
||||
|
||||
// create credit card.
|
||||
$limit = strval($request->get('credit_card_limit')) === '' ? '0' : strval($request->get('credit_card_limit'));
|
||||
if (bccomp($limit, '0') !== 0) {
|
||||
$this->storeCreditCard($request, $repository);
|
||||
$count++;
|
||||
}
|
||||
$message = strval(trans('firefly.stored_new_accounts_new_user'));
|
||||
if ($count == 1) {
|
||||
$message = strval(trans('firefly.stored_new_account_new_user'));
|
||||
}
|
||||
|
||||
Session::flash('success', $message);
|
||||
Session::flash('success', strval(trans('firefly.stored_new_accounts_new_user')));
|
||||
Preferences::mark();
|
||||
|
||||
return redirect(route('index'));
|
||||
@@ -152,29 +134,4 @@ class NewUserController extends Controller
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param NewUserFormRequest $request
|
||||
* @param AccountRepositoryInterface $repository
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private function storeCreditCard(NewUserFormRequest $request, AccountRepositoryInterface $repository): bool
|
||||
{
|
||||
$creditAccount = [
|
||||
'name' => 'Credit card',
|
||||
'iban' => null,
|
||||
'accountType' => 'asset',
|
||||
'virtualBalance' => round($request->get('credit_card_limit'), 12),
|
||||
'active' => true,
|
||||
'accountRole' => 'ccAsset',
|
||||
'openingBalance' => null,
|
||||
'openingBalanceDate' => null,
|
||||
'openingBalanceCurrency' => intval($request->input('amount_currency_id_credit_card_limit')),
|
||||
'ccType' => 'monthlyFull',
|
||||
'ccMonthlyPaymentDate' => Carbon::now()->year . '-01-01',
|
||||
];
|
||||
$repository->store($creditAccount);
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -89,7 +89,7 @@ class PiggyBankController extends Controller
|
||||
/** @var Carbon $date */
|
||||
$date = session('end', Carbon::now()->endOfMonth());
|
||||
$leftOnAccount = $piggyBank->leftOnAccount($date);
|
||||
$savedSoFar = $piggyBank->currentRelevantRep()->currentamount?? '0';
|
||||
$savedSoFar = $piggyBank->currentRelevantRep()->currentamount ?? '0';
|
||||
$leftToSave = bcsub($piggyBank->targetamount, $savedSoFar);
|
||||
$maxAmount = min($leftOnAccount, $leftToSave);
|
||||
|
||||
|
||||
@@ -13,12 +13,18 @@ declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\Http\Controllers;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use ExpandedForm;
|
||||
use FireflyIII\Http\Requests\RuleFormRequest;
|
||||
use FireflyIII\Http\Requests\SelectTransactionsRequest;
|
||||
use FireflyIII\Http\Requests\TestRuleFormRequest;
|
||||
use FireflyIII\Jobs\ExecuteRuleOnExistingTransactions;
|
||||
use FireflyIII\Models\AccountType;
|
||||
use FireflyIII\Models\Rule;
|
||||
use FireflyIII\Models\RuleAction;
|
||||
use FireflyIII\Models\RuleGroup;
|
||||
use FireflyIII\Models\RuleTrigger;
|
||||
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
|
||||
use FireflyIII\Repositories\Rule\RuleRepositoryInterface;
|
||||
use FireflyIII\Repositories\RuleGroup\RuleGroupRepositoryInterface;
|
||||
use FireflyIII\Rules\TransactionMatcher;
|
||||
@@ -188,6 +194,41 @@ class RuleController extends Controller
|
||||
return view('rules.rule.edit', compact('rule', 'subTitle', 'primaryTrigger', 'oldTriggers', 'oldActions', 'triggerCount', 'actionCount'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the given rule on a set of existing transactions
|
||||
*
|
||||
* @param SelectTransactionsRequest $request
|
||||
* @param AccountRepositoryInterface $repository
|
||||
* @param Rule $rule
|
||||
*
|
||||
* @return \Illuminate\Http\RedirectResponse
|
||||
* @internal param RuleGroup $ruleGroup
|
||||
*/
|
||||
public function execute(SelectTransactionsRequest $request, AccountRepositoryInterface $repository, Rule $rule)
|
||||
{
|
||||
// Get parameters specified by the user
|
||||
$accounts = $repository->getAccountsById($request->get('accounts'));
|
||||
$startDate = new Carbon($request->get('start_date'));
|
||||
$endDate = new Carbon($request->get('end_date'));
|
||||
|
||||
// Create a job to do the work asynchronously
|
||||
$job = new ExecuteRuleOnExistingTransactions($rule);
|
||||
|
||||
// Apply parameters to the job
|
||||
$job->setUser(auth()->user());
|
||||
$job->setAccounts($accounts);
|
||||
$job->setStartDate($startDate);
|
||||
$job->setEndDate($endDate);
|
||||
|
||||
// Dispatch a new job to execute it in a queue
|
||||
$this->dispatch($job);
|
||||
|
||||
// Tell the user that the job is queued
|
||||
Session::flash('success', strval(trans('firefly.applied_rule_selection', ['title' => $rule->title])));
|
||||
|
||||
return redirect()->route('rules.index');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param RuleGroupRepositoryInterface $repository
|
||||
*
|
||||
@@ -238,6 +279,25 @@ class RuleController extends Controller
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @param AccountRepositoryInterface $repository
|
||||
* @param Rule $rule
|
||||
*
|
||||
* @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View
|
||||
*/
|
||||
public function selectTransactions(AccountRepositoryInterface $repository, Rule $rule)
|
||||
{
|
||||
// does the user have shared accounts?
|
||||
$accounts = $repository->getAccountsByType([AccountType::ASSET]);
|
||||
$accountList = ExpandedForm::makeSelectList($accounts);
|
||||
$checkedAccounts = array_keys($accountList);
|
||||
$first = session('first')->format('Y-m-d');
|
||||
$today = Carbon::create()->format('Y-m-d');
|
||||
$subTitle = (string)trans('firefly.apply_rule_selection', ['title' => $rule->title]);
|
||||
|
||||
return view('rules.rule.select-transactions', compact('checkedAccounts', 'accountList', 'first', 'today', 'rule', 'subTitle'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param RuleFormRequest $request
|
||||
* @param RuleRepositoryInterface $repository
|
||||
@@ -282,7 +342,7 @@ class RuleController extends Controller
|
||||
// build trigger array from response
|
||||
$triggers = $this->getValidTriggerList($request);
|
||||
|
||||
if (count($triggers) == 0) {
|
||||
if (count($triggers) === 0) {
|
||||
return Response::json(['html' => '', 'warning' => trans('firefly.warning_no_valid_triggers')]);
|
||||
}
|
||||
|
||||
@@ -294,14 +354,61 @@ class RuleController extends Controller
|
||||
$matcher->setLimit($limit);
|
||||
$matcher->setRange($range);
|
||||
$matcher->setTriggers($triggers);
|
||||
$matchingTransactions = $matcher->findMatchingTransactions();
|
||||
$matchingTransactions = $matcher->findTransactionsByTriggers();
|
||||
|
||||
// Warn the user if only a subset of transactions is returned
|
||||
$warning = '';
|
||||
if (count($matchingTransactions) == $limit) {
|
||||
if (count($matchingTransactions) === $limit) {
|
||||
$warning = trans('firefly.warning_transaction_subset', ['max_num_transactions' => $limit]);
|
||||
}
|
||||
if (count($matchingTransactions) == 0) {
|
||||
if (count($matchingTransactions) === 0) {
|
||||
$warning = trans('firefly.warning_no_matching_transactions', ['num_transactions' => $range]);
|
||||
}
|
||||
|
||||
// Return json response
|
||||
$view = view('list.journals-tiny', ['transactions' => $matchingTransactions])->render();
|
||||
|
||||
return Response::json(['html' => $view, 'warning' => $warning]);
|
||||
}
|
||||
|
||||
/**
|
||||
* This method allows the user to test a certain set of rule triggers. The rule triggers are grabbed from
|
||||
* the rule itself.
|
||||
*
|
||||
* This method will parse and validate those rules and create a "TransactionMatcher" which will attempt
|
||||
* to find transaction journals matching the users input. A maximum range of transactions to try (range) and
|
||||
* a maximum number of transactions to return (limit) are set as well.
|
||||
*
|
||||
*
|
||||
* @param Rule $rule
|
||||
*
|
||||
* @return \Illuminate\Http\JsonResponse
|
||||
*/
|
||||
public function testTriggersByRule(Rule $rule)
|
||||
{
|
||||
|
||||
$triggers = $rule->ruleTriggers;
|
||||
|
||||
if (count($triggers) === 0) {
|
||||
return Response::json(['html' => '', 'warning' => trans('firefly.warning_no_valid_triggers')]);
|
||||
}
|
||||
|
||||
$limit = config('firefly.test-triggers.limit');
|
||||
$range = config('firefly.test-triggers.range');
|
||||
|
||||
/** @var TransactionMatcher $matcher */
|
||||
$matcher = app(TransactionMatcher::class);
|
||||
$matcher->setLimit($limit);
|
||||
$matcher->setRange($range);
|
||||
$matcher->setRule($rule);
|
||||
$matchingTransactions = $matcher->findTransactionsByRule();
|
||||
|
||||
// Warn the user if only a subset of transactions is returned
|
||||
$warning = '';
|
||||
if (count($matchingTransactions) === $limit) {
|
||||
$warning = trans('firefly.warning_transaction_subset', ['max_num_transactions' => $limit]);
|
||||
}
|
||||
if (count($matchingTransactions) === 0) {
|
||||
$warning = trans('firefly.warning_no_matching_transactions', ['num_transactions' => $range]);
|
||||
}
|
||||
|
||||
@@ -440,7 +547,7 @@ class RuleController extends Controller
|
||||
|
||||
/** @var RuleTrigger $entry */
|
||||
foreach ($rule->ruleTriggers as $entry) {
|
||||
if ($entry->trigger_type != 'user_action') {
|
||||
if ($entry->trigger_type !== 'user_action') {
|
||||
$count = ($index + 1);
|
||||
$triggers[] = view(
|
||||
'rules.partials.trigger',
|
||||
|
||||
@@ -178,7 +178,7 @@ class RuleGroupController extends Controller
|
||||
$this->dispatch($job);
|
||||
|
||||
// Tell the user that the job is queued
|
||||
Session::flash('success', strval(trans('firefly.executed_group_on_existing_transactions', ['title' => $ruleGroup->title])));
|
||||
Session::flash('success', strval(trans('firefly.applied_rule_group_selection', ['title' => $ruleGroup->title])));
|
||||
|
||||
return redirect()->route('rules.index');
|
||||
}
|
||||
@@ -197,7 +197,7 @@ class RuleGroupController extends Controller
|
||||
$checkedAccounts = array_keys($accountList);
|
||||
$first = session('first')->format('Y-m-d');
|
||||
$today = Carbon::create()->format('Y-m-d');
|
||||
$subTitle = (string)trans('firefly.execute_on_existing_transactions');
|
||||
$subTitle = (string)trans('firefly.apply_rule_group_selection', ['title' => $ruleGroup->title]);
|
||||
|
||||
return view('rules.rule-group.select-transactions', compact('checkedAccounts', 'accountList', 'first', 'today', 'ruleGroup', 'subTitle'));
|
||||
}
|
||||
@@ -246,14 +246,14 @@ class RuleGroupController extends Controller
|
||||
* @param RuleGroupRepositoryInterface $repository
|
||||
* @param RuleGroup $ruleGroup
|
||||
*
|
||||
* @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector
|
||||
* @return $this|\Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector
|
||||
*/
|
||||
public function update(RuleGroupFormRequest $request, RuleGroupRepositoryInterface $repository, RuleGroup $ruleGroup)
|
||||
{
|
||||
$data = [
|
||||
'title' => $request->input('title'),
|
||||
'description' => $request->input('description'),
|
||||
'active' => intval($request->input('active')) == 1,
|
||||
'active' => intval($request->input('active')) === 1,
|
||||
];
|
||||
|
||||
$repository->update($ruleGroup, $data);
|
||||
|
||||
@@ -15,7 +15,7 @@ namespace FireflyIII\Http\Controllers;
|
||||
|
||||
use FireflyIII\Support\Search\SearchInterface;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Collection;
|
||||
use Response;
|
||||
use View;
|
||||
|
||||
/**
|
||||
@@ -51,47 +51,29 @@ class SearchController extends Controller
|
||||
*/
|
||||
public function index(Request $request, SearchInterface $searcher)
|
||||
{
|
||||
// yes, hard coded values:
|
||||
$minSearchLen = 1;
|
||||
$limit = 20;
|
||||
$fullQuery = $request->get('q');
|
||||
|
||||
// ui stuff:
|
||||
$subTitle = '';
|
||||
// parse search terms:
|
||||
$searcher->parseQuery($fullQuery);
|
||||
$query = $searcher->getWordsAsString();
|
||||
$subTitle = trans('breadcrumbs.search_result', ['query' => $query]);
|
||||
|
||||
// query stuff
|
||||
$query = null;
|
||||
$result = [];
|
||||
$rawQuery = $request->get('q');
|
||||
$hasModifiers = true;
|
||||
$modifiers = [];
|
||||
return view('search.index', compact('query', 'fullQuery', 'subTitle'));
|
||||
}
|
||||
|
||||
if (!is_null($request->get('q')) && strlen($request->get('q')) >= $minSearchLen) {
|
||||
// parse query, find modifiers:
|
||||
// set limit for search
|
||||
$searcher->setLimit($limit);
|
||||
$searcher->parseQuery($request->get('q'));
|
||||
public function search(Request $request, SearchInterface $searcher)
|
||||
{
|
||||
$fullQuery = $request->get('query');
|
||||
|
||||
$transactions = $searcher->searchTransactions();
|
||||
$accounts = new Collection;
|
||||
$categories = new Collection;
|
||||
$tags = new Collection;
|
||||
$budgets = new Collection;
|
||||
// parse search terms:
|
||||
$searcher->parseQuery($fullQuery);
|
||||
$searcher->setLimit(20);
|
||||
$transactions = $searcher->searchTransactions();
|
||||
$html = view('search.search', compact('transactions'))->render();
|
||||
|
||||
// no special search thing?
|
||||
if (!$searcher->hasModifiers()) {
|
||||
$hasModifiers = false;
|
||||
$accounts = $searcher->searchAccounts();
|
||||
$categories = $searcher->searchCategories();
|
||||
$budgets = $searcher->searchBudgets();
|
||||
$tags = $searcher->searchTags();
|
||||
}
|
||||
$query = $searcher->getWordsAsString();
|
||||
$subTitle = trans('firefly.search_results_for', ['query' => $query]);
|
||||
$result = ['transactions' => $transactions, 'accounts' => $accounts, 'categories' => $categories, 'budgets' => $budgets, 'tags' => $tags];
|
||||
return Response::json(['count' => $transactions->count(), 'html' => $html]);
|
||||
|
||||
}
|
||||
|
||||
return view('search.index', compact('rawQuery', 'hasModifiers', 'modifiers', 'subTitle', 'limit', 'query', 'result'));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -22,7 +22,6 @@ use FireflyIII\Repositories\Tag\TagRepositoryInterface;
|
||||
use FireflyIII\Support\CacheProperties;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Collection;
|
||||
use Log;
|
||||
use Navigation;
|
||||
use Preferences;
|
||||
use Session;
|
||||
@@ -79,22 +78,16 @@ class TagController extends Controller
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Request $request
|
||||
* Create a new tag.
|
||||
*
|
||||
* @return View
|
||||
*/
|
||||
public function create(Request $request)
|
||||
public function create()
|
||||
{
|
||||
$subTitle = trans('firefly.new_tag');
|
||||
$subTitleIcon = 'fa-tag';
|
||||
$apiKey = env('GOOGLE_MAPS_API_KEY', '');
|
||||
|
||||
$preFilled = [
|
||||
'tagMode' => 'nothing',
|
||||
];
|
||||
if (!$request->old('tagMode')) {
|
||||
Session::flash('preFilled', $preFilled);
|
||||
}
|
||||
// put previous url in session if not redirect from store (not "create another").
|
||||
if (session('tags.create.fromStore') !== true) {
|
||||
$this->rememberPreviousUri('tags.create.uri');
|
||||
@@ -107,6 +100,8 @@ class TagController extends Controller
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a tag
|
||||
*
|
||||
* @param Tag $tag
|
||||
*
|
||||
* @return View
|
||||
@@ -141,38 +136,18 @@ class TagController extends Controller
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Tag $tag
|
||||
* Edit a tag
|
||||
*
|
||||
* @param TagRepositoryInterface $repository
|
||||
* @param Tag $tag
|
||||
*
|
||||
* @return View
|
||||
*/
|
||||
public function edit(Tag $tag, TagRepositoryInterface $repository)
|
||||
public function edit(Tag $tag)
|
||||
{
|
||||
$subTitle = trans('firefly.edit_tag', ['tag' => $tag->tag]);
|
||||
$subTitleIcon = 'fa-tag';
|
||||
$apiKey = env('GOOGLE_MAPS_API_KEY', '');
|
||||
|
||||
/*
|
||||
* Default tag options (again)
|
||||
*/
|
||||
$tagOptions = $this->tagOptions;
|
||||
|
||||
/*
|
||||
* Can this tag become another type?
|
||||
*/
|
||||
$allowAdvance = $repository->tagAllowAdvance($tag);
|
||||
$allowToBalancingAct = $repository->tagAllowBalancing($tag);
|
||||
|
||||
// edit tag options:
|
||||
if ($allowAdvance === false) {
|
||||
unset($tagOptions['advancePayment']);
|
||||
}
|
||||
if ($allowToBalancingAct === false) {
|
||||
unset($tagOptions['balancingAct']);
|
||||
}
|
||||
|
||||
|
||||
// put previous url in session if not redirect from store (not "return_to_edit").
|
||||
if (session('tags.edit.fromUpdate') !== true) {
|
||||
$this->rememberPreviousUri('tags.edit.uri');
|
||||
@@ -181,10 +156,12 @@ class TagController extends Controller
|
||||
Session::flash('gaEventCategory', 'tags');
|
||||
Session::flash('gaEventAction', 'edit');
|
||||
|
||||
return view('tags.edit', compact('tag', 'subTitle', 'subTitleIcon', 'tagOptions', 'apiKey'));
|
||||
return view('tags.edit', compact('tag', 'subTitle', 'subTitleIcon', 'apiKey'));
|
||||
}
|
||||
|
||||
/**
|
||||
* View all tags
|
||||
*
|
||||
* @param TagRepositoryInterface $repository
|
||||
*
|
||||
* @return View
|
||||
@@ -194,6 +171,8 @@ class TagController extends Controller
|
||||
$title = 'Tags';
|
||||
$mainTitleIcon = 'fa-tags';
|
||||
$types = ['nothing', 'balancingAct', 'advancePayment'];
|
||||
$hasTypes = 0; // which types of tag the user actually has.
|
||||
$counts = []; // how many of each type?
|
||||
$count = $repository->count();
|
||||
|
||||
// loop each types and get the tags, group them by year.
|
||||
@@ -209,10 +188,13 @@ class TagController extends Controller
|
||||
return strtolower($date . $tag->tag);
|
||||
}
|
||||
);
|
||||
if ($tags->count() > 0) {
|
||||
$hasTypes++;
|
||||
}
|
||||
$counts[$type] = $tags->count();
|
||||
|
||||
/** @var Tag $tag */
|
||||
foreach ($tags as $tag) {
|
||||
|
||||
$year = is_null($tag->date) ? trans('firefly.no_year') : $tag->date->year;
|
||||
$monthFormatted = is_null($tag->date) ? trans('firefly.no_month') : $tag->date->formatLocalized($this->monthFormat);
|
||||
|
||||
@@ -220,7 +202,7 @@ class TagController extends Controller
|
||||
}
|
||||
}
|
||||
|
||||
return view('tags.index', compact('title', 'mainTitleIcon', 'types', 'collection', 'count'));
|
||||
return view('tags.index', compact('title', 'mainTitleIcon', 'counts', 'hasTypes', 'types', 'collection', 'count'));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -236,17 +218,15 @@ class TagController extends Controller
|
||||
// default values:
|
||||
$subTitle = $tag->tag;
|
||||
$subTitleIcon = 'fa-tag';
|
||||
$page = intval($request->get('page')) == 0 ? 1 : intval($request->get('page'));
|
||||
$page = intval($request->get('page'));
|
||||
$pageSize = intval(Preferences::get('transactionPageSize', 50)->data);
|
||||
$count = 0;
|
||||
$loop = 0;
|
||||
$range = Preferences::get('viewRange', '1M')->data;
|
||||
$start = null;
|
||||
$end = null;
|
||||
$periods = new Collection;
|
||||
$apiKey = env('GOOGLE_MAPS_API_KEY', '');
|
||||
$sum = '0';
|
||||
$path = 'tags/show/' . $tag->id;
|
||||
$path = route('tags.show', [$tag->id]);
|
||||
|
||||
|
||||
// prep for "all" view.
|
||||
@@ -254,8 +234,8 @@ class TagController extends Controller
|
||||
$subTitle = trans('firefly.all_journals_for_tag', ['tag' => $tag->tag]);
|
||||
$start = $repository->firstUseDate($tag);
|
||||
$end = new Carbon;
|
||||
$sum = $repository->sumOfTag($tag);
|
||||
$path = 'tags/show/' . $tag->id . '/all';
|
||||
$sum = $repository->sumOfTag($tag, null, null);
|
||||
$path = route('tags.show', [$tag->id, 'all']);
|
||||
}
|
||||
|
||||
// prep for "specific date" view.
|
||||
@@ -269,6 +249,7 @@ class TagController extends Controller
|
||||
);
|
||||
$periods = $this->getPeriodOverview($tag);
|
||||
$sum = $repository->sumOfTag($tag, $start, $end);
|
||||
$path = route('tags.show', [$tag->id, $moment]);
|
||||
}
|
||||
|
||||
// prep for current period
|
||||
@@ -281,32 +262,14 @@ class TagController extends Controller
|
||||
['tag' => $tag->tag, 'start' => $start->formatLocalized($this->monthAndDayFormat), 'end' => $end->formatLocalized($this->monthAndDayFormat)]
|
||||
);
|
||||
}
|
||||
// grab journals, but be prepared to jump a period back to get the right ones:
|
||||
Log::info('Now at tag loop start.');
|
||||
while ($count === 0 && $loop < 3) {
|
||||
$loop++;
|
||||
Log::info(sprintf('Count is zero, search for journals between %s and %s (pagesize %d, page %d).', $start, $end, $pageSize, $page));
|
||||
/** @var JournalCollectorInterface $collector */
|
||||
$collector = app(JournalCollectorInterface::class);
|
||||
$collector->setAllAssetAccounts()->setRange($start, $end)->setLimit($pageSize)->setPage($page)->withOpposingAccount()
|
||||
->setTag($tag)->withBudgetInformation()->withCategoryInformation()->removeFilter(InternalTransferFilter::class);
|
||||
$journals = $collector->getPaginatedJournals();
|
||||
$journals->setPath($path);
|
||||
$count = $journals->getCollection()->count();
|
||||
if ($count === 0 && $loop < 3) {
|
||||
$start->subDay();
|
||||
$start = Navigation::startOfPeriod($start, $range);
|
||||
$end = Navigation::endOfPeriod($start, $range);
|
||||
Log::info(sprintf('Count is still zero, go back in time to "%s" and "%s"!', $start->format('Y-m-d'), $end->format('Y-m-d')));
|
||||
}
|
||||
}
|
||||
|
||||
if ($moment != 'all' && $loop > 1) {
|
||||
$subTitle = trans(
|
||||
'firefly.journals_in_period_for_tag',
|
||||
['tag' => $tag->tag, 'start' => $start->formatLocalized($this->monthAndDayFormat), 'end' => $end->formatLocalized($this->monthAndDayFormat)]
|
||||
);
|
||||
}
|
||||
/** @var JournalCollectorInterface $collector */
|
||||
$collector = app(JournalCollectorInterface::class);
|
||||
$collector->setAllAssetAccounts()->setRange($start, $end)->setLimit($pageSize)->setPage($page)->withOpposingAccount()
|
||||
->setTag($tag)->withBudgetInformation()->withCategoryInformation()->removeFilter(InternalTransferFilter::class);
|
||||
$journals = $collector->getPaginatedJournals();
|
||||
$journals->setPath($path);
|
||||
|
||||
|
||||
return view('tags.show', compact('apiKey', 'tag', 'periods', 'subTitle', 'subTitleIcon', 'journals', 'sum', 'start', 'end', 'moment'));
|
||||
}
|
||||
|
||||
@@ -86,7 +86,7 @@ class MassController extends Controller
|
||||
foreach ($ids as $journalId) {
|
||||
/** @var TransactionJournal $journal */
|
||||
$journal = $repository->find(intval($journalId));
|
||||
if (!is_null($journal->id) && $journalId == $journal->id) {
|
||||
if (!is_null($journal->id) && intval($journalId) === $journal->id) {
|
||||
$set->push($journal);
|
||||
}
|
||||
}
|
||||
@@ -131,12 +131,11 @@ class MassController extends Controller
|
||||
$filtered = new Collection;
|
||||
$messages = [];
|
||||
/**
|
||||
* @var int $index
|
||||
* @var TransactionJournal $journal
|
||||
*/
|
||||
foreach ($journals as $index => $journal) {
|
||||
$sources = $journal->sourceAccountList($journal);
|
||||
$destinations = $journal->destinationAccountList($journal);
|
||||
foreach ($journals as $journal) {
|
||||
$sources = $journal->sourceAccountList();
|
||||
$destinations = $journal->destinationAccountList();
|
||||
if ($sources->count() > 1) {
|
||||
$messages[] = trans('firefly.cannot_edit_multiple_source', ['description' => $journal->description, 'id' => $journal->id]);
|
||||
continue;
|
||||
@@ -213,11 +212,11 @@ class MassController extends Controller
|
||||
if ($journal) {
|
||||
// get optional fields:
|
||||
$what = strtolower($journal->transactionTypeStr());
|
||||
$sourceAccountId = $request->get('source_account_id')[$journal->id] ?? 0;
|
||||
$sourceAccountId = $request->get('source_account_id')[$journal->id] ?? 0;
|
||||
$sourceAccountName = $request->get('source_account_name')[$journal->id] ?? '';
|
||||
$destAccountId = $request->get('destination_account_id')[$journal->id] ?? 0;
|
||||
$destAccountId = $request->get('destination_account_id')[$journal->id] ?? 0;
|
||||
$destAccountName = $request->get('destination_account_name')[$journal->id] ?? '';
|
||||
$budgetId = $request->get('budget_id')[$journal->id] ?? 0;
|
||||
$budgetId = $request->get('budget_id')[$journal->id] ?? 0;
|
||||
$category = $request->get('category')[$journal->id];
|
||||
$tags = $journal->tags->pluck('tag')->toArray();
|
||||
$amount = round($request->get('amount')[$journal->id], 12);
|
||||
|
||||
@@ -174,7 +174,7 @@ class SingleController extends Controller
|
||||
*
|
||||
* @param TransactionJournal $journal
|
||||
*
|
||||
* @return View
|
||||
* @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector|View
|
||||
*/
|
||||
public function delete(TransactionJournal $journal)
|
||||
{
|
||||
@@ -199,10 +199,10 @@ class SingleController extends Controller
|
||||
}
|
||||
|
||||
/**
|
||||
* @param JournalRepositoryInterface $repository
|
||||
* @param TransactionJournal $transactionJournal
|
||||
* @param TransactionJournal $transactionJournal
|
||||
*
|
||||
* @return \Illuminate\Http\RedirectResponse
|
||||
* @internal param JournalRepositoryInterface $repository
|
||||
*/
|
||||
public function destroy(TransactionJournal $transactionJournal)
|
||||
{
|
||||
|
||||
@@ -114,7 +114,7 @@ class SplitController extends Controller
|
||||
'transactions.split.edit',
|
||||
compact(
|
||||
'subTitleIcon', 'currencies', 'optionalFields',
|
||||
'preFilled', 'subTitle', 'amount', 'sourceAccounts', 'uploadSize', 'destinationAccounts', 'assetAccounts',
|
||||
'preFilled', 'subTitle', 'uploadSize', 'assetAccounts',
|
||||
'budgets', 'journal'
|
||||
)
|
||||
);
|
||||
|
||||
@@ -70,15 +70,13 @@ class TransactionController extends Controller
|
||||
// default values:
|
||||
$subTitleIcon = config('firefly.transactionIconsByWhat.' . $what);
|
||||
$types = config('firefly.transactionTypesByWhat.' . $what);
|
||||
$page = intval($request->get('page')) == 0 ? 1 : intval($request->get('page'));
|
||||
$page = intval($request->get('page'));
|
||||
$pageSize = intval(Preferences::get('transactionPageSize', 50)->data);
|
||||
$count = 0;
|
||||
$loop = 0;
|
||||
$range = Preferences::get('viewRange', '1M')->data;
|
||||
$start = null;
|
||||
$end = null;
|
||||
$periods = new Collection;
|
||||
$path = '/transactions/' . $what;
|
||||
$path = route('transactions.index', [$what]);
|
||||
|
||||
// prep for "all" view.
|
||||
if ($moment === 'all') {
|
||||
@@ -86,13 +84,14 @@ class TransactionController extends Controller
|
||||
$first = $repository->first();
|
||||
$start = $first->date ?? new Carbon;
|
||||
$end = new Carbon;
|
||||
$path = '/transactions/' . $what . '/all/';
|
||||
$path = route('transactions.index', [$what, 'all']);
|
||||
}
|
||||
|
||||
// prep for "specific date" view.
|
||||
if (strlen($moment) > 0 && $moment !== 'all') {
|
||||
$start = new Carbon($moment);
|
||||
$end = Navigation::endOfPeriod($start, $range);
|
||||
$path = route('transactions.index', [$what, $moment]);
|
||||
$subTitle = trans(
|
||||
'firefly.title_' . $what . '_between',
|
||||
['start' => $start->formatLocalized($this->monthAndDayFormat), 'end' => $end->formatLocalized($this->monthAndDayFormat)]
|
||||
@@ -110,32 +109,14 @@ class TransactionController extends Controller
|
||||
['start' => $start->formatLocalized($this->monthAndDayFormat), 'end' => $end->formatLocalized($this->monthAndDayFormat)]
|
||||
);
|
||||
}
|
||||
// grab journals, but be prepared to jump a period back to get the right ones:
|
||||
Log::info('Now at transaction loop start.');
|
||||
while ($count === 0 && $loop < 3) {
|
||||
$loop++;
|
||||
Log::info('Count is zero, search for journals.');
|
||||
/** @var JournalCollectorInterface $collector */
|
||||
$collector = app(JournalCollectorInterface::class);
|
||||
$collector->setAllAssetAccounts()->setRange($start, $end)->setTypes($types)->setLimit($pageSize)->setPage($page)->withOpposingAccount();
|
||||
$collector->removeFilter(InternalTransferFilter::class);
|
||||
$journals = $collector->getPaginatedJournals();
|
||||
$journals->setPath($path);
|
||||
$count = $journals->getCollection()->count();
|
||||
if ($count === 0 && $loop < 3) {
|
||||
$start->subDay();
|
||||
$start = Navigation::startOfPeriod($start, $range);
|
||||
$end = Navigation::endOfPeriod($start, $range);
|
||||
Log::info(sprintf('Count is still zero, go back in time to "%s" and "%s"!', $start->format('Y-m-d'), $end->format('Y-m-d')));
|
||||
}
|
||||
}
|
||||
|
||||
if ($moment != 'all' && $loop > 1) {
|
||||
$subTitle = trans(
|
||||
'firefly.title_' . $what . '_between',
|
||||
['start' => $start->formatLocalized($this->monthAndDayFormat), 'end' => $end->formatLocalized($this->monthAndDayFormat)]
|
||||
);
|
||||
}
|
||||
/** @var JournalCollectorInterface $collector */
|
||||
$collector = app(JournalCollectorInterface::class);
|
||||
$collector->setAllAssetAccounts()->setRange($start, $end)->setTypes($types)->setLimit($pageSize)->setPage($page)->withOpposingAccount();
|
||||
$collector->removeFilter(InternalTransferFilter::class);
|
||||
$journals = $collector->getPaginatedJournals();
|
||||
$journals->setPath($path);
|
||||
|
||||
|
||||
return view('transactions.index', compact('subTitle', 'what', 'subTitleIcon', 'journals', 'periods', 'start', 'end', 'moment'));
|
||||
|
||||
@@ -172,7 +153,7 @@ class TransactionController extends Controller
|
||||
* @param TransactionJournal $journal
|
||||
* @param JournalTaskerInterface $tasker
|
||||
*
|
||||
* @return View
|
||||
* @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector|View
|
||||
*/
|
||||
public function show(TransactionJournal $journal, JournalTaskerInterface $tasker)
|
||||
{
|
||||
@@ -203,7 +184,7 @@ class TransactionController extends Controller
|
||||
$start = $first->date ?? new Carbon;
|
||||
$range = Preferences::get('viewRange', '1M')->data;
|
||||
$start = Navigation::startOfPeriod($start, $range);
|
||||
$end = Navigation::endOfX(new Carbon, $range);
|
||||
$end = Navigation::endOfX(new Carbon, $range, null);
|
||||
$entries = new Collection;
|
||||
$types = config('firefly.transactionTypesByWhat.' . $what);
|
||||
|
||||
|
||||
@@ -37,7 +37,7 @@ class ProfileFormRequest extends Request
|
||||
{
|
||||
return [
|
||||
'current_password' => 'required',
|
||||
'new_password' => 'required|confirmed',
|
||||
'new_password' => 'required|confirmed|secure_password',
|
||||
'new_password_confirmation' => 'required',
|
||||
];
|
||||
}
|
||||
|
||||
@@ -37,7 +37,7 @@ class TagFormRequest extends Request
|
||||
*/
|
||||
public function collectTagData(): array
|
||||
{
|
||||
if ($this->get('setTag') == 'true') {
|
||||
if ($this->get('setTag') === 'true') {
|
||||
$latitude = $this->string('latitude');
|
||||
$longitude = $this->string('longitude');
|
||||
$zoomLevel = $this->integer('zoomLevel');
|
||||
@@ -54,7 +54,6 @@ class TagFormRequest extends Request
|
||||
'latitude' => $latitude,
|
||||
'longitude' => $longitude,
|
||||
'zoomLevel' => $zoomLevel,
|
||||
'tagMode' => $this->string('tagMode'),
|
||||
];
|
||||
|
||||
return $data;
|
||||
@@ -84,7 +83,6 @@ class TagFormRequest extends Request
|
||||
'latitude' => 'numeric|min:-90|max:90',
|
||||
'longitude' => 'numeric|min:-90|max:90',
|
||||
'zoomLevel' => 'numeric|min:0|max:80',
|
||||
'tagMode' => 'required|in:nothing,balancingAct,advancePayment',
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -51,7 +51,7 @@ class UserFormRequest extends Request
|
||||
return [
|
||||
'id' => 'required|exists:users,id',
|
||||
'email' => 'email|required',
|
||||
'password' => 'confirmed',
|
||||
'password' => 'confirmed|secure_password',
|
||||
'blocked_code' => 'between:0,30',
|
||||
'blocked' => 'between:0,1|numeric',
|
||||
];
|
||||
|
||||
42
app/Http/Requests/UserRegistrationRequest.php
Normal file
42
app/Http/Requests/UserRegistrationRequest.php
Normal file
@@ -0,0 +1,42 @@
|
||||
<?php
|
||||
/**
|
||||
* UserRegistrationRequest.php
|
||||
* Copyright (c) 2017 thegrumpydictator@gmail.com
|
||||
* This software may be modified and distributed under the terms of the Creative Commons Attribution-ShareAlike 4.0 International License.
|
||||
*
|
||||
* See the LICENSE file for details.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\Http\Requests;
|
||||
|
||||
/**
|
||||
* Class UserRegistrationRequest
|
||||
*
|
||||
*
|
||||
* @package FireflyIII\Http\Requests
|
||||
*/
|
||||
class UserRegistrationRequest extends Request
|
||||
{
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function authorize()
|
||||
{
|
||||
// Only everybody
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function rules()
|
||||
{
|
||||
return [
|
||||
'email' => 'email|required',
|
||||
'password' => 'confirmed|secure_password',
|
||||
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -10,6 +10,7 @@
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use Carbon\Carbon;
|
||||
use DaveJamesMiller\Breadcrumbs\Generator as BreadCrumbGenerator;
|
||||
use FireflyIII\Exceptions\FireflyException;
|
||||
@@ -470,16 +471,26 @@ Breadcrumbs::register(
|
||||
}
|
||||
);
|
||||
|
||||
/**
|
||||
* FILE IMPORT
|
||||
*/
|
||||
Breadcrumbs::register(
|
||||
'import.configure', function (BreadCrumbGenerator $breadcrumbs, ImportJob $job) {
|
||||
'import.file.index', function (BreadCrumbGenerator $breadcrumbs) {
|
||||
$breadcrumbs->parent('import.index');
|
||||
$breadcrumbs->push(trans('firefly.import_config_sub_title', ['key' => $job->key]), route('import.configure', [$job->key]));
|
||||
$breadcrumbs->push(trans('firefly.import_file'), route('import.file.index'));
|
||||
}
|
||||
);
|
||||
|
||||
Breadcrumbs::register(
|
||||
'import.file.configure', function (BreadCrumbGenerator $breadcrumbs, ImportJob $job) {
|
||||
$breadcrumbs->parent('import.file.index');
|
||||
$breadcrumbs->push(trans('firefly.import_config_sub_title', ['key' => $job->key]), route('import.file.configure', [$job->key]));
|
||||
}
|
||||
);
|
||||
Breadcrumbs::register(
|
||||
'import.status', function (BreadCrumbGenerator $breadcrumbs, ImportJob $job) {
|
||||
$breadcrumbs->parent('import.index');
|
||||
$breadcrumbs->push(trans('firefly.import_status_bread_crumb', ['key' => $job->key]), route('import.status', [$job->key]));
|
||||
'import.file.status', function (BreadCrumbGenerator $breadcrumbs, ImportJob $job) {
|
||||
$breadcrumbs->parent('import.file.index');
|
||||
$breadcrumbs->push(trans('firefly.import_status_bread_crumb', ['key' => $job->key]), route('import.file.status', [$job->key]));
|
||||
}
|
||||
);
|
||||
|
||||
@@ -680,7 +691,7 @@ Breadcrumbs::register(
|
||||
Breadcrumbs::register(
|
||||
'search.index', function (BreadCrumbGenerator $breadcrumbs, $query) {
|
||||
$breadcrumbs->parent('home');
|
||||
$breadcrumbs->push(trans('breadcrumbs.searchResult', ['query' => e($query)]), route('search.index'));
|
||||
$breadcrumbs->push(trans('breadcrumbs.search_result', ['query' => e($query)]), route('search.index'));
|
||||
}
|
||||
);
|
||||
|
||||
@@ -804,7 +815,7 @@ Breadcrumbs::register(
|
||||
* MASS TRANSACTION EDIT / DELETE
|
||||
*/
|
||||
Breadcrumbs::register(
|
||||
'transactions.mass.edit', function (BreadCrumbGenerator $breadcrumbs, Collection $journals) {
|
||||
'transactions.mass.edit', function (BreadCrumbGenerator $breadcrumbs, Collection $journals): void {
|
||||
|
||||
if ($journals->count() > 0) {
|
||||
$journalIds = $journals->pluck('id')->toArray();
|
||||
@@ -816,6 +827,8 @@ Breadcrumbs::register(
|
||||
}
|
||||
|
||||
$breadcrumbs->parent('index');
|
||||
|
||||
return;
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
@@ -48,6 +48,13 @@ interface ConfiguratorInterface
|
||||
*/
|
||||
public function getNextView(): string;
|
||||
|
||||
/**
|
||||
* Return possible warning to user.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getWarningMessage(): string;
|
||||
|
||||
/**
|
||||
* Returns true when the initial configuration for this job is complete.
|
||||
*
|
||||
|
||||
@@ -26,8 +26,12 @@ use Log;
|
||||
*/
|
||||
class CsvConfigurator implements ConfiguratorInterface
|
||||
{
|
||||
/** @var ImportJob */
|
||||
private $job;
|
||||
|
||||
/** @var string */
|
||||
private $warning = '';
|
||||
|
||||
/**
|
||||
* ConfiguratorInterface constructor.
|
||||
*/
|
||||
@@ -50,8 +54,10 @@ class CsvConfigurator implements ConfiguratorInterface
|
||||
/** @var ConfigurationInterface $object */
|
||||
$object = new $class($this->job);
|
||||
$object->setJob($job);
|
||||
$result = $object->storeConfiguration($data);
|
||||
$this->warning = $object->getWarningMessage();
|
||||
|
||||
return $object->storeConfiguration($data);
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -91,6 +97,16 @@ class CsvConfigurator implements ConfiguratorInterface
|
||||
throw new FireflyException('No view for state');
|
||||
}
|
||||
|
||||
/**
|
||||
* Return possible warning to user.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getWarningMessage(): string
|
||||
{
|
||||
return $this->warning;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
|
||||
@@ -29,6 +29,8 @@ class Amount implements ConverterInterface
|
||||
* @param $value
|
||||
*
|
||||
* @return string
|
||||
*
|
||||
* @SuppressWarnings(PHPMD.CyclomaticComplexity)
|
||||
*/
|
||||
public function convert($value): string
|
||||
{
|
||||
@@ -36,10 +38,10 @@ class Amount implements ConverterInterface
|
||||
$decimalPosition = $len - 3;
|
||||
$decimal = null;
|
||||
|
||||
if (($len > 2 && $value{$decimalPosition} == '.') || ($len > 2 && strpos($value, '.') > $decimalPosition)) {
|
||||
if (($len > 2 && $value{$decimalPosition} === '.') || ($len > 2 && strpos($value, '.') > $decimalPosition)) {
|
||||
$decimal = '.';
|
||||
}
|
||||
if ($len > 2 && $value{$decimalPosition} == ',') {
|
||||
if ($len > 2 && $value{$decimalPosition} === ',') {
|
||||
$decimal = ',';
|
||||
}
|
||||
|
||||
|
||||
@@ -58,7 +58,7 @@ class CsvProcessor implements FileProcessorInterface
|
||||
}
|
||||
|
||||
/**
|
||||
* Does the actual job:
|
||||
* Does the actual job.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
@@ -66,34 +66,46 @@ class CsvProcessor implements FileProcessorInterface
|
||||
{
|
||||
Log::debug('Now in CsvProcessor run(). Job is now running...');
|
||||
|
||||
$entries = $this->getImportArray();
|
||||
$index = 0;
|
||||
$entries = new Collection($this->getImportArray());
|
||||
Log::notice('Building importable objects from CSV file.');
|
||||
foreach ($entries as $index => $row) {
|
||||
// verify if not exists already:
|
||||
if ($this->rowAlreadyImported($row)) {
|
||||
$message = sprintf('Row #%d has already been imported.', $index);
|
||||
$this->job->addError($index, $message);
|
||||
$this->job->addStepsDone(5); // all steps.
|
||||
Log::info($message);
|
||||
continue;
|
||||
}
|
||||
$this->objects->push($this->importRow($index, $row));
|
||||
$this->job->addStepsDone(1);
|
||||
}
|
||||
// if job has no step count, set it now:
|
||||
$extended = $this->job->extended_status;
|
||||
if ($extended['steps'] === 0) {
|
||||
$extended['steps'] = $index * 5;
|
||||
$this->job->extended_status = $extended;
|
||||
$this->job->save();
|
||||
}
|
||||
Log::debug(sprintf('Number of entries: %d', $entries->count()));
|
||||
$notImported = $entries->filter(
|
||||
function (array $row, int $index) {
|
||||
if ($this->rowAlreadyImported($row)) {
|
||||
$message = sprintf('Row #%d has already been imported.', $index);
|
||||
$this->job->addError($index, $message);
|
||||
$this->job->addStepsDone(5); // all steps.
|
||||
Log::info($message);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
return $row;
|
||||
}
|
||||
);
|
||||
Log::debug(sprintf('Number of entries left: %d', $notImported->count()));
|
||||
|
||||
// set (new) number of steps:
|
||||
$status = $this->job->extended_status;
|
||||
$status['steps'] = $notImported->count() * 5;
|
||||
$this->job->extended_status = $status;
|
||||
$this->job->save();
|
||||
Log::debug(sprintf('Number of steps: %d', $notImported->count() * 5));
|
||||
|
||||
$notImported->each(
|
||||
function (array $row, int $index) {
|
||||
$journal = $this->importRow($index, $row);
|
||||
$this->objects->push($journal);
|
||||
$this->job->addStepsDone(1);
|
||||
}
|
||||
);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set import job for this processor.
|
||||
*
|
||||
* @param ImportJob $job
|
||||
*
|
||||
* @return FileProcessorInterface
|
||||
@@ -116,7 +128,6 @@ class CsvProcessor implements FileProcessorInterface
|
||||
*/
|
||||
private function annotateValue(int $index, string $value)
|
||||
{
|
||||
$value = trim($value);
|
||||
$config = $this->job->configuration;
|
||||
$role = $config['column-roles'][$index] ?? '_ignore';
|
||||
$mapped = $config['column-mapping-config'][$index][$value] ?? null;
|
||||
@@ -151,6 +162,64 @@ class CsvProcessor implements FileProcessorInterface
|
||||
return $results;
|
||||
}
|
||||
|
||||
/**
|
||||
* Will return string representation of JSON error code.
|
||||
*
|
||||
* @param int $jsonError
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private function getJsonError(int $jsonError): string
|
||||
{
|
||||
switch ($jsonError) {
|
||||
default:
|
||||
return 'Unknown JSON error';
|
||||
case JSON_ERROR_NONE:
|
||||
return 'No JSON error';
|
||||
case JSON_ERROR_DEPTH:
|
||||
return 'JSON_ERROR_DEPTH';
|
||||
case JSON_ERROR_STATE_MISMATCH:
|
||||
return 'JSON_ERROR_STATE_MISMATCH';
|
||||
case JSON_ERROR_CTRL_CHAR:
|
||||
return 'JSON_ERROR_CTRL_CHAR';
|
||||
case JSON_ERROR_SYNTAX:
|
||||
return 'JSON_ERROR_SYNTAX';
|
||||
case JSON_ERROR_UTF8:
|
||||
return 'JSON_ERROR_UTF8';
|
||||
case JSON_ERROR_RECURSION:
|
||||
return 'JSON_ERROR_RECURSION';
|
||||
case JSON_ERROR_INF_OR_NAN:
|
||||
return 'JSON_ERROR_INF_OR_NAN';
|
||||
case JSON_ERROR_UNSUPPORTED_TYPE:
|
||||
return 'JSON_ERROR_UNSUPPORTED_TYPE';
|
||||
case JSON_ERROR_INVALID_PROPERTY_NAME:
|
||||
return 'JSON_ERROR_INVALID_PROPERTY_NAME';
|
||||
case JSON_ERROR_UTF16:
|
||||
return 'JSON_ERROR_UTF16';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Hash an array and return the result.
|
||||
*
|
||||
* @param array $array
|
||||
*
|
||||
* @return string
|
||||
* @throws FireflyException
|
||||
*/
|
||||
private function getRowHash(array $array): string
|
||||
{
|
||||
$json = json_encode($array);
|
||||
$jsonError = json_last_error();
|
||||
|
||||
if ($json === false) {
|
||||
throw new FireflyException(sprintf('Error while encoding JSON for CSV row: %s', $this->getJsonError($jsonError)));
|
||||
}
|
||||
$hash = hash('sha256', $json);
|
||||
|
||||
return $hash;
|
||||
}
|
||||
|
||||
/**
|
||||
* Take a row, build import journal by annotating each value and storing it in the import journal.
|
||||
*
|
||||
@@ -158,15 +227,22 @@ class CsvProcessor implements FileProcessorInterface
|
||||
* @param array $row
|
||||
*
|
||||
* @return ImportJournal
|
||||
* @throws FireflyException
|
||||
*/
|
||||
private function importRow(int $index, array $row): ImportJournal
|
||||
{
|
||||
Log::debug(sprintf('Now at row %d', $index));
|
||||
$row = $this->specifics($row);
|
||||
$row = $this->specifics($row);
|
||||
$hash = $this->getRowHash($row);
|
||||
|
||||
$journal = new ImportJournal;
|
||||
$journal->setUser($this->job->user);
|
||||
$journal->setHash(hash('sha256', json_encode($row)));
|
||||
$journal->setHash($hash);
|
||||
|
||||
/**
|
||||
* @var int $rowIndex
|
||||
* @var string $value
|
||||
*/
|
||||
foreach ($row as $rowIndex => $value) {
|
||||
$value = trim($value);
|
||||
if (strlen($value) > 0) {
|
||||
@@ -175,7 +251,8 @@ class CsvProcessor implements FileProcessorInterface
|
||||
$journal->setValue($annotated);
|
||||
}
|
||||
}
|
||||
Log::debug('ImportJournal complete, returning.');
|
||||
// set some extra info:
|
||||
$journal->asset->setDefaultAccountId($this->job->configuration['import-account']);
|
||||
|
||||
return $journal;
|
||||
}
|
||||
@@ -189,13 +266,12 @@ class CsvProcessor implements FileProcessorInterface
|
||||
*/
|
||||
private function rowAlreadyImported(array $array): bool
|
||||
{
|
||||
$string = json_encode($array);
|
||||
$hash = hash('sha256', json_encode($string));
|
||||
$json = json_encode($hash);
|
||||
$entry = TransactionJournalMeta::leftJoin('transaction_journals', 'transaction_journals.id', '=', 'journal_meta.transaction_journal_id')
|
||||
->where('data', $json)
|
||||
->where('name', 'importHash')
|
||||
->first();
|
||||
$hash = $this->getRowHash($array);
|
||||
$json = json_encode($hash);
|
||||
$entry = TransactionJournalMeta::leftJoin('transaction_journals', 'transaction_journals.id', '=', 'journal_meta.transaction_journal_id')
|
||||
->where('data', $json)
|
||||
->where('name', 'importHash')
|
||||
->first();
|
||||
if (!is_null($entry)) {
|
||||
return true;
|
||||
}
|
||||
@@ -215,8 +291,8 @@ class CsvProcessor implements FileProcessorInterface
|
||||
private function specifics(array $row): array
|
||||
{
|
||||
$config = $this->job->configuration;
|
||||
//
|
||||
foreach ($config['specifics'] as $name => $enabled) {
|
||||
$names = array_keys($config['specifics']);
|
||||
foreach ($names as $name) {
|
||||
|
||||
if (!in_array($name, $this->validSpecifics)) {
|
||||
throw new FireflyException(sprintf('"%s" is not a valid class name', $name));
|
||||
|
||||
@@ -36,7 +36,8 @@ class CommandHandler extends AbstractProcessingHandler
|
||||
{
|
||||
parent::__construct();
|
||||
$this->command = $command;
|
||||
$this->changeLevel(env('LOG_LEVEL', 'debug'));
|
||||
|
||||
$this->changeLevel(env('APP_LOG_LEVEL', 'info'));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -56,9 +57,10 @@ class CommandHandler extends AbstractProcessingHandler
|
||||
*/
|
||||
private function changeLevel(string $level)
|
||||
{
|
||||
$level = strtoupper($level);
|
||||
if (defined(sprintf('Logger::%s', $level))) {
|
||||
$this->setLevel(constant(sprintf('Logger::%s', $level)));
|
||||
$level = strtoupper($level);
|
||||
$reference = sprintf('\Monolog\Logger::%s', $level);
|
||||
if (defined($reference)) {
|
||||
$this->setLevel(constant($reference));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,7 +42,7 @@ class AssetAccountIbans implements MapperInterface
|
||||
if (strlen($iban) > 0) {
|
||||
$topList[$account->id] = $account->iban . ' (' . $account->name . ')';
|
||||
}
|
||||
if (strlen($iban) == 0) {
|
||||
if (strlen($iban) === 0) {
|
||||
$list[$account->id] = $account->name;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -48,7 +48,7 @@ class OpposingAccountIbans implements MapperInterface
|
||||
if (strlen($iban) > 0) {
|
||||
$topList[$account->id] = $account->iban . ' (' . $account->name . ')';
|
||||
}
|
||||
if (strlen($iban) == 0) {
|
||||
if (strlen($iban) === 0) {
|
||||
$list[$account->id] = $account->name;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@ use Illuminate\Support\Collection;
|
||||
use Log;
|
||||
|
||||
/**
|
||||
*
|
||||
* Class ImportAccount
|
||||
*
|
||||
* @package FireflyIII\Import\Object
|
||||
@@ -37,8 +38,18 @@ class ImportAccount
|
||||
private $accountName = [];
|
||||
/** @var array */
|
||||
private $accountNumber = [];
|
||||
/** @var int */
|
||||
private $defaultAccountId = 0;
|
||||
/** @var string */
|
||||
private $expectedType = '';
|
||||
/**
|
||||
* This value is used to indicate the other account ID (the opposing transaction's account),
|
||||
* if it is know. If so, this particular importaccount may never return an Account with this ID.
|
||||
* If it would, this would result in a transaction from-to the same account.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
private $forbiddenAccountId = 0;
|
||||
/** @var AccountRepositoryInterface */
|
||||
private $repository;
|
||||
/** @var User */
|
||||
@@ -115,6 +126,22 @@ class ImportAccount
|
||||
$this->accountNumber = $accountNumber;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $defaultAccountId
|
||||
*/
|
||||
public function setDefaultAccountId(int $defaultAccountId)
|
||||
{
|
||||
$this->defaultAccountId = $defaultAccountId;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $forbiddenAccountId
|
||||
*/
|
||||
public function setForbiddenAccountId(int $forbiddenAccountId)
|
||||
{
|
||||
$this->forbiddenAccountId = $forbiddenAccountId;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param User $user
|
||||
*/
|
||||
@@ -138,7 +165,9 @@ class ImportAccount
|
||||
if (count($this->accountId) === 3) {
|
||||
Log::debug(sprintf('Finding account of type %d and ID %d', $accountType->id, $this->accountId['value']));
|
||||
/** @var Account $account */
|
||||
$account = $this->user->accounts()->where('account_type_id', $accountType->id)->where('id', $this->accountId['value'])->first();
|
||||
$account = $this->user->accounts()->where('id', '!=', $this->forbiddenAccountId)->where('account_type_id', $accountType->id)->where(
|
||||
'id', $this->accountId['value']
|
||||
)->first();
|
||||
if (!is_null($account)) {
|
||||
Log::debug(sprintf('Found unmapped %s account by ID (#%d): %s', $this->expectedType, $account->id, $account->name));
|
||||
|
||||
@@ -154,7 +183,7 @@ class ImportAccount
|
||||
Log::debug(sprintf('Finding account of type %d and IBAN %s', $accountType->id, $iban));
|
||||
$filtered = $accounts->filter(
|
||||
function (Account $account) use ($iban) {
|
||||
if ($account->iban === $iban) {
|
||||
if ($account->iban === $iban && $account->id !== $this->forbiddenAccountId) {
|
||||
Log::debug(
|
||||
sprintf('Found unmapped %s account by IBAN (#%d): %s (%s)', $this->expectedType, $account->id, $account->name, $account->iban)
|
||||
);
|
||||
@@ -177,7 +206,7 @@ class ImportAccount
|
||||
Log::debug(sprintf('Finding account of type %d and name %s', $accountType->id, $name));
|
||||
$filtered = $accounts->filter(
|
||||
function (Account $account) use ($name) {
|
||||
if ($account->name === $name) {
|
||||
if ($account->name === $name && $account->id !== $this->forbiddenAccountId) {
|
||||
Log::debug(sprintf('Found unmapped %s account by name (#%d): %s', $this->expectedType, $account->id, $account->name));
|
||||
|
||||
return $account;
|
||||
@@ -249,13 +278,22 @@ class ImportAccount
|
||||
$search = intval($array['mapped']);
|
||||
$account = $this->repository->find($search);
|
||||
|
||||
if ($account->accountType->type !== $this->expectedType) {
|
||||
if (is_null($account->id)) {
|
||||
Log::error(sprintf('There is no account with id #%d. Invalid mapping will be ignored!', $search));
|
||||
|
||||
return new Account;
|
||||
}
|
||||
// must be of the same type
|
||||
// except when mapped is an asset, then it's fair game.
|
||||
// which only shows that user must map very carefully.
|
||||
if ($account->accountType->type !== $this->expectedType && $account->accountType->type !== AccountType::ASSET) {
|
||||
Log::error(
|
||||
sprintf(
|
||||
'Mapped account #%d is of type "%s" but we expect a "%s"-account. Mapping will be ignored.', $account->id, $account->accountType->type,
|
||||
$this->expectedType
|
||||
)
|
||||
);
|
||||
|
||||
return new Account;
|
||||
}
|
||||
|
||||
@@ -297,6 +335,15 @@ class ImportAccount
|
||||
}
|
||||
$this->expectedType = $oldExpectedType;
|
||||
|
||||
// 4: if search for an asset account, fall back to given "default account" (mandatory)
|
||||
if ($this->expectedType === AccountType::ASSET) {
|
||||
$this->account = $this->repository->find($this->defaultAccountId);
|
||||
Log::debug(sprintf('Fall back to default account #%d "%s"', $this->account->id, $this->account->name));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// 5: then maybe, create one:
|
||||
Log::debug(sprintf('Found no account of type %s so must create one ourselves.', $this->expectedType));
|
||||
|
||||
$data = [
|
||||
|
||||
@@ -180,12 +180,19 @@ class ImportBill
|
||||
|
||||
Log::debug('Finding a mapped bill based on', $array);
|
||||
|
||||
$search = intval($array['mapped']);
|
||||
$account = $this->repository->find($search);
|
||||
$search = intval($array['mapped']);
|
||||
$bill = $this->repository->find($search);
|
||||
|
||||
Log::debug(sprintf('Found bill! #%d ("%s"). Return it', $account->id, $account->name));
|
||||
if (is_null($bill->id)) {
|
||||
Log::error(sprintf('There is no bill with id #%d. Invalid mapping will be ignored!', $search));
|
||||
|
||||
return $account;
|
||||
return new Bill;
|
||||
}
|
||||
|
||||
|
||||
Log::debug(sprintf('Found bill! #%d ("%s"). Return it', $bill->id, $bill->name));
|
||||
|
||||
return $bill;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -180,12 +180,18 @@ class ImportBudget
|
||||
|
||||
Log::debug('Finding a mapped budget based on', $array);
|
||||
|
||||
$search = intval($array['mapped']);
|
||||
$account = $this->repository->find($search);
|
||||
$search = intval($array['mapped']);
|
||||
$budget = $this->repository->find($search);
|
||||
|
||||
Log::debug(sprintf('Found budget! #%d ("%s"). Return it', $account->id, $account->name));
|
||||
if (is_null($budget->id)) {
|
||||
Log::error(sprintf('There is no budget with id #%d. Invalid mapping will be ignored!', $search));
|
||||
|
||||
return $account;
|
||||
return new Budget;
|
||||
}
|
||||
|
||||
Log::debug(sprintf('Found budget! #%d ("%s"). Return it', $budget->id, $budget->name));
|
||||
|
||||
return $budget;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -174,12 +174,18 @@ class ImportCategory
|
||||
|
||||
Log::debug('Finding a mapped category based on', $array);
|
||||
|
||||
$search = intval($array['mapped']);
|
||||
$account = $this->repository->find($search);
|
||||
$search = intval($array['mapped']);
|
||||
$category = $this->repository->find($search);
|
||||
|
||||
Log::debug(sprintf('Found category! #%d ("%s"). Return it', $account->id, $account->name));
|
||||
if (is_null($category->id)) {
|
||||
Log::error(sprintf('There is no category with id #%d. Invalid mapping will be ignored!', $search));
|
||||
|
||||
return $account;
|
||||
return new Category;
|
||||
}
|
||||
|
||||
Log::debug(sprintf('Found category! #%d ("%s"). Return it', $category->id, $category->name));
|
||||
|
||||
return $category;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -209,6 +209,13 @@ class ImportCurrency
|
||||
$search = intval($array['mapped']);
|
||||
$currency = $this->repository->find($search);
|
||||
|
||||
|
||||
if (is_null($currency->id)) {
|
||||
Log::error(sprintf('There is no currency with id #%d. Invalid mapping will be ignored!', $search));
|
||||
|
||||
return new TransactionCurrency;
|
||||
}
|
||||
|
||||
Log::debug(sprintf('Found currency! #%d ("%s"). Return it', $currency->id, $currency->name));
|
||||
|
||||
return $currency;
|
||||
|
||||
@@ -16,9 +16,8 @@ use Carbon\Carbon;
|
||||
use FireflyIII\Exceptions\FireflyException;
|
||||
use FireflyIII\Import\Converter\Amount;
|
||||
use FireflyIII\Import\Converter\ConverterInterface;
|
||||
use FireflyIII\Models\TransactionJournal;
|
||||
use FireflyIII\Import\MapperPreProcess\PreProcessorInterface;
|
||||
use FireflyIII\User;
|
||||
use Illuminate\Support\Collection;
|
||||
use InvalidArgumentException;
|
||||
use Log;
|
||||
use Steam;
|
||||
@@ -40,8 +39,6 @@ class ImportJournal
|
||||
public $category;
|
||||
/** @var string */
|
||||
public $description = '';
|
||||
/** @var Collection */
|
||||
public $errors;
|
||||
/** @var string */
|
||||
public $hash;
|
||||
/** @var array */
|
||||
@@ -50,18 +47,18 @@ class ImportJournal
|
||||
public $notes = '';
|
||||
/** @var ImportAccount */
|
||||
public $opposing;
|
||||
/** @var array */
|
||||
public $tags = [];
|
||||
/** @var string */
|
||||
private $amount = '0';
|
||||
private $amount;
|
||||
/** @var ImportCurrency */
|
||||
private $currency;
|
||||
public $currency;
|
||||
/** @var string */
|
||||
private $date = '';
|
||||
/** @var string */
|
||||
private $externalId = '';
|
||||
/** @var array */
|
||||
private $modifiers = [];
|
||||
/** @var array */
|
||||
private $tags = [];
|
||||
/** @var User */
|
||||
private $user;
|
||||
|
||||
@@ -70,7 +67,6 @@ class ImportJournal
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$this->errors = new Collection;
|
||||
$this->asset = new ImportAccount;
|
||||
$this->opposing = new ImportAccount;
|
||||
$this->bill = new ImportBill;
|
||||
@@ -87,45 +83,29 @@ class ImportJournal
|
||||
$this->modifiers[] = $modifier;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return TransactionJournal
|
||||
* @throws FireflyException
|
||||
*/
|
||||
public function createTransactionJournal(): TransactionJournal
|
||||
{
|
||||
exit('does not work yet');
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getAmount(): string
|
||||
{
|
||||
|
||||
/** @var ConverterInterface $amountConverter */
|
||||
$amountConverter = app(Amount::class);
|
||||
$this->amount = $amountConverter->convert($this->amount);
|
||||
// modify
|
||||
foreach ($this->modifiers as $modifier) {
|
||||
$class = sprintf('FireflyIII\Import\Converter\%s', config(sprintf('csv.import_roles.%s.converter', $modifier['role'])));
|
||||
/** @var ConverterInterface $converter */
|
||||
$converter = app($class);
|
||||
if ($converter->convert($modifier['value']) === -1) {
|
||||
$this->amount = Steam::negative($this->amount);
|
||||
if (is_null($this->amount)) {
|
||||
/** @var ConverterInterface $amountConverter */
|
||||
$amountConverter = app(Amount::class);
|
||||
$this->amount = $amountConverter->convert($this->amount);
|
||||
// modify
|
||||
foreach ($this->modifiers as $modifier) {
|
||||
$class = sprintf('FireflyIII\Import\Converter\%s', config(sprintf('csv.import_roles.%s.converter', $modifier['role'])));
|
||||
/** @var ConverterInterface $converter */
|
||||
$converter = app($class);
|
||||
if ($converter->convert($modifier['value']) === -1) {
|
||||
$this->amount = Steam::negative($this->amount);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $this->amount;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return ImportCurrency
|
||||
*/
|
||||
public function getCurrency(): ImportCurrency
|
||||
{
|
||||
return $this->currency;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $format
|
||||
*
|
||||
@@ -144,6 +124,18 @@ class ImportJournal
|
||||
return $date;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getDescription(): string
|
||||
{
|
||||
if ($this->description === '') {
|
||||
return '(no description)';
|
||||
}
|
||||
|
||||
return $this->description;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $hash
|
||||
*/
|
||||
@@ -226,7 +218,7 @@ class ImportJournal
|
||||
$this->date = $array['value'];
|
||||
break;
|
||||
case 'description':
|
||||
$this->description = $array['value'];
|
||||
$this->description .= $array['value'];
|
||||
break;
|
||||
case 'sepa-ct-op':
|
||||
case 'sepa-ct-id':
|
||||
@@ -257,7 +249,7 @@ class ImportJournal
|
||||
break;
|
||||
case 'tags-comma':
|
||||
case 'tags-space':
|
||||
$this->tags[] = $array;
|
||||
$this->setTags($array);
|
||||
break;
|
||||
case 'date-interest':
|
||||
$this->metaDates['interest_date'] = $array['value'];
|
||||
@@ -270,4 +262,18 @@ class ImportJournal
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $array
|
||||
*/
|
||||
private function setTags(array $array): void
|
||||
{
|
||||
$preProcessorClass = config(sprintf('csv.import_roles.%s.pre-process-mapper', $array['role']));
|
||||
/** @var PreProcessorInterface $preProcessor */
|
||||
$preProcessor = app(sprintf('\FireflyIII\Import\MapperPreProcess\%s', $preProcessorClass));
|
||||
$tags = $preProcessor->run($array['value']);
|
||||
$this->tags = array_merge($this->tags, $tags);
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,11 +13,11 @@ namespace FireflyIII\Import\Routine;
|
||||
|
||||
|
||||
use Carbon\Carbon;
|
||||
use DB;
|
||||
use FireflyIII\Import\FileProcessor\FileProcessorInterface;
|
||||
use FireflyIII\Import\Storage\ImportStorage;
|
||||
use FireflyIII\Models\ImportJob;
|
||||
use FireflyIII\Models\Tag;
|
||||
use FireflyIII\Models\TransactionJournal;
|
||||
use FireflyIII\Repositories\Tag\TagRepositoryInterface;
|
||||
use Illuminate\Support\Collection;
|
||||
use Log;
|
||||
@@ -64,14 +64,19 @@ class ImportRoutine
|
||||
Log::info(sprintf('Returned %d valid objects from file processor', $this->lines));
|
||||
|
||||
$storage = $this->storeObjects($importObjects);
|
||||
Log::debug('Back in run()');
|
||||
|
||||
// update job:
|
||||
$this->job->status = 'finished';
|
||||
$this->job->save();
|
||||
|
||||
Log::debug('Updated job...');
|
||||
|
||||
$this->journals = $storage->journals;
|
||||
$this->errors = $storage->errors;
|
||||
|
||||
Log::debug('Going to call createImportTag()');
|
||||
|
||||
// create tag, link tag to all journals:
|
||||
$this->createImportTag();
|
||||
|
||||
@@ -101,7 +106,7 @@ class ImportRoutine
|
||||
$processor = app($class);
|
||||
$processor->setJob($this->job);
|
||||
|
||||
if ($this->job->status == 'configured') {
|
||||
if ($this->job->status === 'configured') {
|
||||
|
||||
// set job as "running"...
|
||||
$this->job->status = 'running';
|
||||
@@ -120,6 +125,7 @@ class ImportRoutine
|
||||
*/
|
||||
private function createImportTag(): Tag
|
||||
{
|
||||
Log::debug('Now in createImportTag()');
|
||||
/** @var TagRepositoryInterface $repository */
|
||||
$repository = app(TagRepositoryInterface::class);
|
||||
$repository->setUser($this->job->user);
|
||||
@@ -138,14 +144,17 @@ class ImportRoutine
|
||||
$this->job->extended_status = $extended;
|
||||
$this->job->save();
|
||||
|
||||
$this->journals->each(
|
||||
function (TransactionJournal $journal) use ($tag) {
|
||||
$journal->tags()->save($tag);
|
||||
}
|
||||
);
|
||||
Log::debug(sprintf('Created tag #%d ("%s")', $tag->id, $tag->tag));
|
||||
Log::debug('Looping journals...');
|
||||
$journalIds = $this->journals->pluck('id')->toArray();
|
||||
$tagId = $tag->id;
|
||||
foreach ($journalIds as $journalId) {
|
||||
Log::debug(sprintf('Linking journal #%d to tag #%d...', $journalId, $tagId));
|
||||
DB::table('tag_transaction_journal')->insert(['transaction_journal_id' => $journalId, 'tag_id' => $tagId]);
|
||||
}
|
||||
Log::info(sprintf('Linked %d journals to tag #%d ("%s")', $this->journals->count(), $tag->id, $tag->tag));
|
||||
|
||||
return $tag;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -160,6 +169,7 @@ class ImportRoutine
|
||||
$storage->setDateFormat($this->job->configuration['date-format']);
|
||||
$storage->setObjects($objects);
|
||||
$storage->store();
|
||||
Log::info('Back in storeObjects()');
|
||||
|
||||
return $storage;
|
||||
}
|
||||
|
||||
@@ -103,7 +103,7 @@ class AbnAmroDescription implements SpecificInterface
|
||||
$this->row[8] = $matches[4]; // 'opposing-account-name'
|
||||
$this->row[7] = $matches[4]; // 'description'
|
||||
|
||||
if ($matches[1] == 'GEA') {
|
||||
if ($matches[1] === 'GEA') {
|
||||
$this->row[7] = 'GEA ' . $matches[4]; // 'description'
|
||||
}
|
||||
|
||||
|
||||
@@ -11,27 +11,12 @@ declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\Import\Storage;
|
||||
|
||||
use Amount;
|
||||
use Carbon\Carbon;
|
||||
use FireflyIII\Exceptions\FireflyException;
|
||||
use FireflyIII\Import\Object\ImportAccount;
|
||||
use FireflyIII\Import\Object\ImportJournal;
|
||||
use FireflyIII\Models\Account;
|
||||
use FireflyIII\Models\AccountType;
|
||||
use FireflyIII\Models\Bill;
|
||||
use FireflyIII\Models\Budget;
|
||||
use FireflyIII\Models\Category;
|
||||
use FireflyIII\Models\ImportJob;
|
||||
use FireflyIII\Models\Rule;
|
||||
use FireflyIII\Models\Transaction;
|
||||
use FireflyIII\Models\TransactionCurrency;
|
||||
use FireflyIII\Models\TransactionJournal;
|
||||
use FireflyIII\Models\TransactionType;
|
||||
use FireflyIII\Repositories\Currency\CurrencyRepositoryInterface;
|
||||
use FireflyIII\Rules\Processor;
|
||||
use Illuminate\Support\Collection;
|
||||
use Log;
|
||||
use Steam;
|
||||
|
||||
/**
|
||||
* Is capable of storing individual ImportJournal objects.
|
||||
@@ -41,15 +26,16 @@ use Steam;
|
||||
*/
|
||||
class ImportStorage
|
||||
{
|
||||
use ImportSupport;
|
||||
|
||||
/** @var Collection */
|
||||
public $errors;
|
||||
/** @var Collection */
|
||||
public $journals;
|
||||
/** @var CurrencyRepositoryInterface */
|
||||
private $currencyRepository;
|
||||
/** @var int */
|
||||
protected $defaultCurrencyId = 1;
|
||||
/** @var string */
|
||||
private $dateFormat = 'Ymd';
|
||||
/** @var TransactionCurrency */
|
||||
private $defaultCurrency;
|
||||
private $dateFormat = 'Ymd'; // yes, hard coded
|
||||
/** @var ImportJob */
|
||||
private $job;
|
||||
/** @var Collection */
|
||||
@@ -57,6 +43,9 @@ class ImportStorage
|
||||
/** @var Collection */
|
||||
private $rules;
|
||||
|
||||
/** @var array */
|
||||
private $transfers = [];
|
||||
|
||||
/**
|
||||
* ImportStorage constructor.
|
||||
*/
|
||||
@@ -80,11 +69,11 @@ class ImportStorage
|
||||
*/
|
||||
public function setJob(ImportJob $job)
|
||||
{
|
||||
$this->job = $job;
|
||||
$repository = app(CurrencyRepositoryInterface::class);
|
||||
$repository->setUser($job->user);
|
||||
$this->currencyRepository = $repository;
|
||||
$this->rules = $this->getUserRules();
|
||||
$this->job = $job;
|
||||
$currency = app('amount')->getDefaultCurrencyByUser($this->job->user);
|
||||
$this->defaultCurrencyId = $currency->id;
|
||||
$this->transfers = $this->getTransfers();
|
||||
$this->rules = $this->getRules();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -96,257 +85,82 @@ class ImportStorage
|
||||
}
|
||||
|
||||
/**
|
||||
* Do storage of import objects
|
||||
*/
|
||||
public function store()
|
||||
{
|
||||
$this->defaultCurrency = Amount::getDefaultCurrencyByUser($this->job->user);
|
||||
|
||||
// routine below consists of 3 steps.
|
||||
/**
|
||||
* @var int $index
|
||||
* @var ImportJournal $object
|
||||
*/
|
||||
foreach ($this->objects as $index => $object) {
|
||||
try {
|
||||
$this->storeImportJournal($index, $object);
|
||||
} catch (FireflyException $e) {
|
||||
$this->errors->push($e->getMessage());
|
||||
Log::error(sprintf('Cannot import row #%d because: %s', $index, $e->getMessage()));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @param TransactionJournal $journal
|
||||
* Do storage of import objects. Is the main function.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function applyRules(TransactionJournal $journal): bool
|
||||
public function store(): bool
|
||||
{
|
||||
if ($this->rules->count() > 0) {
|
||||
|
||||
/** @var Rule $rule */
|
||||
foreach ($this->rules as $rule) {
|
||||
Log::debug(sprintf('Going to apply rule #%d to journal %d.', $rule->id, $journal->id));
|
||||
$processor = Processor::make($rule);
|
||||
$processor->handleTransactionJournal($journal);
|
||||
|
||||
if ($rule->stop_processing) {
|
||||
return true;
|
||||
$this->objects->each(
|
||||
function (ImportJournal $importJournal, int $index) {
|
||||
try {
|
||||
$this->storeImportJournal($index, $importJournal);
|
||||
} catch (FireflyException $e) {
|
||||
$this->errors->push($e->getMessage());
|
||||
Log::error(sprintf('Cannot import row #%d because: %s', $index, $e->getMessage()));
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
Log::info('ImportStorage has finished.');
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $journalId
|
||||
* @param int $accountId
|
||||
* @param int $currencyId
|
||||
* @param string $amount
|
||||
* @param int $index
|
||||
* @param ImportJournal $importJournal
|
||||
*
|
||||
* @return bool
|
||||
* @throws FireflyException
|
||||
*/
|
||||
private function createTransaction(int $journalId, int $accountId, int $currencyId, string $amount): bool
|
||||
protected function storeImportJournal(int $index, ImportJournal $importJournal): bool
|
||||
{
|
||||
$transaction = new Transaction;
|
||||
$transaction->account_id = $accountId;
|
||||
$transaction->transaction_journal_id = $journalId;
|
||||
$transaction->transaction_currency_id = $currencyId;
|
||||
$transaction->amount = $amount;
|
||||
$transaction->save();
|
||||
if (is_null($transaction->id)) {
|
||||
$errorText = join(', ', $transaction->getErrors()->all());
|
||||
throw new FireflyException($errorText);
|
||||
}
|
||||
Log::debug(sprintf('Created transaction with ID #%d and account #%d', $transaction->id, $accountId));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ImportJournal $importJournal
|
||||
*
|
||||
* @return TransactionCurrency
|
||||
*/
|
||||
private function getCurrency(ImportJournal $importJournal, Account $account): TransactionCurrency
|
||||
{
|
||||
// start with currency pref of account, if any:
|
||||
$currency = $this->currencyRepository->find(intval($account->getMeta('currency_id')));
|
||||
if (!is_null($currency->id)) {
|
||||
return $currency;
|
||||
}
|
||||
|
||||
// use given currency
|
||||
$currency = $importJournal->getCurrency()->getTransactionCurrency();
|
||||
if (!is_null($currency->id)) {
|
||||
return $currency;
|
||||
}
|
||||
|
||||
// backup to default
|
||||
$currency = $this->defaultCurrency;
|
||||
|
||||
return $currency;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ImportAccount $account
|
||||
* @param $amount
|
||||
*
|
||||
* @return Account
|
||||
*/
|
||||
private function getOpposingAccount(ImportAccount $account, $amount): Account
|
||||
{
|
||||
if (bccomp($amount, '0') === -1) {
|
||||
Log::debug(sprintf('%s is negative, create opposing expense account.', $amount));
|
||||
$account->setExpectedType(AccountType::EXPENSE);
|
||||
|
||||
return $account->getAccount();
|
||||
}
|
||||
Log::debug(sprintf('%s is positive, create opposing revenue account.', $amount));
|
||||
// amount is positive, it's a deposit, opposing is an revenue:
|
||||
$account->setExpectedType(AccountType::REVENUE);
|
||||
|
||||
$databaseAccount = $account->getAccount();
|
||||
|
||||
return $databaseAccount;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $amount
|
||||
*
|
||||
* @return TransactionType
|
||||
*/
|
||||
private function getTransactionType(string $amount): TransactionType
|
||||
{
|
||||
$transactionType = new TransactionType();
|
||||
// amount is negative, it's a withdrawal, opposing is an expense:
|
||||
if (bccomp($amount, '0') === -1) {
|
||||
$transactionType = TransactionType::whereType(TransactionType::WITHDRAWAL)->first();
|
||||
}
|
||||
if (bccomp($amount, '0') === 1) {
|
||||
$transactionType = TransactionType::whereType(TransactionType::DEPOSIT)->first();
|
||||
}
|
||||
|
||||
return $transactionType;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Collection
|
||||
*/
|
||||
private function getUserRules(): Collection
|
||||
{
|
||||
$set = Rule::distinct()
|
||||
->where('rules.user_id', $this->job->user->id)
|
||||
->leftJoin('rule_groups', 'rule_groups.id', '=', 'rules.rule_group_id')
|
||||
->leftJoin('rule_triggers', 'rules.id', '=', 'rule_triggers.rule_id')
|
||||
->where('rule_groups.active', 1)
|
||||
->where('rule_triggers.trigger_type', 'user_action')
|
||||
->where('rule_triggers.trigger_value', 'store-journal')
|
||||
->where('rules.active', 1)
|
||||
->orderBy('rule_groups.order', 'ASC')
|
||||
->orderBy('rules.order', 'ASC')
|
||||
->get(['rules.*', 'rule_groups.order']);
|
||||
Log::debug(sprintf('Found %d user rules.', $set->count()));
|
||||
|
||||
return $set;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @param TransactionJournal $journal
|
||||
* @param Bill $bill
|
||||
*/
|
||||
private function storeBill(TransactionJournal $journal, Bill $bill)
|
||||
{
|
||||
if (!is_null($bill->id)) {
|
||||
Log::debug(sprintf('Linked bill #%d to journal #%d', $bill->id, $journal->id));
|
||||
$journal->bill()->associate($bill);
|
||||
$journal->save();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param TransactionJournal $journal
|
||||
* @param Budget $budget
|
||||
*/
|
||||
private function storeBudget(TransactionJournal $journal, Budget $budget)
|
||||
{
|
||||
if (!is_null($budget->id)) {
|
||||
Log::debug(sprintf('Linked budget #%d to journal #%d', $budget->id, $journal->id));
|
||||
$journal->budgets()->save($budget);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param TransactionJournal $journal
|
||||
* @param Category $category
|
||||
*/
|
||||
private function storeCategory(TransactionJournal $journal, Category $category)
|
||||
{
|
||||
|
||||
if (!is_null($category->id)) {
|
||||
Log::debug(sprintf('Linked category #%d to journal #%d', $category->id, $journal->id));
|
||||
$journal->categories()->save($category);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private function storeImportJournal(int $index, ImportJournal $importJournal): bool
|
||||
{
|
||||
Log::debug(sprintf('Going to store object #%d with description "%s"', $index, $importJournal->description));
|
||||
|
||||
$asset = $importJournal->asset->getAccount();
|
||||
$amount = $importJournal->getAmount();
|
||||
$currency = $this->getCurrency($importJournal, $asset);
|
||||
$date = $importJournal->getDate($this->dateFormat);
|
||||
$transactionType = $this->getTransactionType($amount);
|
||||
$opposing = $this->getOpposingAccount($importJournal->opposing, $amount);
|
||||
|
||||
// if opposing is an asset account, it's a transfer:
|
||||
if ($opposing->accountType->type === AccountType::ASSET) {
|
||||
Log::debug(sprintf('Opposing account #%d %s is an asset account, make transfer.', $opposing->id, $opposing->name));
|
||||
$transactionType = TransactionType::whereType(TransactionType::TRANSFER)->first();
|
||||
}
|
||||
|
||||
// verify that opposing account is of the correct type:
|
||||
if ($opposing->accountType->type === AccountType::EXPENSE && $transactionType->type !== TransactionType::WITHDRAWAL) {
|
||||
Log::error(sprintf('Row #%d is imported as a %s but opposing is an expense account. This cannot be!', $index, $transactionType->type));
|
||||
}
|
||||
Log::debug(sprintf('Going to store object #%d with description "%s"', $index, $importJournal->getDescription()));
|
||||
$assetAccount = $importJournal->asset->getAccount();
|
||||
$amount = $importJournal->getAmount();
|
||||
$currencyId = $this->getCurrencyId($importJournal);
|
||||
$foreignCurrencyId = $this->getForeignCurrencyId($importJournal, $currencyId);
|
||||
$date = $importJournal->getDate($this->dateFormat)->format('Y-m-d');
|
||||
$opposingAccount = $this->getOpposingAccount($importJournal->opposing, $assetAccount->id, $amount);
|
||||
$transactionType = $this->getTransactionType($amount, $opposingAccount);
|
||||
$description = $importJournal->getDescription();
|
||||
|
||||
/*** First step done! */
|
||||
$this->job->addStepsDone(1);
|
||||
|
||||
// create a journal:
|
||||
$journal = new TransactionJournal;
|
||||
$journal->user_id = $this->job->user_id;
|
||||
$journal->transaction_type_id = $transactionType->id;
|
||||
$journal->transaction_currency_id = $currency->id;
|
||||
$journal->description = $importJournal->description;
|
||||
$journal->date = $date->format('Y-m-d');
|
||||
$journal->order = 0;
|
||||
$journal->tag_count = 0;
|
||||
$journal->completed = false;
|
||||
|
||||
if (!$journal->save()) {
|
||||
$errorText = join(', ', $journal->getErrors()->all());
|
||||
/**
|
||||
* Check for double transfer.
|
||||
*/
|
||||
$parameters = [
|
||||
'type' => $transactionType,
|
||||
'description' => $description,
|
||||
'amount' => $amount,
|
||||
'date' => $date,
|
||||
'asset' => $assetAccount->name,
|
||||
'opposing' => $opposingAccount->name,
|
||||
];
|
||||
if ($this->isDoubleTransfer($parameters) || $this->hashAlreadyImported($importJournal->hash)) {
|
||||
$this->job->addStepsDone(3);
|
||||
throw new FireflyException($errorText);
|
||||
// throw error
|
||||
throw new FireflyException('Detected a possible duplicate, skip this one.');
|
||||
}
|
||||
unset($parameters);
|
||||
|
||||
// save meta data:
|
||||
$journal->setMeta('importHash', $importJournal->hash);
|
||||
Log::debug(sprintf('Created journal with ID #%d', $journal->id));
|
||||
// store journal and create transactions:
|
||||
$parameters = [
|
||||
'type' => $transactionType,
|
||||
'currency' => $currencyId,
|
||||
'foreign_currency' => $foreignCurrencyId,
|
||||
'asset' => $assetAccount,
|
||||
'opposing' => $opposingAccount,
|
||||
'description' => $description,
|
||||
'date' => $date,
|
||||
'hash' => $importJournal->hash,
|
||||
'amount' => $amount,
|
||||
|
||||
// create transactions:
|
||||
$this->createTransaction($journal->id, $asset->id, $currency->id, $amount);
|
||||
$this->createTransaction($journal->id, $opposing->id, $currency->id, Steam::opposite($amount));
|
||||
];
|
||||
$journal = $this->storeJournal($parameters);
|
||||
unset($parameters);
|
||||
|
||||
/*** Another step done! */
|
||||
$this->job->addStepsDone(1);
|
||||
@@ -356,50 +170,57 @@ class ImportStorage
|
||||
$this->storeBudget($journal, $importJournal->budget->getBudget());
|
||||
$this->storeBill($journal, $importJournal->bill->getBill());
|
||||
$this->storeMeta($journal, $importJournal->metaDates);
|
||||
|
||||
// sepa thing as note:
|
||||
if (strlen($importJournal->notes) > 0) {
|
||||
$journal->setMeta('notes', $importJournal->notes);
|
||||
}
|
||||
$journal->setMeta('notes', $importJournal->notes);
|
||||
$this->storeTags($importJournal->tags, $journal);
|
||||
|
||||
// set journal completed:
|
||||
$journal->completed = true;
|
||||
$journal->save();
|
||||
|
||||
/*** Another step done! */
|
||||
$this->job->addStepsDone(1);
|
||||
|
||||
// run rules:
|
||||
$this->applyRules($journal);
|
||||
/*** Another step done! */
|
||||
$this->job->addStepsDone(1);
|
||||
|
||||
$this->journals->push($journal);
|
||||
|
||||
Log::info(
|
||||
sprintf(
|
||||
'Imported new journal #%d with description "%s" and amount %s %s.', $journal->id, $journal->description, $journal->transactionCurrency->code,
|
||||
$amount
|
||||
)
|
||||
);
|
||||
Log::info(sprintf('Imported new journal #%d: "%s", amount %s %s.', $journal->id, $journal->description, $journal->transactionCurrency->code, $amount));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param TransactionJournal $journal
|
||||
* @param array $dates
|
||||
* @param array $parameters
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private function storeMeta(TransactionJournal $journal, array $dates)
|
||||
private function isDoubleTransfer(array $parameters): bool
|
||||
{
|
||||
// all other date fields as meta thing:
|
||||
foreach ($dates as $name => $value) {
|
||||
try {
|
||||
$date = new Carbon($value);
|
||||
$journal->setMeta($name, $date);
|
||||
} catch (\Exception $e) {
|
||||
// don't care, ignore:
|
||||
Log::warning(sprintf('Could not parse "%s" into a valid Date object for field %s', $value, $name));
|
||||
if ($parameters['type'] !== TransactionType::TRANSFER) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$amount = app('steam')->positive($parameters['amount']);
|
||||
$names = [$parameters['asset'], $parameters['opposing']];
|
||||
sort($names);
|
||||
|
||||
foreach ($this->transfers as $transfer) {
|
||||
if ($parameters['description'] !== $transfer['description']) {
|
||||
return false;
|
||||
}
|
||||
if ($names !== $transfer['names']) {
|
||||
return false;
|
||||
}
|
||||
if (bccomp($amount, $transfer['amount']) !== 0) {
|
||||
return false;
|
||||
}
|
||||
if ($parameters['date'] !== $transfer['date']) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
444
app/Import/Storage/ImportSupport.php
Normal file
444
app/Import/Storage/ImportSupport.php
Normal file
@@ -0,0 +1,444 @@
|
||||
<?php
|
||||
/**
|
||||
* ImportSupport.php
|
||||
* Copyright (c) 2017 thegrumpydictator@gmail.com
|
||||
* This software may be modified and distributed under the terms of the
|
||||
* Creative Commons Attribution-ShareAlike 4.0 International License.
|
||||
*
|
||||
* See the LICENSE file for details.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\Import\Storage;
|
||||
|
||||
|
||||
use Carbon\Carbon;
|
||||
use Exception;
|
||||
use FireflyIII\Exceptions\FireflyException;
|
||||
use FireflyIII\Import\Object\ImportAccount;
|
||||
use FireflyIII\Import\Object\ImportJournal;
|
||||
use FireflyIII\Models\Account;
|
||||
use FireflyIII\Models\AccountType;
|
||||
use FireflyIII\Models\Bill;
|
||||
use FireflyIII\Models\Budget;
|
||||
use FireflyIII\Models\Category;
|
||||
use FireflyIII\Models\Rule;
|
||||
use FireflyIII\Models\Transaction;
|
||||
use FireflyIII\Models\TransactionJournal;
|
||||
use FireflyIII\Models\TransactionJournalMeta;
|
||||
use FireflyIII\Models\TransactionType;
|
||||
use FireflyIII\Repositories\Tag\TagRepositoryInterface;
|
||||
use FireflyIII\Rules\Processor;
|
||||
use Illuminate\Database\Query\JoinClause;
|
||||
use Illuminate\Support\Collection;
|
||||
use Log;
|
||||
|
||||
trait ImportSupport
|
||||
{
|
||||
/** @var int */
|
||||
protected $defaultCurrencyId = 1;
|
||||
|
||||
/**
|
||||
* @param TransactionJournal $journal
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function applyRules(TransactionJournal $journal): bool
|
||||
{
|
||||
if ($this->rules->count() > 0) {
|
||||
$this->rules->each(
|
||||
function (Rule $rule) use ($journal) {
|
||||
Log::debug(sprintf('Going to apply rule #%d to journal %d.', $rule->id, $journal->id));
|
||||
$processor = Processor::make($rule);
|
||||
$processor->handleTransactionJournal($journal);
|
||||
if ($rule->stop_processing) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $parameters
|
||||
*
|
||||
* @return bool
|
||||
* @throws FireflyException
|
||||
*/
|
||||
private function createTransaction(array $parameters): bool
|
||||
{
|
||||
$transaction = new Transaction;
|
||||
$transaction->account_id = $parameters['account'];
|
||||
$transaction->transaction_journal_id = $parameters['id'];
|
||||
$transaction->transaction_currency_id = $parameters['currency'];
|
||||
$transaction->amount = $parameters['amount'];
|
||||
$transaction->foreign_currency_id = $parameters['foreign_currency'];
|
||||
$transaction->foreign_amount = $parameters['foreign_amount'];
|
||||
$transaction->save();
|
||||
if (is_null($transaction->id)) {
|
||||
$errorText = join(', ', $transaction->getErrors()->all());
|
||||
throw new FireflyException($errorText);
|
||||
}
|
||||
Log::debug(sprintf('Created transaction with ID #%d, account #%d, amount %s', $transaction->id, $parameters['account'], $parameters['amount']));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method finds out what the import journal's currency should be. The account itself
|
||||
* is favoured (and usually it stops there). If no preference is found, the journal has a say
|
||||
* and thirdly the default currency is used.
|
||||
*
|
||||
* @param ImportJournal $importJournal
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
private function getCurrencyId(ImportJournal $importJournal): int
|
||||
{
|
||||
// start with currency pref of account, if any:
|
||||
$account = $importJournal->asset->getAccount();
|
||||
$currencyId = intval($account->getMeta('currency_id'));
|
||||
if ($currencyId > 0) {
|
||||
return $currencyId;
|
||||
}
|
||||
|
||||
// use given currency
|
||||
$currency = $importJournal->currency->getTransactionCurrency();
|
||||
if (!is_null($currency->id)) {
|
||||
return $currency->id;
|
||||
}
|
||||
|
||||
// backup to default
|
||||
$currency = $this->defaultCurrencyId;
|
||||
|
||||
return $currency;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* The foreign currency is only returned when the journal has a different value from the
|
||||
* currency id (see other method).
|
||||
*
|
||||
* @param ImportJournal $importJournal
|
||||
* @param int $currencyId
|
||||
*
|
||||
* @see ImportSupport::getCurrencyId
|
||||
*
|
||||
* @return int|null
|
||||
*/
|
||||
private function getForeignCurrencyId(ImportJournal $importJournal, int $currencyId): ?int
|
||||
{
|
||||
// use given currency by import journal.
|
||||
$currency = $importJournal->currency->getTransactionCurrency();
|
||||
if (!is_null($currency->id) && $currency->id !== $currencyId) {
|
||||
return $currency->id;
|
||||
}
|
||||
|
||||
// return null, because no different:
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* The search for the opposing account is complex. Firstly, we forbid the ImportAccount to resolve into the asset
|
||||
* account to prevent a situation where the transaction flows from A to A. Given the amount, we "expect" the opposing
|
||||
* account to be an expense or a revenue account. However, the mapping given by the user may return something else
|
||||
* entirely (usually an asset account). So whatever the expectation, the result may be anything.
|
||||
*
|
||||
* When the result does not match the expected type (a negative amount cannot be linked to a revenue account) the next step
|
||||
* will return an error.
|
||||
*
|
||||
* @param ImportAccount $account
|
||||
* @param int $forbiddenAccount
|
||||
* @param string $amount
|
||||
*
|
||||
* @see ImportSupport::getTransactionType
|
||||
*
|
||||
* @return Account
|
||||
*/
|
||||
private function getOpposingAccount(ImportAccount $account, int $forbiddenAccount, string $amount): Account
|
||||
{
|
||||
$account->setForbiddenAccountId($forbiddenAccount);
|
||||
if (bccomp($amount, '0') === -1) {
|
||||
Log::debug(sprintf('%s is negative, create opposing expense account.', $amount));
|
||||
$account->setExpectedType(AccountType::EXPENSE);
|
||||
|
||||
return $account->getAccount();
|
||||
}
|
||||
Log::debug(sprintf('%s is positive, create opposing revenue account.', $amount));
|
||||
// amount is positive, it's a deposit, opposing is an revenue:
|
||||
$account->setExpectedType(AccountType::REVENUE);
|
||||
|
||||
$databaseAccount = $account->getAccount();
|
||||
|
||||
return $databaseAccount;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Collection
|
||||
*/
|
||||
private function getRules(): Collection
|
||||
{
|
||||
$set = Rule::distinct()
|
||||
->where('rules.user_id', $this->job->user->id)
|
||||
->leftJoin('rule_groups', 'rule_groups.id', '=', 'rules.rule_group_id')
|
||||
->leftJoin('rule_triggers', 'rules.id', '=', 'rule_triggers.rule_id')
|
||||
->where('rule_groups.active', 1)
|
||||
->where('rule_triggers.trigger_type', 'user_action')
|
||||
->where('rule_triggers.trigger_value', 'store-journal')
|
||||
->where('rules.active', 1)
|
||||
->orderBy('rule_groups.order', 'ASC')
|
||||
->orderBy('rules.order', 'ASC')
|
||||
->get(['rules.*', 'rule_groups.order']);
|
||||
Log::debug(sprintf('Found %d user rules.', $set->count()));
|
||||
|
||||
return $set;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Given the amount and the opposing account its easy to define which kind of transaction type should be associated with the new
|
||||
* import. This may however fail when there is an unexpected mismatch between the transaction type and the opposing account.
|
||||
*
|
||||
* @param string $amount
|
||||
* @param Account $account
|
||||
*
|
||||
* @return string
|
||||
* @throws FireflyException
|
||||
* @see ImportSupport::getOpposingAccount()
|
||||
*/
|
||||
private function getTransactionType(string $amount, Account $account): string
|
||||
{
|
||||
$transactionType = '';
|
||||
// amount is negative, it's a withdrawal, opposing is an expense:
|
||||
if (bccomp($amount, '0') === -1) {
|
||||
$transactionType = TransactionType::WITHDRAWAL;
|
||||
}
|
||||
|
||||
if (bccomp($amount, '0') === 1) {
|
||||
$transactionType = TransactionType::DEPOSIT;
|
||||
}
|
||||
|
||||
// if opposing is an asset account, it's a transfer:
|
||||
if ($account->accountType->type === AccountType::ASSET) {
|
||||
Log::debug(sprintf('Opposing account #%d %s is an asset account, make transfer.', $account->id, $account->name));
|
||||
$transactionType = TransactionType::TRANSFER;
|
||||
}
|
||||
|
||||
// verify that opposing account is of the correct type:
|
||||
if ($account->accountType->type === AccountType::EXPENSE && $transactionType !== TransactionType::WITHDRAWAL) {
|
||||
$message = 'This row is imported as a withdrawal but opposing is an expense account. This cannot be!';
|
||||
Log::error($message);
|
||||
throw new FireflyException($message);
|
||||
}
|
||||
|
||||
return $transactionType;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method returns a collection of the current transfers in the system and some meta data for
|
||||
* this set. This can later be used to see if the journal that firefly is trying to import
|
||||
* is not already present.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function getTransfers(): array
|
||||
{
|
||||
$set = TransactionJournal::leftJoin('transaction_types', 'transaction_types.id', '=', 'transaction_journals.transaction_type_id')
|
||||
->leftJoin(
|
||||
'transactions AS source', function (JoinClause $join) {
|
||||
$join->on('transaction_journals.id', '=', 'source.transaction_journal_id')->where('source.amount', '<', 0);
|
||||
}
|
||||
)
|
||||
->leftJoin(
|
||||
'transactions AS destination', function (JoinClause $join) {
|
||||
$join->on('transaction_journals.id', '=', 'destination.transaction_journal_id')->where(
|
||||
'destination.amount', '>', 0
|
||||
);
|
||||
}
|
||||
)
|
||||
->leftJoin('accounts as source_accounts', 'source.account_id', '=', 'source_accounts.id')
|
||||
->leftJoin('accounts as destination_accounts', 'destination.account_id', '=', 'destination_accounts.id')
|
||||
->where('transaction_journals.user_id', $this->job->user_id)
|
||||
->where('transaction_types.type', TransactionType::TRANSFER)
|
||||
->get(
|
||||
['transaction_journals.id', 'transaction_journals.encrypted', 'transaction_journals.description',
|
||||
'source_accounts.name as source_name', 'destination_accounts.name as destination_name', 'destination.amount'
|
||||
, 'transaction_journals.date']
|
||||
);
|
||||
$array = [];
|
||||
/** @var TransactionJournal $entry */
|
||||
foreach ($set as $entry) {
|
||||
$original = [app('steam')->tryDecrypt($entry->source_name), app('steam')->tryDecrypt($entry->destination_name)];
|
||||
sort($original);
|
||||
$array[] = [
|
||||
'names' => $original,
|
||||
'amount' => $entry->amount,
|
||||
'date' => $entry->date->format('Y-m-d'),
|
||||
'description' => $entry->description,
|
||||
];
|
||||
}
|
||||
|
||||
return $array;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the import journal has not been imported before.
|
||||
*
|
||||
* @param string $hash
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private function hashAlreadyImported(string $hash): bool
|
||||
{
|
||||
$json = json_encode($hash);
|
||||
$entry = TransactionJournalMeta::leftJoin('transaction_journals', 'transaction_journals.id', '=', 'journal_meta.transaction_journal_id')
|
||||
->where('data', $json)
|
||||
->where('name', 'importHash')
|
||||
->first();
|
||||
if (!is_null($entry)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @param TransactionJournal $journal
|
||||
* @param Bill $bill
|
||||
*/
|
||||
private function storeBill(TransactionJournal $journal, Bill $bill)
|
||||
{
|
||||
if (!is_null($bill->id)) {
|
||||
Log::debug(sprintf('Linked bill #%d to journal #%d', $bill->id, $journal->id));
|
||||
$journal->bill()->associate($bill);
|
||||
$journal->save();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param TransactionJournal $journal
|
||||
* @param Budget $budget
|
||||
*/
|
||||
private function storeBudget(TransactionJournal $journal, Budget $budget)
|
||||
{
|
||||
if (!is_null($budget->id)) {
|
||||
Log::debug(sprintf('Linked budget #%d to journal #%d', $budget->id, $journal->id));
|
||||
$journal->budgets()->save($budget);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param TransactionJournal $journal
|
||||
* @param Category $category
|
||||
*/
|
||||
private function storeCategory(TransactionJournal $journal, Category $category)
|
||||
{
|
||||
|
||||
if (!is_null($category->id)) {
|
||||
Log::debug(sprintf('Linked category #%d to journal #%d', $category->id, $journal->id));
|
||||
$journal->categories()->save($category);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private function storeJournal(array $parameters): TransactionJournal
|
||||
{
|
||||
// find transaction type:
|
||||
$transactionType = TransactionType::whereType($parameters['type'])->first();
|
||||
|
||||
// create a journal:
|
||||
$journal = new TransactionJournal;
|
||||
$journal->user_id = $this->job->user_id;
|
||||
$journal->transaction_type_id = $transactionType->id;
|
||||
$journal->transaction_currency_id = $parameters['currency'];
|
||||
$journal->description = $parameters['description'];
|
||||
$journal->date = $parameters['date'];
|
||||
$journal->order = 0;
|
||||
$journal->tag_count = 0;
|
||||
$journal->completed = false;
|
||||
|
||||
if (!$journal->save()) {
|
||||
$errorText = join(', ', $journal->getErrors()->all());
|
||||
// add three steps:
|
||||
$this->job->addStepsDone(3);
|
||||
// throw error
|
||||
throw new FireflyException($errorText);
|
||||
}
|
||||
// save meta data:
|
||||
$journal->setMeta('importHash', $parameters['hash']);
|
||||
Log::debug(sprintf('Created journal with ID #%d', $journal->id));
|
||||
|
||||
// create transactions:
|
||||
$one = [
|
||||
'id' => $journal->id,
|
||||
'account' => $parameters['asset']->id,
|
||||
'currency' => $parameters['currency'],
|
||||
'amount' => $parameters['amount'],
|
||||
'foreign_currency' => $parameters['foreign_currency'],
|
||||
'foreign_amount' => is_null($parameters['foreign_currency']) ? null : $parameters['amount'],
|
||||
];
|
||||
$opposite = app('steam')->opposite($parameters['amount']);
|
||||
$two = [
|
||||
'id' => $journal->id,
|
||||
'account' => $parameters['opposing']->id,
|
||||
'currency' => $parameters['currency'],
|
||||
'amount' => $opposite,
|
||||
'foreign_currency' => $parameters['foreign_currency'],
|
||||
'foreign_amount' => is_null($parameters['foreign_currency']) ? null : $opposite,
|
||||
];
|
||||
$this->createTransaction($one);
|
||||
$this->createTransaction($two);
|
||||
|
||||
return $journal;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param TransactionJournal $journal
|
||||
* @param array $dates
|
||||
*/
|
||||
private function storeMeta(TransactionJournal $journal, array $dates)
|
||||
{
|
||||
// all other date fields as meta thing:
|
||||
foreach ($dates as $name => $value) {
|
||||
try {
|
||||
$date = new Carbon($value);
|
||||
$journal->setMeta($name, $date);
|
||||
} catch (Exception $e) {
|
||||
// don't care, ignore:
|
||||
Log::warning(sprintf('Could not parse "%s" into a valid Date object for field %s', $value, $name));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $tags
|
||||
* @param TransactionJournal $journal
|
||||
*/
|
||||
private function storeTags(array $tags, TransactionJournal $journal): void
|
||||
{
|
||||
$repository = app(TagRepositoryInterface::class);
|
||||
$repository->setUser($journal->user);
|
||||
|
||||
foreach ($tags as $tag) {
|
||||
$dbTag = $repository->findByTag($tag);
|
||||
if (is_null($dbTag->id)) {
|
||||
$dbTag = $repository->store(
|
||||
['tag' => $tag, 'date' => null, 'description' => null, 'latitude' => null, 'longitude' => null,
|
||||
'zoomLevel' => null, 'tagMode' => 'nothing']
|
||||
);
|
||||
}
|
||||
$journal->tags()->save($dbTag);
|
||||
Log::debug(sprintf('Linked tag %d ("%s") to journal #%d', $dbTag->id, $dbTag->tag, $journal->id));
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
}
|
||||
161
app/Jobs/ExecuteRuleOnExistingTransactions.php
Normal file
161
app/Jobs/ExecuteRuleOnExistingTransactions.php
Normal file
@@ -0,0 +1,161 @@
|
||||
<?php
|
||||
/**
|
||||
* ExecuteRuleOnExistingTransactions.php
|
||||
* Copyright (c) 2017 thegrumpydictator@gmail.com
|
||||
* This software may be modified and distributed under the terms of the Creative Commons Attribution-ShareAlike 4.0 International License.
|
||||
*
|
||||
* See the LICENSE file for details.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\Jobs;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use FireflyIII\Helpers\Collector\JournalCollectorInterface;
|
||||
use FireflyIII\Models\Rule;
|
||||
use FireflyIII\Rules\Processor;
|
||||
use FireflyIII\User;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Queue\InteractsWithQueue;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
use Illuminate\Support\Collection;
|
||||
|
||||
/**
|
||||
* Class ExecuteRuleOnExistingTransactions
|
||||
*
|
||||
* @package FireflyIII\Jobs
|
||||
*/
|
||||
class ExecuteRuleOnExistingTransactions extends Job implements ShouldQueue
|
||||
{
|
||||
use InteractsWithQueue, SerializesModels;
|
||||
|
||||
/** @var Collection */
|
||||
private $accounts;
|
||||
/** @var Carbon */
|
||||
private $endDate;
|
||||
/** @var Rule */
|
||||
private $rule;
|
||||
/** @var Carbon */
|
||||
private $startDate;
|
||||
/** @var User */
|
||||
private $user;
|
||||
|
||||
/**
|
||||
* Create a new job instance.
|
||||
*
|
||||
* @param Rule $rule
|
||||
*/
|
||||
public function __construct(Rule $rule)
|
||||
{
|
||||
$this->rule = $rule;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Collection
|
||||
*/
|
||||
public function getAccounts(): Collection
|
||||
{
|
||||
return $this->accounts;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param Collection $accounts
|
||||
*/
|
||||
public function setAccounts(Collection $accounts)
|
||||
{
|
||||
$this->accounts = $accounts;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \Carbon\Carbon
|
||||
*/
|
||||
public function getEndDate(): Carbon
|
||||
{
|
||||
return $this->endDate;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param Carbon $date
|
||||
*/
|
||||
public function setEndDate(Carbon $date)
|
||||
{
|
||||
$this->endDate = $date;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \Carbon\Carbon
|
||||
*/
|
||||
public function getStartDate(): Carbon
|
||||
{
|
||||
return $this->startDate;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param Carbon $date
|
||||
*/
|
||||
public function setStartDate(Carbon $date)
|
||||
{
|
||||
$this->startDate = $date;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return User
|
||||
*/
|
||||
public function getUser(): User
|
||||
{
|
||||
return $this->user;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param User $user
|
||||
*/
|
||||
public function setUser(User $user)
|
||||
{
|
||||
$this->user = $user;
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the job.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
// Lookup all journals that match the parameters specified
|
||||
$transactions = $this->collectJournals();
|
||||
$processor = Processor::make($this->rule);
|
||||
|
||||
// Execute the rules for each transaction
|
||||
foreach ($transactions as $transaction) {
|
||||
|
||||
$processor->handleTransaction($transaction);
|
||||
|
||||
// Stop processing this group if the rule specifies 'stop_processing'
|
||||
if ($processor->getRule()->stop_processing) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Collect all journals that should be processed
|
||||
*
|
||||
* @return Collection
|
||||
*/
|
||||
protected function collectJournals()
|
||||
{
|
||||
/** @var JournalCollectorInterface $collector */
|
||||
$collector = app(JournalCollectorInterface::class);
|
||||
$collector->setUser($this->user);
|
||||
$collector->setAccounts($this->accounts)->setRange($this->startDate, $this->endDate);
|
||||
|
||||
return $collector->getJournals();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -80,7 +80,7 @@ class MailError extends Job implements ShouldQueue
|
||||
Mail::send(
|
||||
['emails.error-html', 'emails.error-text'], $args,
|
||||
function (Message $message) use ($email) {
|
||||
if ($email != 'mail@example.com') {
|
||||
if ($email !== 'mail@example.com') {
|
||||
$message->to($email, $email)->subject('Caught an error in Firely III');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,16 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* RegisteredUser.php
|
||||
* Copyright (c) 2017 thegrumpydictator@gmail.com
|
||||
* This software may be modified and distributed under the terms of the
|
||||
* Creative Commons Attribution-ShareAlike 4.0 International License.
|
||||
*
|
||||
* See the LICENSE file for details.
|
||||
*/
|
||||
|
||||
namespace FireflyIII\Mail;
|
||||
|
||||
|
||||
@@ -1,4 +1,16 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* RequestedNewPassword.php
|
||||
* Copyright (c) 2017 thegrumpydictator@gmail.com
|
||||
* This software may be modified and distributed under the terms of the
|
||||
* Creative Commons Attribution-ShareAlike 4.0 International License.
|
||||
*
|
||||
* See the LICENSE file for details.
|
||||
*/
|
||||
|
||||
namespace FireflyIII\Mail;
|
||||
|
||||
|
||||
@@ -95,7 +95,7 @@ class Account extends Model
|
||||
|
||||
/** @var Account $account */
|
||||
foreach ($set as $account) {
|
||||
if ($account->name == $fields['name']) {
|
||||
if ($account->name === $fields['name']) {
|
||||
return $account;
|
||||
}
|
||||
}
|
||||
@@ -116,7 +116,7 @@ class Account extends Model
|
||||
{
|
||||
|
||||
if (auth()->check()) {
|
||||
if ($value->user_id == auth()->user()->id) {
|
||||
if (intval($value->user_id) === auth()->user()->id) {
|
||||
return $value;
|
||||
}
|
||||
}
|
||||
@@ -187,7 +187,7 @@ class Account extends Model
|
||||
public function getMeta(string $fieldName): string
|
||||
{
|
||||
foreach ($this->accountMeta as $meta) {
|
||||
if ($meta->name == $fieldName) {
|
||||
if ($meta->name === $fieldName) {
|
||||
return strval($meta->data);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -55,7 +55,7 @@ class Attachment extends Model
|
||||
{
|
||||
if (auth()->check()) {
|
||||
|
||||
if ($value->user_id == auth()->user()->id) {
|
||||
if (intval($value->user_id) === auth()->user()->id) {
|
||||
return $value;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -62,7 +62,7 @@ class Bill extends Model
|
||||
public static function routeBinder(Bill $value)
|
||||
{
|
||||
if (auth()->check()) {
|
||||
if ($value->user_id == auth()->user()->id) {
|
||||
if (intval($value->user_id) === auth()->user()->id) {
|
||||
return $value;
|
||||
}
|
||||
}
|
||||
@@ -77,7 +77,7 @@ class Bill extends Model
|
||||
public function getMatchAttribute($value)
|
||||
{
|
||||
|
||||
if (intval($this->match_encrypted) == 1) {
|
||||
if (intval($this->match_encrypted) === 1) {
|
||||
return Crypt::decrypt($value);
|
||||
}
|
||||
|
||||
@@ -92,7 +92,7 @@ class Bill extends Model
|
||||
public function getNameAttribute($value)
|
||||
{
|
||||
|
||||
if (intval($this->name_encrypted) == 1) {
|
||||
if (intval($this->name_encrypted) === 1) {
|
||||
return Crypt::decrypt($value);
|
||||
}
|
||||
|
||||
|
||||
@@ -66,7 +66,7 @@ class Budget extends Model
|
||||
$set = $query->get(['budgets.*']);
|
||||
/** @var Budget $budget */
|
||||
foreach ($set as $budget) {
|
||||
if ($budget->name == $fields['name']) {
|
||||
if ($budget->name === $fields['name']) {
|
||||
return $budget;
|
||||
}
|
||||
}
|
||||
@@ -85,7 +85,7 @@ class Budget extends Model
|
||||
public static function routeBinder(Budget $value)
|
||||
{
|
||||
if (auth()->check()) {
|
||||
if ($value->user_id == auth()->user()->id) {
|
||||
if (intval($value->user_id) === auth()->user()->id) {
|
||||
return $value;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -67,7 +67,7 @@ class Category extends Model
|
||||
$set = $query->get(['categories.*']);
|
||||
/** @var Category $category */
|
||||
foreach ($set as $category) {
|
||||
if ($category->name == $fields['name']) {
|
||||
if ($category->name === $fields['name']) {
|
||||
return $category;
|
||||
}
|
||||
}
|
||||
@@ -86,7 +86,7 @@ class Category extends Model
|
||||
public static function routeBinder(Category $value)
|
||||
{
|
||||
if (auth()->check()) {
|
||||
if ($value->user_id == auth()->user()->id) {
|
||||
if (intval($value->user_id) === auth()->user()->id) {
|
||||
return $value;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -124,7 +124,7 @@ class ImportJob extends Model
|
||||
if (is_null($value)) {
|
||||
return [];
|
||||
}
|
||||
if (strlen($value) == 0) {
|
||||
if (strlen($value) === 0) {
|
||||
return [];
|
||||
}
|
||||
|
||||
@@ -138,7 +138,7 @@ class ImportJob extends Model
|
||||
*/
|
||||
public function getExtendedStatusAttribute($value)
|
||||
{
|
||||
if (strlen($value) == 0) {
|
||||
if (strlen($value) === 0) {
|
||||
return [];
|
||||
}
|
||||
|
||||
|
||||
@@ -58,7 +58,7 @@ class PiggyBank extends Model
|
||||
public static function routeBinder(PiggyBank $value)
|
||||
{
|
||||
if (auth()->check()) {
|
||||
if ($value->account->user_id == auth()->user()->id) {
|
||||
if (intval($value->account->user_id) === auth()->user()->id) {
|
||||
return $value;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -51,7 +51,7 @@ class Rule extends Model
|
||||
public static function routeBinder(Rule $value)
|
||||
{
|
||||
if (auth()->check()) {
|
||||
if ($value->user_id == auth()->user()->id) {
|
||||
if (intval($value->user_id) === auth()->user()->id) {
|
||||
return $value;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -52,7 +52,7 @@ class RuleGroup extends Model
|
||||
public static function routeBinder(RuleGroup $value)
|
||||
{
|
||||
if (auth()->check()) {
|
||||
if ($value->user_id == auth()->user()->id) {
|
||||
if (intval($value->user_id) === auth()->user()->id) {
|
||||
return $value;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -66,7 +66,7 @@ class Tag extends Model
|
||||
$set = $query->get(['tags.*']);
|
||||
/** @var Tag $tag */
|
||||
foreach ($set as $tag) {
|
||||
if ($tag->tag == $fields['tag']) {
|
||||
if ($tag->tag === $fields['tag']) {
|
||||
return $tag;
|
||||
}
|
||||
}
|
||||
@@ -87,7 +87,7 @@ class Tag extends Model
|
||||
public static function routeBinder(Tag $value)
|
||||
{
|
||||
if (auth()->check()) {
|
||||
if ($value->user_id == auth()->user()->id) {
|
||||
if (intval($value->user_id) === auth()->user()->id) {
|
||||
return $value;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -207,7 +207,7 @@ class TransactionJournal extends Model
|
||||
public function isDeposit(): bool
|
||||
{
|
||||
if (!is_null($this->transaction_type_type)) {
|
||||
return $this->transaction_type_type == TransactionType::DEPOSIT;
|
||||
return $this->transaction_type_type === TransactionType::DEPOSIT;
|
||||
}
|
||||
|
||||
return $this->transactionType->isDeposit();
|
||||
@@ -220,7 +220,7 @@ class TransactionJournal extends Model
|
||||
public function isOpeningBalance(): bool
|
||||
{
|
||||
if (!is_null($this->transaction_type_type)) {
|
||||
return $this->transaction_type_type == TransactionType::OPENING_BALANCE;
|
||||
return $this->transaction_type_type === TransactionType::OPENING_BALANCE;
|
||||
}
|
||||
|
||||
return $this->transactionType->isOpeningBalance();
|
||||
@@ -233,7 +233,7 @@ class TransactionJournal extends Model
|
||||
public function isTransfer(): bool
|
||||
{
|
||||
if (!is_null($this->transaction_type_type)) {
|
||||
return $this->transaction_type_type == TransactionType::TRANSFER;
|
||||
return $this->transaction_type_type === TransactionType::TRANSFER;
|
||||
}
|
||||
|
||||
return $this->transactionType->isTransfer();
|
||||
@@ -246,7 +246,7 @@ class TransactionJournal extends Model
|
||||
public function isWithdrawal(): bool
|
||||
{
|
||||
if (!is_null($this->transaction_type_type)) {
|
||||
return $this->transaction_type_type == TransactionType::WITHDRAWAL;
|
||||
return $this->transaction_type_type === TransactionType::WITHDRAWAL;
|
||||
}
|
||||
|
||||
return $this->transactionType->isWithdrawal();
|
||||
|
||||
@@ -35,6 +35,8 @@ use FireflyIII\Helpers\Report\ReportHelper;
|
||||
use FireflyIII\Helpers\Report\ReportHelperInterface;
|
||||
use FireflyIII\Repositories\User\UserRepository;
|
||||
use FireflyIII\Repositories\User\UserRepositoryInterface;
|
||||
use FireflyIII\Services\Password\PwndVerifier;
|
||||
use FireflyIII\Services\Password\Verifier;
|
||||
use FireflyIII\Support\Amount;
|
||||
use FireflyIII\Support\ExpandedForm;
|
||||
use FireflyIII\Support\FireflyConfig;
|
||||
@@ -147,6 +149,9 @@ class FireflyServiceProvider extends ServiceProvider
|
||||
$this->app->bind(FiscalHelperInterface::class, FiscalHelper::class);
|
||||
$this->app->bind(BalanceReportHelperInterface::class, BalanceReportHelper::class);
|
||||
$this->app->bind(BudgetReportHelperInterface::class, BudgetReportHelper::class);
|
||||
|
||||
// password verifier thing
|
||||
$this->app->bind(Verifier::class, PwndVerifier::class);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -25,6 +25,7 @@ use FireflyIII\Models\TransactionJournal;
|
||||
use FireflyIII\Models\TransactionType;
|
||||
use FireflyIII\User;
|
||||
use Log;
|
||||
use Validator;
|
||||
|
||||
|
||||
/**
|
||||
@@ -178,7 +179,7 @@ class AccountRepository implements AccountRepositoryInterface
|
||||
{
|
||||
// update the account:
|
||||
$account->name = $data['name'];
|
||||
$account->active = $data['active'] == '1' ? true : false;
|
||||
$account->active = $data['active'];
|
||||
$account->virtual_balance = $data['virtualBalance'];
|
||||
$account->iban = $data['iban'];
|
||||
$account->save();
|
||||
@@ -236,7 +237,7 @@ class AccountRepository implements AccountRepositoryInterface
|
||||
$data['accountType'] = $data['accountType'] ?? 'invalid';
|
||||
$type = config('firefly.accountTypeByIdentifier.' . $data['accountType']);
|
||||
$accountType = AccountType::whereType($type)->first();
|
||||
|
||||
$data['iban'] = $this->filterIban($data['iban']);
|
||||
// verify account type
|
||||
if (is_null($accountType)) {
|
||||
throw new FireflyException(sprintf('Account type "%s" is invalid. Cannot create account.', $data['accountType']));
|
||||
@@ -251,16 +252,17 @@ class AccountRepository implements AccountRepositoryInterface
|
||||
}
|
||||
|
||||
// create it:
|
||||
$newAccount = new Account(
|
||||
[
|
||||
'user_id' => $this->user->id,
|
||||
'account_type_id' => $accountType->id,
|
||||
'name' => $data['name'],
|
||||
'virtual_balance' => $data['virtualBalance'],
|
||||
'active' => $data['active'] === true ? true : false,
|
||||
'iban' => $data['iban'],
|
||||
]
|
||||
);
|
||||
$databaseData
|
||||
= [
|
||||
'user_id' => $this->user->id,
|
||||
'account_type_id' => $accountType->id,
|
||||
'name' => $data['name'],
|
||||
'virtual_balance' => $data['virtualBalance'],
|
||||
'active' => $data['active'] === true ? true : false,
|
||||
'iban' => $data['iban'],
|
||||
];
|
||||
$newAccount = new Account($databaseData);
|
||||
Log::debug('Final account creation dataset', $databaseData);
|
||||
$newAccount->save();
|
||||
// verify its creation:
|
||||
if (is_null($newAccount->id)) {
|
||||
@@ -268,6 +270,7 @@ class AccountRepository implements AccountRepositoryInterface
|
||||
sprintf('Could not create account "%s" (%d error(s))', $data['name'], $newAccount->getErrors()->count()), $newAccount->getErrors()->toArray()
|
||||
);
|
||||
throw new FireflyException(sprintf('Tried to create account named "%s" but failed. The logs have more details.', $data['name']));
|
||||
|
||||
}
|
||||
Log::debug(sprintf('Created new account #%d named "%s" of type %s.', $newAccount->id, $newAccount->name, $accountType->type));
|
||||
|
||||
@@ -457,12 +460,12 @@ class AccountRepository implements AccountRepositoryInterface
|
||||
// update transactions:
|
||||
/** @var Transaction $transaction */
|
||||
foreach ($journal->transactions()->get() as $transaction) {
|
||||
if ($account->id == $transaction->account_id) {
|
||||
if ($account->id === $transaction->account_id) {
|
||||
$transaction->amount = $amount;
|
||||
$transaction->transaction_currency_id = $currencyId;
|
||||
$transaction->save();
|
||||
}
|
||||
if ($account->id != $transaction->account_id) {
|
||||
if ($account->id !== $transaction->account_id) {
|
||||
$transaction->amount = bcmul($amount, '-1');
|
||||
$transaction->transaction_currency_id = $currencyId;
|
||||
$transaction->save();
|
||||
@@ -491,4 +494,26 @@ class AccountRepository implements AccountRepositoryInterface
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param null|string $iban
|
||||
*
|
||||
* @return null|string
|
||||
*/
|
||||
private function filterIban(?string $iban)
|
||||
{
|
||||
if (is_null($iban)) {
|
||||
return null;
|
||||
}
|
||||
$data = ['iban' => $iban];
|
||||
$rules = ['iban' => 'required|iban'];
|
||||
$validator = Validator::make($data, $rules);
|
||||
if ($validator->fails()) {
|
||||
Log::error(sprintf('Detected invalid IBAN ("%s"). Return NULL instead.', $iban));
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
return $iban;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -206,7 +206,8 @@ class AccountTasker implements AccountTaskerInterface
|
||||
$expenses[$opposingId]['count']++;
|
||||
}
|
||||
// do averages:
|
||||
foreach ($expenses as $key => $entry) {
|
||||
$keys = array_keys($expenses);
|
||||
foreach ($keys as $key) {
|
||||
if ($expenses[$key]['count'] > 1) {
|
||||
$expenses[$key]['average'] = bcdiv($expenses[$key]['sum'], strval($expenses[$key]['count']));
|
||||
}
|
||||
|
||||
@@ -62,6 +62,36 @@ class AttachmentRepository implements AttachmentRepositoryInterface
|
||||
return $disk->exists($attachment->fileName());
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $id
|
||||
*
|
||||
* @return Attachment
|
||||
*/
|
||||
public function find(int $id): Attachment
|
||||
{
|
||||
$attachment = $this->user->attachments()->find($id);
|
||||
if (is_null($attachment)) {
|
||||
return new Attachment;
|
||||
}
|
||||
|
||||
return $attachment;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $id
|
||||
*
|
||||
* @return Attachment
|
||||
*/
|
||||
public function findWithoutUser(int $id): Attachment
|
||||
{
|
||||
$attachment = Attachment::find($id);
|
||||
if (is_null($attachment)) {
|
||||
return new Attachment;
|
||||
}
|
||||
|
||||
return $attachment;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Collection
|
||||
*/
|
||||
|
||||
@@ -40,6 +40,20 @@ interface AttachmentRepositoryInterface
|
||||
*/
|
||||
public function exists(Attachment $attachment): bool;
|
||||
|
||||
/**
|
||||
* @param int $id
|
||||
*
|
||||
* @return Attachment
|
||||
*/
|
||||
public function find(int $id): Attachment;
|
||||
|
||||
/**
|
||||
* @param int $id
|
||||
*
|
||||
* @return Attachment
|
||||
*/
|
||||
public function findWithoutUser(int $id): Attachment;
|
||||
|
||||
/**
|
||||
* @return Collection
|
||||
*/
|
||||
|
||||
@@ -116,7 +116,7 @@ class BillRepository implements BillRepositoryInterface
|
||||
$set = $set->sortBy(
|
||||
function (Bill $bill) {
|
||||
|
||||
$int = $bill->active == 1 ? 0 : 1;
|
||||
$int = $bill->active === 1 ? 0 : 1;
|
||||
|
||||
return $int . strtolower($bill->name);
|
||||
}
|
||||
@@ -168,7 +168,7 @@ class BillRepository implements BillRepositoryInterface
|
||||
$set = $set->sortBy(
|
||||
function (Bill $bill) {
|
||||
|
||||
$int = $bill->active == 1 ? 0 : 1;
|
||||
$int = $bill->active === 1 ? 0 : 1;
|
||||
|
||||
return $int . strtolower($bill->name);
|
||||
}
|
||||
@@ -500,7 +500,7 @@ class BillRepository implements BillRepositoryInterface
|
||||
|
||||
return true;
|
||||
}
|
||||
if ($bill->id == $journal->bill_id) {
|
||||
if ($bill->id === $journal->bill_id) {
|
||||
// if no match, but bill used to match, remove it:
|
||||
$journal->bill_id = null;
|
||||
$journal->save();
|
||||
|
||||
@@ -32,7 +32,14 @@ use Log;
|
||||
*/
|
||||
trait CreateJournalsTrait
|
||||
{
|
||||
|
||||
/**
|
||||
* @param User $user
|
||||
* @param TransactionType $type
|
||||
* @param array $data
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
abstract public function storeAccounts(User $user, TransactionType $type, array $data): array;
|
||||
|
||||
/**
|
||||
*
|
||||
|
||||
@@ -191,6 +191,9 @@ class JournalTasker implements JournalTaskerInterface
|
||||
$journalId = intval($transaction->transaction_journal_id);
|
||||
$identifier = intval($transaction->identifier);
|
||||
|
||||
// also add the virtual balance to the balance:
|
||||
$virtualBalance = strval($transaction->account->virtual_balance);
|
||||
|
||||
// go!
|
||||
$sum = Transaction::leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id')
|
||||
->where('account_id', $transaction->account_id)
|
||||
@@ -224,6 +227,6 @@ class JournalTasker implements JournalTaskerInterface
|
||||
}
|
||||
)->sum('transactions.amount');
|
||||
|
||||
return strval($sum);
|
||||
return bcadd(strval($sum), $virtualBalance);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -73,7 +73,7 @@ trait UpdateJournalsTrait
|
||||
protected function updateDestinationTransaction(TransactionJournal $journal, Account $account, array $data)
|
||||
{
|
||||
$set = $journal->transactions()->where('amount', '>', 0)->get();
|
||||
if ($set->count() != 1) {
|
||||
if ($set->count() !== 1) {
|
||||
throw new FireflyException(sprintf('Journal #%d has %d transactions with an amount more than zero.', $journal->id, $set->count()));
|
||||
}
|
||||
/** @var Transaction $transaction */
|
||||
@@ -98,7 +98,7 @@ trait UpdateJournalsTrait
|
||||
{
|
||||
// should be one:
|
||||
$set = $journal->transactions()->where('amount', '<', 0)->get();
|
||||
if ($set->count() != 1) {
|
||||
if ($set->count() !== 1) {
|
||||
throw new FireflyException(sprintf('Journal #%d has %d transactions with an amount more than zero.', $journal->id, $set->count()));
|
||||
}
|
||||
/** @var Transaction $transaction */
|
||||
@@ -140,7 +140,7 @@ trait UpdateJournalsTrait
|
||||
DB::table('tag_transaction_journal')->where('transaction_journal_id', $journal->id)->whereNotIn('tag_id', $ids)->delete();
|
||||
}
|
||||
// if count is zero, delete them all:
|
||||
if (count($ids) == 0) {
|
||||
if (count($ids) === 0) {
|
||||
DB::table('tag_transaction_journal')->where('transaction_journal_id', $journal->id)->delete();
|
||||
}
|
||||
|
||||
|
||||
@@ -236,7 +236,7 @@ class RuleRepository implements RuleRepositoryInterface
|
||||
$rule->rule_group_id = $data['rule_group_id'];
|
||||
$rule->order = ($order + 1);
|
||||
$rule->active = 1;
|
||||
$rule->stop_processing = intval($data['stop_processing']) == 1;
|
||||
$rule->stop_processing = intval($data['stop_processing']) === 1;
|
||||
$rule->title = $data['title'];
|
||||
$rule->description = strlen($data['description']) > 0 ? $data['description'] : null;
|
||||
|
||||
@@ -265,7 +265,7 @@ class RuleRepository implements RuleRepositoryInterface
|
||||
$ruleAction->active = 1;
|
||||
$ruleAction->stop_processing = $values['stopProcessing'];
|
||||
$ruleAction->action_type = $values['action'];
|
||||
$ruleAction->action_value = $values['value'];
|
||||
$ruleAction->action_value = is_null($values['value']) ? '' : $values['value'];
|
||||
$ruleAction->save();
|
||||
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user