Compare commits

..

477 Commits
4.6.2 ... 4.6.4

Author SHA1 Message Date
James Cole
2915fae942 Merge branch 'release/4.6.4' 2017-08-13 09:04:40 +02:00
James Cole
f9b5468481 Last minute fixes for import routine. 2017-08-13 08:49:45 +02:00
James Cole
287f37eba5 Remove unused parameters. 2017-08-13 08:24:58 +02:00
James Cole
26740668da Import transfer error 2017-08-12 21:23:19 +02:00
James Cole
bdd72f0d30 Fix path for tag controller. 2017-08-12 21:22:59 +02:00
James Cole
7e79f25949 Merge languages. 2017-08-12 19:47:46 +02:00
James Cole
2d917e166c Merge pull request #755 from firefly-iii/l10n_develop
New Crowdin translations
2017-08-12 19:47:26 +02:00
James Cole
ad4a811d0a Small bug fixes [skip ci] 2017-08-12 19:11:24 +02:00
James Cole
ab1aa97af4 Read me says 7.1 too. [skip ci] 2017-08-12 19:11:11 +02:00
James Cole
018941c5b3 Actually use journal [skip ci] 2017-08-12 19:05:29 +02:00
James Cole
cc1439fb7b Revamped import routine. 2017-08-12 19:03:42 +02:00
James Cole
f684a2900b Include default currency. 2017-08-12 16:42:29 +02:00
James Cole
83b721a322 Small bug fix in map / roles. 2017-08-12 16:41:05 +02:00
James Cole
a879528ed8 Back to basics. 2017-08-12 16:40:51 +02:00
James Cole
accbff3ccb Small cleanup in CSV processor 2017-08-12 16:12:30 +02:00
James Cole
0375f77b73 Prevent asset account from being found by opposing account. #719 2017-08-12 13:49:40 +02:00
James Cole
9d01162f42 New translations auth.php (Chinese Traditional) 2017-08-12 10:42:10 +02:00
James Cole
3436dc1564 New translations firefly.php (Slovenian) 2017-08-12 10:42:09 +02:00
James Cole
089293079f New translations firefly.php (Spanish) 2017-08-12 10:42:06 +02:00
James Cole
00e2cd7c04 New translations form.php (Chinese Traditional) 2017-08-12 10:42:04 +02:00
James Cole
f5abb933b0 New translations form.php (French) 2017-08-12 10:42:03 +02:00
James Cole
848386bbaf New translations form.php (Dutch) 2017-08-12 10:42:01 +02:00
James Cole
5562a3c2ae New translations firefly.php (Portuguese, Brazilian) 2017-08-12 10:42:00 +02:00
James Cole
9e8738660b New translations firefly.php (Polish) 2017-08-12 10:41:57 +02:00
James Cole
178772d50d New translations firefly.php (Chinese Traditional) 2017-08-12 10:41:55 +02:00
James Cole
8d7f29c7c9 New translations firefly.php (Dutch) 2017-08-12 10:41:52 +02:00
James Cole
255f87f1c3 New translations firefly.php (German) 2017-08-12 10:41:50 +02:00
James Cole
975354d081 New translations firefly.php (French) 2017-08-12 10:41:48 +02:00
James Cole
ec6890ced1 New translations form.php (German) 2017-08-12 10:41:46 +02:00
James Cole
290efb4b62 New translations form.php (Polish) 2017-08-12 10:41:44 +02:00
James Cole
ab3ea76244 New translations help.php (Portuguese, Brazilian) 2017-08-12 10:41:43 +02:00
James Cole
21d07aef15 New translations help.php (Slovenian) 2017-08-12 10:41:42 +02:00
James Cole
6031cd02b9 New translations help.php (Spanish) 2017-08-12 10:41:41 +02:00
James Cole
2cdb4cab12 New translations list.php (Dutch) 2017-08-12 10:41:41 +02:00
James Cole
679217f2ef New translations list.php (Chinese Traditional) 2017-08-12 10:41:40 +02:00
James Cole
3aacc0a258 New translations help.php (Polish) 2017-08-12 10:41:39 +02:00
James Cole
254cf6cc5b New translations help.php (German) 2017-08-12 10:41:38 +02:00
James Cole
c06095259a New translations form.php (Slovenian) 2017-08-12 10:41:37 +02:00
James Cole
30fd98c8d7 New translations form.php (Portuguese, Brazilian) 2017-08-12 10:41:36 +02:00
James Cole
3a410f04fe New translations form.php (Spanish) 2017-08-12 10:41:35 +02:00
James Cole
4a0a58cdc1 New translations help.php (Chinese Traditional) 2017-08-12 10:41:34 +02:00
James Cole
c834f93539 New translations help.php (French) 2017-08-12 10:41:33 +02:00
James Cole
35bdb84e57 New translations help.php (Dutch) 2017-08-12 10:41:32 +02:00
James Cole
36a3a09fcc New translations breadcrumbs.php (Dutch) 2017-08-12 10:41:30 +02:00
James Cole
2c07b490e8 New translations breadcrumbs.php (French) 2017-08-12 10:41:29 +02:00
James Cole
5790311cbd New translations breadcrumbs.php (German) 2017-08-12 10:41:29 +02:00
James Cole
fde3322117 New translations breadcrumbs.php (Portuguese, Brazilian) 2017-08-12 10:41:28 +02:00
James Cole
5664e133d1 New translations breadcrumbs.php (Polish) 2017-08-12 10:41:27 +02:00
James Cole
a1dc38a83f New translations breadcrumbs.php (Chinese Traditional) 2017-08-12 10:41:26 +02:00
James Cole
df974f93ee New translations auth.php (Spanish) 2017-08-12 10:41:25 +02:00
James Cole
3dde64cdac New translations auth.php (French) 2017-08-12 10:41:24 +02:00
James Cole
6b4887df0f New translations auth.php (Dutch) 2017-08-12 10:41:23 +02:00
James Cole
ca8ef3961e New translations auth.php (German) 2017-08-12 10:41:22 +02:00
James Cole
114e0e5b1e New translations auth.php (Polish) 2017-08-12 10:41:22 +02:00
James Cole
c7a247cbba New translations auth.php (Slovenian) 2017-08-12 10:41:21 +02:00
James Cole
3a641a7020 New translations auth.php (Portuguese, Brazilian) 2017-08-12 10:41:20 +02:00
James Cole
0977859bf5 New translations breadcrumbs.php (Slovenian) 2017-08-12 10:41:19 +02:00
James Cole
a644253ee9 New translations breadcrumbs.php (Spanish) 2017-08-12 10:41:18 +02:00
James Cole
3403356be8 New translations config.php (Spanish) 2017-08-12 10:41:16 +02:00
James Cole
4a9ec0d0d4 New translations config.php (Slovenian) 2017-08-12 10:41:15 +02:00
James Cole
250c5c0745 New translations config.php (Dutch) 2017-08-12 10:41:14 +02:00
James Cole
120b5880b1 New translations config.php (Chinese Traditional) 2017-08-12 10:41:13 +02:00
James Cole
18eff73795 New translations config.php (French) 2017-08-12 10:41:12 +02:00
James Cole
89b9dbb1b4 New translations config.php (German) 2017-08-12 10:41:12 +02:00
James Cole
dde4b3ab93 New translations config.php (Portuguese, Brazilian) 2017-08-12 10:41:11 +02:00
James Cole
b1cc655ba5 New translations config.php (Polish) 2017-08-12 10:41:10 +02:00
James Cole
5645a12d5b New translations list.php (French) 2017-08-12 10:41:09 +02:00
James Cole
ce1a935c06 New translations list.php (German) 2017-08-12 10:41:08 +02:00
James Cole
58b90ad6c9 New translations breadcrumbs.php (Russian) 2017-08-12 10:41:07 +02:00
James Cole
4740e63e76 New translations config.php (Russian) 2017-08-12 10:41:06 +02:00
James Cole
319932c734 New translations form.php (Russian) 2017-08-12 10:41:05 +02:00
James Cole
7cf415288a New translations firefly.php (Russian) 2017-08-12 10:41:03 +02:00
James Cole
fe67c1db8f New translations auth.php (Russian) 2017-08-12 10:41:01 +02:00
James Cole
09afbd89b8 New translations demo.php (Spanish) 2017-08-12 10:41:00 +02:00
James Cole
824f7d827c New translations demo.php (French) 2017-08-12 10:40:59 +02:00
James Cole
3a8ca56f95 New translations demo.php (German) 2017-08-12 10:40:58 +02:00
James Cole
137e3fd083 New translations demo.php (Polish) 2017-08-12 10:40:57 +02:00
James Cole
1285944b1f New translations demo.php (Slovenian) 2017-08-12 10:40:56 +02:00
James Cole
cd8e9deab7 New translations demo.php (Portuguese, Brazilian) 2017-08-12 10:40:56 +02:00
James Cole
de424eac64 New translations help.php (Russian) 2017-08-12 10:40:55 +02:00
James Cole
d106a09766 New translations list.php (Russian) 2017-08-12 10:40:54 +02:00
James Cole
963e8ccd2c New translations intro.php (Polish) 2017-08-12 10:40:53 +02:00
James Cole
3474b8f2d0 New translations intro.php (Portuguese, Brazilian) 2017-08-12 10:40:52 +02:00
James Cole
e27b449ae9 New translations intro.php (Russian) 2017-08-12 10:40:51 +02:00
James Cole
7656818456 New translations intro.php (Spanish) 2017-08-12 10:40:50 +02:00
James Cole
d214be1215 New translations intro.php (Slovenian) 2017-08-12 10:40:49 +02:00
James Cole
c8678c3ee5 New translations intro.php (German) 2017-08-12 10:40:48 +02:00
James Cole
108ee40c3f New translations intro.php (French) 2017-08-12 10:40:47 +02:00
James Cole
de51d205dc New translations passwords.php (Russian) 2017-08-12 10:40:46 +02:00
James Cole
1f76246edc New translations pagination.php (Russian) 2017-08-12 10:40:45 +02:00
James Cole
f91974b766 New translations validation.php (Russian) 2017-08-12 10:40:44 +02:00
James Cole
df85936145 New translations demo.php (Russian) 2017-08-12 10:40:43 +02:00
James Cole
f39aa0f52a New translations intro.php (Dutch) 2017-08-12 10:40:42 +02:00
James Cole
180812394b New translations intro.php (Chinese Traditional) 2017-08-12 10:40:41 +02:00
James Cole
291fa9597f New translations demo.php (Dutch) 2017-08-12 10:40:40 +02:00
James Cole
6c4d8c25fc New translations demo.php (Chinese Traditional) 2017-08-12 10:40:39 +02:00
James Cole
bf4b32a8e9 New translations pagination.php (Polish) 2017-08-12 10:40:39 +02:00
James Cole
2e9dac7678 New translations pagination.php (Portuguese, Brazilian) 2017-08-12 10:40:38 +02:00
James Cole
369314c1cb New translations pagination.php (Slovenian) 2017-08-12 10:40:37 +02:00
James Cole
6ab86898af New translations passwords.php (Chinese Traditional) 2017-08-12 10:40:36 +02:00
James Cole
f3aa65d219 New translations pagination.php (Spanish) 2017-08-12 10:40:35 +02:00
James Cole
edf4f19de9 New translations pagination.php (German) 2017-08-12 10:40:34 +02:00
James Cole
91eaaae6d7 New translations pagination.php (French) 2017-08-12 10:40:34 +02:00
James Cole
ca0ae5a165 New translations list.php (Portuguese, Brazilian) 2017-08-12 10:40:33 +02:00
James Cole
86d788d294 New translations list.php (Polish) 2017-08-12 10:40:32 +02:00
James Cole
e345cea9be New translations list.php (Slovenian) 2017-08-12 10:40:31 +02:00
James Cole
779f7841c2 New translations list.php (Spanish) 2017-08-12 10:40:30 +02:00
James Cole
15fdca46b8 New translations pagination.php (Dutch) 2017-08-12 10:40:29 +02:00
James Cole
97e099c70f New translations pagination.php (Chinese Traditional) 2017-08-12 10:40:28 +02:00
James Cole
d5a58bb763 New translations passwords.php (Dutch) 2017-08-12 10:40:26 +02:00
James Cole
2774df0d30 New translations passwords.php (French) 2017-08-12 10:40:25 +02:00
James Cole
93a1799a4b New translations validation.php (German) 2017-08-12 10:40:24 +02:00
James Cole
f65f051c7c New translations validation.php (Polish) 2017-08-12 10:40:23 +02:00
James Cole
c46952dd4e New translations validation.php (Portuguese, Brazilian) 2017-08-12 10:40:22 +02:00
James Cole
1dad6b6118 New translations validation.php (Spanish) 2017-08-12 10:40:22 +02:00
James Cole
e964621f2c New translations validation.php (Slovenian) 2017-08-12 10:40:21 +02:00
James Cole
46ae62f693 New translations validation.php (French) 2017-08-12 10:40:20 +02:00
James Cole
7d578f5852 New translations validation.php (Dutch) 2017-08-12 10:40:19 +02:00
James Cole
467f257ad3 New translations passwords.php (Polish) 2017-08-12 10:40:18 +02:00
James Cole
55186c0a49 New translations passwords.php (German) 2017-08-12 10:40:17 +02:00
James Cole
1a0a4f7112 New translations passwords.php (Portuguese, Brazilian) 2017-08-12 10:40:16 +02:00
James Cole
658290ae80 New translations passwords.php (Slovenian) 2017-08-12 10:40:15 +02:00
James Cole
cb9f29c6fc New translations validation.php (Chinese Traditional) 2017-08-12 10:40:15 +02:00
James Cole
d8f4955292 New translations passwords.php (Spanish) 2017-08-12 10:40:13 +02:00
James Cole
9803932324 Various code clean up. 2017-08-12 10:27:45 +02:00
James Cole
a3a416b5e2 New composer file 2017-08-12 09:15:09 +02:00
James Cole
1453a318fe Merge branch 'develop' of https://github.com/firefly-iii/firefly-iii into develop
* 'develop' of https://github.com/firefly-iii/firefly-iii: (23 commits)
  New translations firefly.php (Dutch)
  New translations firefly.php (Chinese Traditional)
  New translations firefly.php (Dutch)
  New translations firefly.php (Polish)
  New translations firefly.php (Portuguese, Brazilian)
  New translations firefly.php (Slovenian)
  New translations firefly.php (Spanish)
  New translations firefly.php (German)
  New translations firefly.php (French)
  New translations firefly.php (Russian)
  New translations firefly.php (Dutch)
  New translations validation.php (Dutch)
  New translations form.php (Dutch)
  New translations csv.php (Dutch)
  New translations firefly.php (Slovenian)
  New translations firefly.php (Spanish)
  New translations firefly.php (Portuguese, Brazilian)
  New translations firefly.php (Polish)
  New translations firefly.php (Chinese Traditional)
  New translations firefly.php (Dutch)
  ...
2017-08-12 09:14:54 +02:00
James Cole
9ffa01d318 Merge pull request #754 from firefly-iii/l10n_develop
New Crowdin translations
2017-08-12 09:14:18 +02:00
James Cole
29b779c873 Remove intro popup. 2017-08-12 09:06:13 +02:00
James Cole
598307f676 Update change log 2017-08-12 09:06:02 +02:00
James Cole
aa9d546bf5 New translations firefly.php (Dutch) 2017-08-12 08:20:30 +02:00
James Cole
f2ef245eca New translations firefly.php (Chinese Traditional) 2017-08-12 08:10:24 +02:00
James Cole
458606381e New translations firefly.php (Dutch) 2017-08-12 08:10:21 +02:00
James Cole
244f174dc9 New translations firefly.php (Polish) 2017-08-12 08:10:19 +02:00
James Cole
8a577f3197 New translations firefly.php (Portuguese, Brazilian) 2017-08-12 08:10:17 +02:00
James Cole
2f1b24cc14 New translations firefly.php (Slovenian) 2017-08-12 08:10:13 +02:00
James Cole
d5f836db7a New translations firefly.php (Spanish) 2017-08-12 08:10:10 +02:00
James Cole
ffd7c36cb2 New translations firefly.php (German) 2017-08-12 08:05:26 +02:00
James Cole
dba543d539 New translations firefly.php (French) 2017-08-12 08:05:24 +02:00
James Cole
9753c0f8eb New translations firefly.php (Russian) 2017-08-12 08:05:09 +02:00
James Cole
c50b4d3d47 New translations firefly.php (Dutch) 2017-08-12 08:05:02 +02:00
James Cole
907f886cf0 New translations validation.php (Dutch) 2017-08-12 08:04:53 +02:00
James Cole
218a2d2004 Expand password modal. 2017-08-12 08:01:40 +02:00
James Cole
0e0f2c6833 New translations form.php (Dutch) 2017-08-12 08:00:06 +02:00
James Cole
e59090d3b6 Text and modal for password security. 2017-08-12 07:56:02 +02:00
James Cole
0404be8bef New translations csv.php (Dutch) 2017-08-12 07:50:05 +02:00
James Cole
337895cbaa Add newlines to files. 2017-08-12 07:48:39 +02:00
James Cole
743547096e Various code cleanup 2017-08-12 07:47:42 +02:00
James Cole
81fb3df45e Fix #747 2017-08-12 07:39:58 +02:00
James Cole
12624cab5b Improve foreign currency routine for #746 2017-08-12 07:38:22 +02:00
James Cole
7d62ea88d2 New translations firefly.php (Slovenian) 2017-08-12 07:10:58 +02:00
James Cole
abcc277430 New translations firefly.php (Spanish) 2017-08-12 07:10:55 +02:00
James Cole
4c4c9426ee New translations firefly.php (Portuguese, Brazilian) 2017-08-12 07:10:52 +02:00
James Cole
88fdeb2bf2 New translations firefly.php (Polish) 2017-08-12 07:10:50 +02:00
James Cole
f9ab868022 New translations firefly.php (Chinese Traditional) 2017-08-12 07:10:47 +02:00
James Cole
148d2cec8f New translations firefly.php (Dutch) 2017-08-12 07:10:45 +02:00
James Cole
5869f157f6 New translations firefly.php (German) 2017-08-12 07:10:43 +02:00
James Cole
69a2902161 New translations firefly.php (French) 2017-08-12 07:10:40 +02:00
James Cole
c19b048249 New translations firefly.php (Russian) 2017-08-12 07:10:26 +02:00
James Cole
cd2c8acdb2 Merge pull request #729 from firefly-iii/l10n_develop
New Crowdin translations
2017-08-12 07:01:33 +02:00
James Cole
7d0d1c764f Make sure we’re still at 4.6.4 and bunq import is unavailable. [skip ci] 2017-08-12 07:01:21 +02:00
James Cole
b5acf1d529 Merge early release branch
Code for #749
  Add link to split withdrawal
  Clarifies #751
  Text for #748
  Improve error reporting #752
  Small code cleanup.
  Add copyright markers.
2017-08-12 06:59:59 +02:00
James Cole
72b08384ad Code for #749 2017-08-12 06:55:46 +02:00
James Cole
f51e48f282 Add link to split withdrawal 2017-08-12 06:55:35 +02:00
James Cole
2e6d1f3642 Clarifies #751 2017-08-12 06:44:00 +02:00
James Cole
716af4ed93 Text for #748 2017-08-12 06:41:14 +02:00
James Cole
31de86c9eb Improve error reporting #752 2017-08-12 06:40:17 +02:00
James Cole
04a2cd1f1f Small code cleanup. 2017-08-11 05:42:15 +02:00
James Cole
bc0ef11a8c Add copyright markers. 2017-08-11 05:36:05 +02:00
James Cole
90c8420a4c Initial code for preparing bunq import #714 2017-08-11 05:21:00 +02:00
James Cole
657d5e0d74 Upgrade version in develop 2017-08-11 05:02:49 +02:00
James Cole
03fa4d957c Added upgrade instruction [skip ci] 2017-08-10 20:52:10 +02:00
James Cole
8d4530f1f2 Fixes #743 2017-08-10 20:48:29 +02:00
James Cole
6d15c503c3 Fix #515 2017-08-10 20:41:33 +02:00
James Cole
75ddb34398 Remove references to date range thing. 2017-08-10 19:57:18 +02:00
James Cole
6fbfe6fb72 Remove references to date range thing. 2017-08-10 19:55:52 +02:00
James Cole
db500e911c update date range picker 2017-08-10 19:55:23 +02:00
James Cole
c0c5ced6ad Fix multi currency edit display 2017-08-10 19:55:13 +02:00
James Cole
726270f8bc New translations form.php (Chinese Traditional) 2017-08-08 16:41:01 +02:00
James Cole
c3908450a0 New translations form.php (French) 2017-08-08 16:40:59 +02:00
James Cole
f7ba05f465 New translations form.php (Dutch) 2017-08-08 16:40:58 +02:00
James Cole
dea7f7d5d6 New translations form.php (German) 2017-08-08 16:40:46 +02:00
James Cole
ed7a0a2b9d New translations form.php (Polish) 2017-08-08 16:40:45 +02:00
James Cole
ac6b4db0f8 New translations form.php (Slovenian) 2017-08-08 16:40:41 +02:00
James Cole
b5d659c13c New translations form.php (Portuguese, Brazilian) 2017-08-08 16:40:40 +02:00
James Cole
919cb5d1f2 New translations form.php (Spanish) 2017-08-08 16:40:39 +02:00
James Cole
e504d3cc35 New translations form.php (Russian) 2017-08-08 16:40:28 +02:00
James Cole
1d67939e76 No password service in env file. [skip ci] 2017-08-08 16:30:43 +02:00
James Cole
5d1e90d29c Expand verify password routine. 2017-08-08 16:30:21 +02:00
James Cole
1678eba9cc New translations intro.php (Polish) 2017-08-08 16:10:35 +02:00
James Cole
cc142b2ba1 New translations intro.php (Portuguese, Brazilian) 2017-08-08 16:10:33 +02:00
James Cole
48cbffba14 New translations intro.php (Russian) 2017-08-08 16:10:32 +02:00
James Cole
d909bb1b25 New translations intro.php (Spanish) 2017-08-08 16:10:31 +02:00
James Cole
3313e66fc2 New translations intro.php (Slovenian) 2017-08-08 16:10:30 +02:00
James Cole
085892a4c8 New translations intro.php (German) 2017-08-08 16:10:28 +02:00
James Cole
817e059230 New translations intro.php (French) 2017-08-08 16:10:27 +02:00
James Cole
7ce8891246 New translations intro.php (Dutch) 2017-08-08 16:10:25 +02:00
James Cole
439b2589f9 New translations intro.php (Chinese Traditional) 2017-08-08 16:10:24 +02:00
James Cole
e7b5cf66d2 Fix #739 2017-08-08 16:06:58 +02:00
James Cole
b29fabf76c Include virtual balance in transaction overview #735 2017-08-07 20:36:23 +02:00
James Cole
5d837c3ee4 Bump version. [skip ci] 2017-08-07 20:27:58 +02:00
James Cole
4c0396ad1c Fixes #733 2017-08-07 17:59:26 +02:00
James Cole
97b62fce79 New translations firefly.php (Russian) 2017-08-05 02:00:07 +02:00
James Cole
e904a38735 New translations firefly.php (Russian) 2017-08-05 01:10:08 +02:00
James Cole
ae72187aed New translations form.php (Russian) 2017-08-05 01:10:05 +02:00
James Cole
e4ebeefa61 New translations firefly.php (Russian) 2017-08-05 01:00:09 +02:00
James Cole
417373ba70 New translations form.php (Russian) 2017-08-05 01:00:06 +02:00
James Cole
36f9ded08e New translations breadcrumbs.php (Russian) 2017-08-05 01:00:05 +02:00
James Cole
858925b8c8 New translations firefly.php (Slovenian) 2017-08-04 17:11:13 +02:00
James Cole
d36c96fba9 New translations firefly.php (Spanish) 2017-08-04 17:11:10 +02:00
James Cole
2be6fb329e New translations firefly.php (Portuguese, Brazilian) 2017-08-04 17:11:05 +02:00
James Cole
da56363ef9 New translations firefly.php (Polish) 2017-08-04 17:11:02 +02:00
James Cole
90d73228f3 New translations firefly.php (Chinese Traditional) 2017-08-04 17:10:59 +02:00
James Cole
1c76fcd26b New translations firefly.php (Dutch) 2017-08-04 17:10:55 +02:00
James Cole
c26c4ddf15 New translations firefly.php (German) 2017-08-04 17:10:53 +02:00
James Cole
537bdb62da New translations firefly.php (French) 2017-08-04 17:10:49 +02:00
James Cole
b2ac2bd97a New translations firefly.php (Russian) 2017-08-04 17:10:32 +02:00
James Cole
aee6ea56b5 New translations validation.php (Russian) 2017-08-04 17:10:24 +02:00
James Cole
ff5506c842 New translations validation.php (German) 2017-08-04 17:10:16 +02:00
James Cole
94c5340fbf New translations validation.php (Polish) 2017-08-04 17:10:15 +02:00
James Cole
0e11026b60 New translations validation.php (Portuguese, Brazilian) 2017-08-04 17:10:14 +02:00
James Cole
1d8e953ebc New translations validation.php (Spanish) 2017-08-04 17:10:13 +02:00
James Cole
8708fba4bc New translations validation.php (Slovenian) 2017-08-04 17:10:12 +02:00
James Cole
bc1294ae61 New translations validation.php (French) 2017-08-04 17:10:10 +02:00
James Cole
308f05101e New translations validation.php (Dutch) 2017-08-04 17:10:09 +02:00
James Cole
f368739303 New translations validation.php (Chinese Traditional) 2017-08-04 17:10:07 +02:00
James Cole
1bca1b921b Fix import tests. 2017-08-04 16:24:44 +02:00
James Cole
3e513e92b1 Import routine is prepped for multi-source import (such as banks). 2017-08-04 15:56:14 +02:00
James Cole
194fe178c0 Refactor for #732 2017-08-04 15:48:26 +02:00
James Cole
5d10a19bfa First version of routine #732 2017-08-04 15:46:52 +02:00
James Cole
8f52a68526 New translations firefly.php (French) 2017-08-04 13:10:13 +02:00
James Cole
c67d0d59bb New translations firefly.php (French) 2017-08-04 13:00:08 +02:00
James Cole
4477064f17 New translations firefly.php (French) 2017-08-04 12:50:08 +02:00
James Cole
9dbe24b37c New translations csv.php (Portuguese, Brazilian) 2017-08-04 08:20:13 +02:00
James Cole
46a85295e8 New translations firefly.php (Portuguese, Brazilian) 2017-08-04 08:20:11 +02:00
James Cole
63170324a8 New translations form.php (Portuguese, Brazilian) 2017-08-04 08:20:08 +02:00
James Cole
4fd157b5f4 New translations firefly.php (Polish) 2017-08-03 21:00:23 +02:00
James Cole
e5406a0ea3 New translations firefly.php (Chinese Traditional) 2017-08-03 21:00:20 +02:00
James Cole
9b64ba21fd New translations firefly.php (Portuguese, Brazilian) 2017-08-03 21:00:17 +02:00
James Cole
18fcf07971 New translations firefly.php (Slovenian) 2017-08-03 21:00:14 +02:00
James Cole
d3404c6570 New translations firefly.php (Spanish) 2017-08-03 21:00:10 +02:00
James Cole
1878b5287b Cheap solution for #730 [skip ci] 2017-08-03 20:50:16 +02:00
James Cole
7f74545586 New translations firefly.php (German) 2017-07-30 14:10:38 +02:00
James Cole
a920894a8f New translations firefly.php (French) 2017-07-30 14:10:35 +02:00
James Cole
2c912456ce New translations firefly.php (Russian) 2017-07-30 14:10:23 +02:00
James Cole
ebda475972 New translations firefly.php (Dutch) 2017-07-30 14:10:17 +02:00
James Cole
05aace84e1 Fix tests for show() routines. 2017-07-30 13:57:46 +02:00
James Cole
1cfa1faccc Remove all time jumps because they’re unclear and complex. 2017-07-30 13:53:04 +02:00
James Cole
1caa393974 Simple cache routine. 2017-07-30 13:44:36 +02:00
James Cole
ef357ab6e5 Fix tests 2017-07-30 13:42:38 +02:00
James Cole
c39c5492ea Simplify controllers 2017-07-30 13:42:30 +02:00
James Cole
1a5d54f74f New translations firefly.php (Slovenian) 2017-07-30 10:40:53 +02:00
James Cole
13dc6c7dfb New translations firefly.php (Spanish) 2017-07-30 10:40:50 +02:00
James Cole
4c7dee69c2 New translations firefly.php (Portuguese, Brazilian) 2017-07-30 10:40:47 +02:00
James Cole
dc9cf7689d New translations firefly.php (Polish) 2017-07-30 10:40:44 +02:00
James Cole
778a408c6c New translations firefly.php (Chinese Traditional) 2017-07-30 10:40:42 +02:00
James Cole
2b7f3061d0 New translations firefly.php (Dutch) 2017-07-30 10:40:40 +02:00
James Cole
92903e1ec3 New translations firefly.php (German) 2017-07-30 10:40:37 +02:00
James Cole
c5a621010e New translations firefly.php (French) 2017-07-30 10:40:35 +02:00
James Cole
0b5b636578 New translations firefly.php (Russian) 2017-07-30 10:40:20 +02:00
James Cole
8fcdb91ba3 Will no longer support extended tag modes. 2017-07-30 10:22:14 +02:00
James Cole
f67d5f1197 Fix bug in filter. 2017-07-30 10:21:45 +02:00
James Cole
6e0e6203cc Some help text for attachment decrypter 2017-07-30 10:21:35 +02:00
James Cole
2694ce4148 Decryption routine for #671 2017-07-30 08:22:39 +02:00
James Cole
7c02b032f6 New translations firefly.php (Slovenian) 2017-07-29 14:40:52 +02:00
James Cole
deb7754cb9 New translations firefly.php (Spanish) 2017-07-29 14:40:50 +02:00
James Cole
624f3c60bd New translations firefly.php (Portuguese, Brazilian) 2017-07-29 14:40:46 +02:00
James Cole
8a8e792faa New translations firefly.php (Polish) 2017-07-29 14:40:44 +02:00
James Cole
bc836011bc New translations firefly.php (Chinese Traditional) 2017-07-29 14:40:41 +02:00
James Cole
107eedfb49 New translations firefly.php (Dutch) 2017-07-29 14:40:38 +02:00
James Cole
61a7dcda23 New translations firefly.php (German) 2017-07-29 14:40:36 +02:00
James Cole
873ca4f438 New translations firefly.php (French) 2017-07-29 14:40:33 +02:00
James Cole
29221c2901 New translations firefly.php (Russian) 2017-07-29 14:40:19 +02:00
James Cole
eac9613df7 Fix for #648 2017-07-29 14:25:54 +02:00
James Cole
184d5d25a6 New translations csv.php (Spanish) 2017-07-29 08:40:46 +02:00
James Cole
ae33411566 New translations csv.php (Slovenian) 2017-07-29 08:40:34 +02:00
James Cole
479ebcc3fa New translations csv.php (Portuguese, Brazilian) 2017-07-29 08:40:33 +02:00
James Cole
1ae572cf30 New translations csv.php (Chinese Traditional) 2017-07-29 08:40:29 +02:00
James Cole
ac6e16688d New translations csv.php (Dutch) 2017-07-29 08:40:28 +02:00
James Cole
69159e0271 New translations csv.php (French) 2017-07-29 08:40:28 +02:00
James Cole
cae35d6a5a New translations csv.php (Polish) 2017-07-29 08:40:27 +02:00
James Cole
df9b65e296 New translations csv.php (German) 2017-07-29 08:40:26 +02:00
James Cole
fb0b34a6a0 New translations csv.php (Russian) 2017-07-29 08:40:22 +02:00
James Cole
db3b822aef Include warning for #722 2017-07-29 08:27:39 +02:00
James Cole
f4994ef151 Also store links when importing data. 2017-07-28 14:52:01 +02:00
James Cole
8e27291417 Tags no longer mappable. 2017-07-28 14:40:53 +02:00
James Cole
aae9ad78e5 This should fix the tests for now. 2017-07-26 18:40:48 +02:00
James Cole
fb3efbfc66 Fix parameter count. 2017-07-26 16:40:25 +02:00
James Cole
0f8a66609a Fix controller for #718 2017-07-26 16:40:14 +02:00
James Cole
b3bb8c386f Fix routes for #718 2017-07-26 16:40:00 +02:00
James Cole
b3b5e0e155 This will disable the PHP 7.0 tests. 2017-07-25 21:01:37 +02:00
James Cole
99a0bf1286 This fixes #717 2017-07-25 21:01:25 +02:00
James Cole
650f0ee752 Merge branch 'hotfix/4.6.3.1' into develop 2017-07-23 20:02:46 +02:00
James Cole
2ddcf1120f Merge branch 'hotfix/4.6.3.1' 2017-07-23 20:02:45 +02:00
James Cole
d45c74915c This fixes #715 2017-07-23 20:02:32 +02:00
James Cole
cd373791ac Updated composer file. 2017-07-23 20:00:26 +02:00
James Cole
7b03b0c5fc Some PHP 7.1 compatible code. 2017-07-23 19:06:24 +02:00
James Cole
57d6677131 Merge branch 'release/4.6.3' 2017-07-23 10:01:48 +02:00
James Cole
05e73344eb Various files for upgrade. 2017-07-23 09:54:07 +02:00
James Cole
fff8f96490 Merge pull request #706 from firefly-iii/l10n_develop
New Crowdin translations
2017-07-23 09:53:40 +02:00
James Cole
1ccfb34246 New translations firefly.php (Slovenian) 2017-07-23 09:50:53 +02:00
James Cole
b30bd00993 New translations firefly.php (Spanish) 2017-07-23 09:50:51 +02:00
James Cole
6b2d9fe816 New translations firefly.php (Portuguese, Brazilian) 2017-07-23 09:50:47 +02:00
James Cole
531114279d New translations firefly.php (Polish) 2017-07-23 09:50:45 +02:00
James Cole
944aaff0fa New translations firefly.php (Chinese Traditional) 2017-07-23 09:50:42 +02:00
James Cole
6bafa6819d New translations firefly.php (Dutch) 2017-07-23 09:50:40 +02:00
James Cole
8d7091285a New translations firefly.php (German) 2017-07-23 09:50:38 +02:00
James Cole
579e95219a New translations firefly.php (French) 2017-07-23 09:50:35 +02:00
James Cole
4a746881e3 New translations firefly.php (Russian) 2017-07-23 09:50:20 +02:00
James Cole
9122f1b642 New translations firefly.php (Dutch) 2017-07-23 09:40:08 +02:00
James Cole
8b033ed4a8 Update contributing and support guidelines [skip ci] 2017-07-23 09:37:51 +02:00
James Cole
8e53a63243 Broken translation. [skip ci] 2017-07-23 09:36:50 +02:00
James Cole
4de01e7387 New translations firefly.php (Dutch) 2017-07-23 09:30:10 +02:00
James Cole
f85ab74e2b Code cleanup and tests. 2017-07-23 08:32:51 +02:00
James Cole
8bb7d5de3f Various code clean up. 2017-07-23 08:16:11 +02:00
James Cole
68424e485c New translations firefly.php (Slovenian) 2017-07-23 07:41:00 +02:00
James Cole
ac34285eed New translations firefly.php (Spanish) 2017-07-23 07:40:58 +02:00
James Cole
e605a82573 New translations firefly.php (Portuguese, Brazilian) 2017-07-23 07:40:55 +02:00
James Cole
1061764426 New translations firefly.php (Polish) 2017-07-23 07:40:52 +02:00
James Cole
8d93e410f7 New translations firefly.php (Chinese Traditional) 2017-07-23 07:40:49 +02:00
James Cole
9336cdcc5d New translations firefly.php (Dutch) 2017-07-23 07:40:47 +02:00
James Cole
7639c03646 New translations firefly.php (German) 2017-07-23 07:40:45 +02:00
James Cole
2da55bff9c New translations firefly.php (French) 2017-07-23 07:40:43 +02:00
James Cole
6ea5d45dec New translations firefly.php (Russian) 2017-07-23 07:40:29 +02:00
James Cole
e9504a3899 New translations intro.php (Polish) 2017-07-23 07:40:25 +02:00
James Cole
7bff2fd1e5 New translations intro.php (Portuguese, Brazilian) 2017-07-23 07:40:23 +02:00
James Cole
694712e933 New translations intro.php (Russian) 2017-07-23 07:40:22 +02:00
James Cole
aa675559e6 New translations intro.php (Spanish) 2017-07-23 07:40:22 +02:00
James Cole
2af19d675a New translations intro.php (Slovenian) 2017-07-23 07:40:21 +02:00
James Cole
4588d16fc2 New translations intro.php (German) 2017-07-23 07:40:20 +02:00
James Cole
5dbd3031be New translations intro.php (French) 2017-07-23 07:40:19 +02:00
James Cole
59c16eba77 New translations intro.php (Dutch) 2017-07-23 07:40:17 +02:00
James Cole
8c21ec96d1 New translations intro.php (Chinese Traditional) 2017-07-23 07:40:16 +02:00
James Cole
2c00a8353d Fix small issue reenabling intro [skip ci] 2017-07-23 07:35:30 +02:00
James Cole
19e7a76f85 Forgot small statement [skip ci] 2017-07-23 07:32:06 +02:00
James Cole
f16a186faf Option to re-enable intro boxes. 2017-07-23 07:30:05 +02:00
James Cole
456dd39ec4 New intro texts. 2017-07-22 22:56:21 +02:00
James Cole
5b69a697e4 Improve pages to show intro texts. 2017-07-22 10:50:30 +02:00
James Cole
20d279fee4 New translations firefly.php (Slovenian) 2017-07-21 06:20:54 +02:00
James Cole
cd55d819af New translations firefly.php (Spanish) 2017-07-21 06:20:52 +02:00
James Cole
dce51da92f New translations firefly.php (Portuguese, Brazilian) 2017-07-21 06:20:48 +02:00
James Cole
6118cf6041 New translations firefly.php (Polish) 2017-07-21 06:20:45 +02:00
James Cole
4ae8bffd4d New translations firefly.php (Chinese Traditional) 2017-07-21 06:20:42 +02:00
James Cole
5ebe86d5b6 New translations firefly.php (Dutch) 2017-07-21 06:20:40 +02:00
James Cole
317d97a310 New translations firefly.php (German) 2017-07-21 06:20:38 +02:00
James Cole
082aa1d8e3 New translations firefly.php (French) 2017-07-21 06:20:36 +02:00
James Cole
2f2963676f New translations firefly.php (Russian) 2017-07-21 06:20:19 +02:00
James Cole
f2d388f742 Improve code for intro texts. 2017-07-21 06:00:10 +02:00
James Cole
d79008495a Remove some html comments. 2017-07-21 04:36:23 +02:00
James Cole
43f1867fb8 Language strings for #708 2017-07-21 04:36:09 +02:00
James Cole
043c28628c Fixes #708 2017-07-21 04:35:57 +02:00
James Cole
9540854c8a Language is not complete yet. [skip ci] 2017-07-21 04:26:13 +02:00
James Cole
b951a6ca66 Merge pull request #712 from simonsmiley/update-charts
Make charts easier to read
2017-07-19 16:11:58 +02:00
Simon Hanna
26561a395d Make charts easier to read
* Do not fill the area below the lines
* Provide border color
* Decrease transparency of border
2017-07-19 15:53:22 +02:00
James Cole
0a884c8718 New translations intro.php (Polish) 2017-07-16 18:40:23 +02:00
James Cole
b393e3c662 New translations intro.php (Portuguese, Brazilian) 2017-07-16 18:40:22 +02:00
James Cole
c36062210f New translations intro.php (Russian) 2017-07-16 18:40:21 +02:00
James Cole
861381fbb6 New translations intro.php (Spanish) 2017-07-16 18:40:20 +02:00
James Cole
b9019f9590 New translations intro.php (Slovenian) 2017-07-16 18:40:19 +02:00
James Cole
fcfa877e89 New translations intro.php (German) 2017-07-16 18:40:18 +02:00
James Cole
d61849eefe New translations intro.php (French) 2017-07-16 18:40:17 +02:00
James Cole
bc83bd6ec8 New translations intro.php (Dutch) 2017-07-16 18:40:15 +02:00
James Cole
c18c94e565 New translations intro.php (Chinese Traditional) 2017-07-16 18:40:15 +02:00
James Cole
77d077ec08 Fix help button. 2017-07-16 18:28:46 +02:00
James Cole
021d0e6359 More stuff for intro texts. 2017-07-16 18:14:29 +02:00
James Cole
7f4fd2c52f Fix tests. 2017-07-16 17:05:52 +02:00
James Cole
8ffe098e3b New translations firefly.php (Slovenian) 2017-07-16 13:20:56 +02:00
James Cole
a2ca86ca74 New translations firefly.php (Spanish) 2017-07-16 13:20:54 +02:00
James Cole
deef018806 New translations firefly.php (Portuguese, Brazilian) 2017-07-16 13:20:50 +02:00
James Cole
c3f8b2ea56 New translations firefly.php (Polish) 2017-07-16 13:20:47 +02:00
James Cole
e3696b6055 New translations firefly.php (Chinese Traditional) 2017-07-16 13:20:45 +02:00
James Cole
b82b340668 New translations firefly.php (Dutch) 2017-07-16 13:20:42 +02:00
James Cole
a8b01c439b New translations firefly.php (German) 2017-07-16 13:20:39 +02:00
James Cole
6ec686ca7e New translations firefly.php (French) 2017-07-16 13:20:37 +02:00
James Cole
fdb664fecb New translations firefly.php (Russian) 2017-07-16 13:20:21 +02:00
James Cole
09f838089b Allow rule to be applied to transactions (not just group). 2017-07-16 13:04:45 +02:00
James Cole
db0fe918b8 New translations firefly.php (Dutch) 2017-07-16 08:00:07 +02:00
James Cole
8ca696d547 New translations firefly.php (Dutch) 2017-07-16 07:50:41 +02:00
James Cole
b7ccb1ce5c New translations breadcrumbs.php (Dutch) 2017-07-16 07:50:33 +02:00
James Cole
5f0033e466 New translations intro.php (Polish) 2017-07-16 07:50:22 +02:00
James Cole
477adba3e1 New translations intro.php (Portuguese, Brazilian) 2017-07-16 07:50:21 +02:00
James Cole
eb15ca8ebd New translations intro.php (Russian) 2017-07-16 07:50:20 +02:00
James Cole
dbf5907c20 New translations intro.php (Spanish) 2017-07-16 07:50:19 +02:00
James Cole
e95deb9443 New translations intro.php (Slovenian) 2017-07-16 07:50:19 +02:00
James Cole
e19c36b5c4 New translations intro.php (German) 2017-07-16 07:50:18 +02:00
James Cole
fdd589db4f New translations intro.php (French) 2017-07-16 07:50:17 +02:00
James Cole
cd4e45d8f5 New translations intro.php (Dutch) 2017-07-16 07:50:16 +02:00
James Cole
e6e31b9cab New translations intro.php (Chinese Traditional) 2017-07-16 07:50:15 +02:00
James Cole
b676b1fef9 Simplify import storage routine. 2017-07-16 07:45:20 +02:00
James Cole
58bfb35fa6 Built a new routine for intro tours. 2017-07-16 07:35:08 +02:00
James Cole
09131e8c36 Add Russian language. 2017-07-15 22:42:14 +02:00
James Cole
1d6ca91c01 This fixes the tests. 2017-07-15 22:41:57 +02:00
James Cole
fa00ba2edd Expand tests and fix various small issues in strict comparison. 2017-07-15 22:17:24 +02:00
James Cole
65114c8483 New translations firefly.php (German) 2017-07-15 22:00:52 +02:00
James Cole
d033985141 New translations firefly.php (Polish) 2017-07-15 22:00:49 +02:00
James Cole
5badfbee93 New translations firefly.php (Portuguese, Brazilian) 2017-07-15 22:00:47 +02:00
James Cole
44a61842f0 New translations firefly.php (Slovenian) 2017-07-15 22:00:45 +02:00
James Cole
70b61af572 New translations firefly.php (French) 2017-07-15 22:00:42 +02:00
James Cole
53a53d393d New translations firefly.php (Chinese Traditional) 2017-07-15 22:00:38 +02:00
James Cole
6c95b5757b New translations firefly.php (Spanish) 2017-07-15 22:00:35 +02:00
James Cole
693b3e5a6f New translations firefly.php (Russian) 2017-07-15 22:00:18 +02:00
James Cole
aac1338bdd Merge pull request #705 from firefly-iii/l10n_develop
New Crowdin translations
2017-07-15 21:58:20 +02:00
James Cole
2979137102 New translations firefly.php (Dutch) 2017-07-15 21:50:12 +02:00
James Cole
0068675f3b Remove alert [skip ci] 2017-07-15 21:48:02 +02:00
James Cole
6c1a22ea7f Merge pull request #702 from firefly-iii/l10n_develop
New Crowdin translations
2017-07-15 21:42:33 +02:00
James Cole
2869b92e6b Merge branch 'develop' into l10n_develop 2017-07-15 21:42:27 +02:00
James Cole
8a38ce1964 Remove references to old tour but include code for new tour. 2017-07-15 21:40:42 +02:00
James Cole
3a7c5566ed New translations firefly.php (German) 2017-07-15 17:30:52 +02:00
James Cole
1bed8c2b08 New translations firefly.php (Polish) 2017-07-15 17:30:49 +02:00
James Cole
e24500b306 New translations firefly.php (Portuguese, Brazilian) 2017-07-15 17:30:47 +02:00
James Cole
6251914419 New translations firefly.php (Slovenian) 2017-07-15 17:30:45 +02:00
James Cole
8d9e943d57 New translations firefly.php (French) 2017-07-15 17:30:43 +02:00
James Cole
f71bc5d323 New translations firefly.php (Dutch) 2017-07-15 17:30:40 +02:00
James Cole
a8082b2b3f New translations firefly.php (Chinese Traditional) 2017-07-15 17:30:37 +02:00
James Cole
510b72303d New translations firefly.php (Spanish) 2017-07-15 17:30:35 +02:00
James Cole
4666d4bbaa New translations breadcrumbs.php (Chinese Traditional) 2017-07-15 17:30:28 +02:00
James Cole
025f1e9502 New translations breadcrumbs.php (Dutch) 2017-07-15 17:30:27 +02:00
James Cole
b4c05098a9 New translations breadcrumbs.php (French) 2017-07-15 17:30:27 +02:00
James Cole
3c8ee6f7f7 New translations breadcrumbs.php (German) 2017-07-15 17:30:26 +02:00
James Cole
8b34135b08 New translations breadcrumbs.php (Polish) 2017-07-15 17:30:24 +02:00
James Cole
d4b7c6689f New translations breadcrumbs.php (Portuguese, Brazilian) 2017-07-15 17:30:23 +02:00
James Cole
d72aadfb00 New translations breadcrumbs.php (Slovenian) 2017-07-15 17:30:21 +02:00
James Cole
00ff8ae166 New translations breadcrumbs.php (Spanish) 2017-07-15 17:30:20 +02:00
James Cole
cffcf49b26 New translations breadcrumbs.php (Russian) 2017-07-15 17:30:14 +02:00
James Cole
ad55aa8b85 New translations firefly.php (Russian) 2017-07-15 17:30:13 +02:00
James Cole
accbdea942 Improve search and display of results. 2017-07-15 17:19:12 +02:00
James Cole
22144b78ea Strict comparisons. 2017-07-15 16:41:07 +02:00
James Cole
c03ab269f0 Memory optimised, connect journal to tag. 2017-07-15 10:43:22 +02:00
James Cole
0c6c4d5959 Fix small issue in chart. 2017-07-15 10:26:27 +02:00
James Cole
e4e1873770 Improve import routine. 2017-07-15 10:26:16 +02:00
James Cole
3b3579025d Improve results when presented with invalid data. #701 2017-07-14 17:57:20 +02:00
James Cole
5cf8f2f4f4 Add sort flag for #698 2017-07-14 17:25:03 +02:00
James Cole
c4ff05b1ba Sort fix #698 [skip ci] 2017-07-14 17:21:30 +02:00
James Cole
2bf54d0b8e Remove extra X [skip ci] 2017-07-14 17:10:56 +02:00
James Cole
2a63888546 Table is sortable, #697 2017-07-14 17:10:06 +02:00
James Cole
cb8294cbd2 Disable button form on submit. 2017-07-14 17:05:32 +02:00
James Cole
c9a99be183 Fix #699 2017-07-14 17:04:18 +02:00
James Cole
5d2e026e5a New translations firefly.php (Polish) 2017-07-14 16:10:12 +02:00
James Cole
568fdad52a New translations form.php (Polish) 2017-07-14 16:10:08 +02:00
James Cole
174fb65fed New translations firefly.php (Polish) 2017-07-14 16:00:25 +02:00
James Cole
2e77c45ae8 New translations csv.php (Polish) 2017-07-14 15:50:27 +02:00
James Cole
1b674a0abf New translations firefly.php (Polish) 2017-07-14 15:50:25 +02:00
James Cole
4beb8b1f6b New translations csv.php (Polish) 2017-07-14 15:40:20 +02:00
James Cole
3c4abb7b60 Fixed some issues in import, should improve results for #701 2017-07-14 06:41:47 +02:00
James Cole
970c73c221 Fix path 2017-07-14 06:41:10 +02:00
James Cole
6797c1255f New translations firefly.php (Russian) 2017-07-13 14:40:45 +02:00
James Cole
0ed159b553 New translations firefly.php (Russian) 2017-07-13 14:30:23 +02:00
James Cole
faa8679804 New translations firefly.php (Russian) 2017-07-13 14:20:12 +02:00
James Cole
e3d2253958 New translations firefly.php (Russian) 2017-07-13 14:10:18 +02:00
James Cole
3f7b7996c0 New translations firefly.php (Russian) 2017-07-13 14:00:16 +02:00
James Cole
9229ff54df New translations csv.php (Russian) 2017-07-13 13:50:15 +02:00
James Cole
204ab8f427 New translations demo.php (Russian) 2017-07-13 13:50:13 +02:00
James Cole
8ee6c0789d New translations csv.php (Russian) 2017-07-13 13:40:21 +02:00
James Cole
be188e7266 New translations form.php (Russian) 2017-07-13 13:40:20 +02:00
James Cole
1a43b62750 New translations help.php (Russian) 2017-07-13 13:30:11 +02:00
James Cole
eaa8321aa8 New translations help.php (Russian) 2017-07-13 13:20:19 +02:00
James Cole
f01f0071dd New translations list.php (Russian) 2017-07-13 13:20:17 +02:00
James Cole
ec69cc3590 New translations list.php (Russian) 2017-07-13 13:10:16 +02:00
James Cole
102dac1eb8 New translations validation.php (Russian) 2017-07-13 13:10:14 +02:00
James Cole
d54c5f1f99 New translations passwords.php (Russian) 2017-07-13 12:10:27 +02:00
James Cole
c2e0e00c47 New translations pagination.php (Russian) 2017-07-13 12:10:26 +02:00
James Cole
f24e387363 New translations breadcrumbs.php (Russian) 2017-07-13 12:00:27 +02:00
James Cole
09715b83e4 New translations auth.php (Russian) 2017-07-13 11:50:19 +02:00
James Cole
e4f197bbc9 New translations breadcrumbs.php (Russian) 2017-07-13 11:50:18 +02:00
James Cole
de64cef48d New translations auth.php (Russian) 2017-07-13 11:01:08 +02:00
James Cole
b85329b371 New translations firefly.php (Russian) 2017-07-13 11:01:07 +02:00
James Cole
215f9a4245 New translations csv.php (Russian) 2017-07-13 11:00:58 +02:00
James Cole
80ce7a64ba New translations config.php (Russian) 2017-07-13 11:00:56 +02:00
James Cole
65f3dc0656 New translations breadcrumbs.php (Russian) 2017-07-13 11:00:55 +02:00
James Cole
f9b6b841b6 New translations form.php (Russian) 2017-07-13 11:00:53 +02:00
James Cole
d4f2d649e8 New translations help.php (Russian) 2017-07-13 11:00:50 +02:00
James Cole
15b057fe68 New translations demo.php (Russian) 2017-07-13 11:00:48 +02:00
James Cole
9cb549f4a1 New translations validation.php (Russian) 2017-07-13 11:00:47 +02:00
James Cole
4d67578458 New translations passwords.php (Russian) 2017-07-13 11:00:44 +02:00
James Cole
71faaf1c19 New translations list.php (Russian) 2017-07-13 11:00:43 +02:00
James Cole
0187f29b4b New translations pagination.php (Russian) 2017-07-13 11:00:40 +02:00
James Cole
9b6ccdd43a Fixes #694 2017-07-10 19:52:31 +02:00
427 changed files with 13002 additions and 7138 deletions

View File

@@ -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
View 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
View File

@@ -5,3 +5,4 @@ Homestead.json
Homestead.yaml
.env
public/google*.html
report.html

View File

@@ -1,6 +1,5 @@
language: php
php:
- 7.0
- 7.1
cache:

View File

@@ -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

View File

@@ -1,6 +1,6 @@
# Firefly III: A personal finances manager
[![Requires PHP7](https://img.shields.io/badge/php-7.0-red.svg)](https://secure.php.net/downloads.php) [![Latest Stable Version](https://poser.pugx.org/grumpydictator/firefly-iii/v/stable)](https://packagist.org/packages/grumpydictator/firefly-iii) [![License](https://img.shields.io/badge/license-CC%20BY--SA%204.0-lightgrey.svg)](https://creativecommons.org/licenses/by-sa/4.0/) [![Donate](https://img.shields.io/badge/Donate-PayPal-green.svg)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=44UKUT455HUFA)
[![Requires PHP7.1](https://img.shields.io/badge/php-7.1-red.svg)](https://secure.php.net/downloads.php) [![Latest Stable Version](https://poser.pugx.org/grumpydictator/firefly-iii/v/stable)](https://packagist.org/packages/grumpydictator/firefly-iii) [![License](https://img.shields.io/badge/license-CC%20BY--SA%204.0-lightgrey.svg)](https://creativecommons.org/licenses/by-sa/4.0/) [![Donate](https://img.shields.io/badge/Donate-PayPal-green.svg)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=44UKUT455HUFA)
[![The index of Firefly III](https://i.nder.be/hurdhgyg/400)](https://i.nder.be/h2b37243) [![The account overview of Firefly III](https://i.nder.be/hnkfkdpr/400)](https://i.nder.be/hv70pbwc)

View File

@@ -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;

View 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;
}
}

View File

@@ -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:
}
}

View File

@@ -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();
}
}

View File

@@ -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
{
/**

View File

@@ -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()

View File

@@ -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,
];
/**

View File

@@ -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 . ')';
}

View File

@@ -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) {

View File

@@ -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) {

View File

@@ -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;
}

View File

@@ -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'));
}

View File

@@ -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;

View File

@@ -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';

View File

@@ -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);

View File

@@ -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.

View File

@@ -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';

View File

@@ -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) {

View File

@@ -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

View File

@@ -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);

View File

@@ -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;

View File

@@ -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;

View File

@@ -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

View File

@@ -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.

View File

@@ -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);

View File

@@ -67,7 +67,7 @@ class CategoryController extends Controller
$start = $repository->firstUseDate($category);
if ($start->year == 1900) {
if ($start->year === 1900) {
$start = new Carbon;
}

View File

@@ -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];
}
}

View File

@@ -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];
}
}

View File

@@ -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);
}
);

View File

@@ -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>';
}

View File

@@ -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 '&nbsp;';
}
/**
* @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector
*/

View 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.';
}
}
}

View 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;
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View 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;
}
}

View File

@@ -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');
}
}

View File

@@ -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;
}
}

View File

@@ -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);

View File

@@ -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',

View File

@@ -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);

View File

@@ -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'));
}
}

View File

@@ -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'));
}

View File

@@ -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);

View File

@@ -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)
{

View File

@@ -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'
)
);

View File

@@ -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);

View File

@@ -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',
];
}

View File

@@ -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',
];
}
}

View File

@@ -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',
];

View 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',
];
}
}

View File

@@ -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;
}
);

View File

@@ -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.
*

View File

@@ -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
*/

View File

@@ -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 = ',';
}

View File

@@ -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));

View File

@@ -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));
}
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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 = [

View File

@@ -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;
}
/**

View File

@@ -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;
}
/**

View File

@@ -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;
}
/**

View File

@@ -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;

View File

@@ -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;
}
}

View File

@@ -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;
}

View File

@@ -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'
}

View File

@@ -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;
}
}

View 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;
}
}

View 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();
}
}

View File

@@ -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');
}
}

View File

@@ -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;

View File

@@ -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;

View File

@@ -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);
}
}

View File

@@ -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;
}
}

View File

@@ -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);
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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 [];
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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();

View File

@@ -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);
}
}

View File

@@ -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;
}
}

View File

@@ -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']));
}

View File

@@ -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
*/

View File

@@ -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
*/

View File

@@ -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();

View File

@@ -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;
/**
*

View File

@@ -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);
}
}

View File

@@ -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();
}

View File

@@ -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