Compare commits

..

511 commits

Author SHA1 Message Date
Hillel Coren
90cb83be90 Update version 2021-06-10 10:30:28 +03:00
Hillel Coren
1d2cfb2bd7 Update version 2021-06-10 10:24:59 +03:00
David Bomba
9b00ca926a
Merge pull request #5987 from turbo124/master
Fixes for quote status on migration
2021-06-10 08:13:05 +10:00
David Bomba
c389fa7026 Merge branch 'master' of https://github.com/turbo124/invoiceninja 2021-06-08 07:31:47 +10:00
David Bomba
515756f08a Minor fixes for invoice status 2021-06-08 07:30:55 +10:00
David Bomba
50e79da171
Merge pull request #5925 from turbo124/master
Adjustments for migration
2021-06-05 15:04:57 +10:00
David Bomba
b0d6a9ce5b Adjustments for migration 2021-06-05 13:38:00 +10:00
David Bomba
480d18d5ba
Merge pull request #5740 from fpellet/patch-1
Fix issue on Safari 14
2021-06-04 17:28:32 +10:00
David Bomba
9c8d0ba3cd
Merge pull request #5816 from turbo124/master
Fixes for migration
2021-05-26 07:30:35 +10:00
David Bomba
cf62014bc5 Merge branch 'master' of https://github.com/turbo124/invoiceninja 2021-05-26 07:30:05 +10:00
David Bomba
30c302d1b6 Add timestamps to company gateways migration 2021-05-26 07:29:53 +10:00
Hillel Coren
fef994b99e Adjust check data order 2021-05-23 09:09:57 +03:00
David Bomba
32bddeca44 Add client and contacts to ninja migration 2021-05-20 14:13:07 +10:00
Florent Pellet
d61a3d89b7
Fix issue on Safari 14
Safari 14 freeze if the preview is enable

https://github.com/invoiceninja/invoiceninja/issues/4305
2021-05-18 15:30:01 +02:00
Hillel Coren
54a9e63851 Update version 2021-05-18 00:36:26 +03:00
David Bomba
94140c173a
Merge pull request #5730 from turbo124/master
Fixes for product migration
2021-05-18 07:31:34 +10:00
David Bomba
71d047dd53 Fixes for product migration 2021-05-18 07:31:09 +10:00
=
f721cdbe64 Fixes for migration resources 2021-05-16 20:04:01 +10:00
David Bomba
60e21cc1be
Merge pull request #5705 from turbo124/master
Update migration output.
2021-05-15 11:12:55 +10:00
David Bomba
b41249eda6 export ninja tokens 2021-05-12 20:46:35 +10:00
David Bomba
51e111f973 migration hosted tokens 2021-05-12 20:44:22 +10:00
David Bomba
897c134ee3
Merge pull request #5687 from turbo124/master
Fixes for migration
2021-05-12 17:28:51 +10:00
David Bomba
77700a6a0d Merge branch 'master' of https://github.com/turbo124/invoiceninja 2021-05-12 17:28:19 +10:00
David Bomba
136e8bf98b Fixes for migration designs 2021-05-12 17:28:17 +10:00
David Bomba
36c5d915f6 migrations 2021-05-11 16:12:38 +10:00
David Bomba
e63b3cb76a Return early from try block 2021-05-10 14:11:26 +10:00
David Bomba
a5b0348da3
Merge pull request #5605 from turbo124/master
Fixes for wrong designs
2021-05-04 23:35:57 +10:00
David Bomba
a272956df2 Fixes for wrong designs 2021-05-04 12:40:14 +10:00
David Bomba
d619522e2a
Merge pull request #5590 from turbo124/master
Code of conduct
2021-05-03 21:25:56 +10:00
David Bomba
22178f9476 Code of Conduct 2021-05-03 21:25:14 +10:00
David Bomba
64ac8299e4 minor fixes 2021-05-03 13:57:33 +10:00
David Bomba
ff888e8cf2 fixes for migration 2021-05-03 13:53:57 +10:00
David Bomba
b65c501f02
Merge pull request #5558 from turbo124/master
Fixes for migration
2021-04-29 17:31:15 +10:00
David Bomba
f78443e6d9 check decryption 2021-04-29 14:44:44 +10:00
David Bomba
5159288bb8 fixes for hosted migration 2021-04-29 12:48:54 +10:00
David Bomba
4e04e2af28
Merge pull request #5556 from turbo124/master
Hosted migration
2021-04-29 12:36:19 +10:00
David Bomba
d3e44b779c Fixes for hosted migration 2021-04-29 12:35:56 +10:00
David Bomba
1d49f1fe21 Fixes for lang conflict 2021-04-29 12:31:49 +10:00
David Bomba
f395434f2a Improve migration flow for hosted users 2021-04-29 12:31:09 +10:00
David Bomba
c975045c35 updated texts 2021-04-28 09:56:55 +10:00
David Bomba
ec70871181
Merge pull request #5542 from turbo124/master
Updated texts
2021-04-28 09:44:23 +10:00
David Bomba
c90eac806f Texts 2021-04-28 09:43:35 +10:00
Hillel Coren
0ef5eb24d8 Enable migration for hosted 2021-04-25 10:44:51 +03:00
Hillel Coren
b645356122 Hide migration on hosted 2021-04-25 08:43:49 +03:00
David Bomba
f3927861bc Merge branch 'master' of https://github.com/turbo124/invoiceninja 2021-04-19 13:36:06 +10:00
David Bomba
20f8c7e1e3 minor fixes for migration 2021-04-19 13:36:00 +10:00
Hillel Coren
2bdb26dd06 Update version 2021-04-18 09:39:21 +03:00
David Bomba
191b4f3f5d
Merge pull request #5459 from turbo124/master
Migration fixes.
2021-04-18 07:02:19 +10:00
David Bomba
7dc1929430 Merge branch 'master' of https://github.com/turbo124/invoiceninja 2021-04-18 07:01:20 +10:00
David Bomba
71acb1189e Fixes for recurring status 2021-04-18 07:01:11 +10:00
David Bomba
5630193dfe Fixes for credit card meta data 2021-04-16 12:05:14 +10:00
David Bomba
2f7e91b46d textsg 2021-04-14 07:29:35 +10:00
David Bomba
7fdc8e0d00
Merge pull request #5427 from turbo124/master
Update translations
2021-04-14 07:26:39 +10:00
David Bomba
3fed6b7771
Merge branch 'master' into master 2021-04-14 07:25:37 +10:00
David Bomba
dd2d10a54f Update translations 2021-04-14 07:22:57 +10:00
Hillel Coren
ba0260cff5 Update lang files 2021-04-12 20:07:07 +03:00
David Bomba
2d08738ae6
Merge pull request #5357 from turbo124/master
Migrate client task rate.
2021-04-07 07:53:10 +10:00
David Bomba
7184f0bcdc migrate client task rate 2021-04-07 07:52:49 +10:00
David Bomba
a553bb8dff migrate client task rate 2021-04-07 07:52:13 +10:00
Hillel Coren
cbac71435e Update version 2021-04-02 11:22:43 +03:00
David Bomba
98d62fa340
Merge pull request #5320 from turbo124/master
disable 2fa on migration
2021-04-02 10:18:59 +11:00
David Bomba
49ec098b11 disable 2fa on migration 2021-04-02 10:18:39 +11:00
Hillel Coren
79179b923e Update version 2021-04-01 12:10:25 +03:00
Benjamin Beganović
64e7b1d4df
Update bug_report.md 2021-04-01 11:06:31 +02:00
David Bomba
b4e1067b15
Merge pull request #5306 from turbo124/master
Remove has_tasks
2021-04-01 10:17:38 +11:00
David Bomba
d659d6dd90 remove has_tasks 2021-04-01 10:17:15 +11:00
David Bomba
fb294a7f7e
Merge pull request #5302 from turbo124/master
Add has_tasks and has_expenses to migration transformer
2021-03-31 21:31:57 +11:00
David Bomba
eaaf954244 Add has_tasks and has_expenses to migration transformer 2021-03-31 21:31:25 +11:00
David Bomba
be4f184d97
Merge pull request #5301 from turbo124/master
minor fix for datetime
2021-03-31 19:39:14 +11:00
David Bomba
10f1ac93ac minor fix for datetime 2021-03-31 19:38:57 +11:00
David Bomba
e6915f36a5
Merge pull request #5283 from turbo124/master
Minor fixes for migration
2021-03-30 20:29:42 +11:00
David Bomba
ce18449cc7 Minor fixes for migration 2021-03-30 20:29:20 +11:00
Benjamin Beganović
cd421c4558
Merge pull request #5282 from beganovich/v4-3003-migration-improvements
(v4) Migration improvements & bug fixes
2021-03-30 10:50:18 +02:00
Benjamin Beganović
25dca51b5e - Show message if migration didn't start properly
- Update completed.blade.php with errors message
- Better status checking in CompleteService.php
2021-03-30 10:49:29 +02:00
Benjamin Beganović
3d69498c6e
Merge pull request #5261 from beganovich/v4-2603-improve-authentication-process
(v4) Improve authentication workflow
2021-03-26 14:14:40 +01:00
Benjamin Beganović
2aa850439c Improve authentication workflow 2021-03-26 14:14:08 +01:00
Benjamin Beganović
4756dd58f9
Merge pull request #5217 from beganovich/v4-2203-translations-sync
Sync translations
2021-03-22 11:41:24 +01:00
Benjamin Beganović
6d4d639c65 update translations 2021-03-22 11:40:37 +01:00
David Bomba
d08b6e874f
Merge pull request #5212 from turbo124/master
Add missing defaults
2021-03-22 21:05:25 +11:00
David Bomba
65cf2752a6 Add missing defaults 2021-03-22 21:05:00 +11:00
David Bomba
d911c24421 Update texts 2021-03-20 13:47:35 +11:00
David Bomba
3afc70bfec
Update README.md 2021-03-20 09:02:13 +11:00
Hillel Coren
e3637197d7 Update version 2021-03-17 13:07:19 +02:00
David Bomba
3542d7fffb
Merge pull request #5161 from turbo124/master
Migration fixes
2021-03-17 22:05:24 +11:00
David Bomba
7fce31e82e
Merge branch 'master' into master 2021-03-17 22:05:19 +11:00
David Bomba
59a1596732 Fixes for migration 2021-03-17 20:42:17 +11:00
Hillel Coren
63eeb28bc4 Update languages 2021-03-17 08:39:34 +02:00
David Bomba
60e0154ac6 Adjust counter frequency in migration 2021-03-16 10:32:12 +11:00
David Bomba
b94e4c602e rtrim trailing slash 2021-03-16 09:04:27 +11:00
David Bomba
c608336c04 Fixes for missing translations 2021-03-16 09:01:41 +11:00
Benjamin Beganović
2e90506a5e
Update bug_report.md 2021-03-09 18:06:28 +01:00
David Bomba
94624e6719
Merge pull request #5061 from turbo124/master
Add Russian language
2021-03-09 08:07:43 +11:00
David Bomba
84774d208c Add Russian language 2021-03-09 08:06:33 +11:00
David Bomba
1cba176139
Merge pull request #5039 from turbo124/master
Add translations
2021-03-05 20:31:10 +11:00
David Bomba
6c759a8844
Merge branch 'master' into master 2021-03-05 20:31:05 +11:00
David Bomba
a631681f73 Add translations 2021-03-05 20:27:45 +11:00
Hillel Coren
e1f15277d6 Update version 2021-02-28 08:00:34 +02:00
David Bomba
7e4593cd89
Merge pull request #5004 from turbo124/master
Fixes for custom value nullables
2021-02-28 12:25:36 +11:00
David Bomba
d32ac5327c Fixes for custom value nullables 2021-02-28 12:25:15 +11:00
Hillel Coren
e72d8ecfa2 Update PHP min version to 7.1 2021-02-19 10:20:45 +02:00
Hillel Coren
9a5e429942 Update docs 2021-02-17 13:29:34 +02:00
Hillel Coren
20ecad0774 Update version 2021-02-17 12:47:47 +02:00
Hillel Coren
044220d570 Merge branch 'master' of github.com:invoiceninja/invoiceninja 2021-02-17 12:46:33 +02:00
Hillel Coren
ac13be9e01 Update lang files 2021-02-17 12:46:24 +02:00
Benjamin Beganović
66117cd456
Update texts.php 2021-02-17 11:42:17 +01:00
Hillel Coren
87eb98ca20 Update lang files 2021-02-16 18:39:53 +02:00
Hillel Coren
3b67e2a8c8 Merge branch 'master' of github.com:invoiceninja/invoiceninja 2021-02-16 18:34:12 +02:00
Hillel Coren
99237898de Update lang file 2021-02-16 18:33:57 +02:00
Benjamin Beganović
d5d917e3f8
Update texts.php 2021-02-16 13:33:16 +01:00
David Bomba
14b67a2053
Merge pull request #4903 from turbo124/master
Update texts
2021-02-15 07:54:07 +11:00
David Bomba
6546b41e26 Update texts 2021-02-15 07:52:34 +11:00
David Bomba
b31fa80b5a Update texts 2021-02-15 07:51:21 +11:00
David Bomba
e42964ffaf
Merge pull request #4900 from myles1729/patch-1
Update README.md
2021-02-14 17:18:56 +11:00
myles1729
4c2a4fd142
Update README.md
Link to v5 self-host instructions broken - I think this is the correct one
2021-02-14 13:31:44 +13:00
David Bomba
8aa44f6607 Merge branch 'master' of https://github.com/turbo124/invoiceninja 2021-02-10 09:42:24 +11:00
David Bomba
3efb8cf252 Fixes for frequenceis 2021-02-10 09:40:55 +11:00
David Bomba
aca292b737
Merge pull request #4849 from turbo124/master
Minor fixes for migration
2021-02-09 08:09:40 +11:00
David Bomba
7d268604b7 Fixes for quote status 2021-02-09 07:59:40 +11:00
David Bomba
c488602b74 Add more properties for migration 2021-02-08 08:43:23 +11:00
David Bomba
65795b1271
Merge pull request #4844 from turbo124/master
add start migration texts
2021-02-08 08:25:48 +11:00
David Bomba
5baef3f69a add start migration texts 2021-02-04 11:08:56 +11:00
Hillel Coren
0ff600fde7 Update languages 2021-02-03 23:30:59 +02:00
Hillel Coren
67b2758d3f Update version 2021-02-03 23:26:51 +02:00
David Bomba
4e67b8b4cd
Merge pull request #4829 from turbo124/master
Fixes for custom values in  migration
2021-02-04 07:16:29 +11:00
David Bomba
5a1cdf8dfa Fixes for custom values in migration 2021-02-04 07:16:09 +11:00
David Bomba
5eb3f89650
Merge pull request #4826 from turbo124/master
Fixes for migrations
2021-02-03 23:30:56 +11:00
David Bomba
8770f0d289 fixes for migrations 2021-02-03 23:30:05 +11:00
David Bomba
905da88348
Merge pull request #4821 from turbo124/master
Additional logging when generating migration data inside V4.
2021-02-03 09:27:05 +11:00
David Bomba
e5716ae6c0 Fixes for migrations where the gateway is no longer supported in V5 2021-02-03 07:28:24 +11:00
David Bomba
df32d61552
Merge pull request #4811 from turbo124/master
Fixes for lang file
2021-02-01 20:43:53 +11:00
David Bomba
2abc7f570b Fixes for lang file 2021-02-01 20:43:16 +11:00
=
4bc41fbf8e Fixes for lang file 2021-02-01 18:37:18 +11:00
David Bomba
f594eea8e8
Merge pull request #4806 from turbo124/master
Update  lang files
2021-02-01 09:05:06 +11:00
David Bomba
239c3e5d8d
Merge branch 'master' into master 2021-02-01 09:04:57 +11:00
David Bomba
350de90af0 Merge branch 'master' of https://github.com/turbo124/invoiceninja 2021-02-01 09:02:44 +11:00
David Bomba
b99c40714f Update lang files 2021-02-01 09:02:33 +11:00
David Bomba
9197973eb4
Merge pull request #4804 from Akjo4712/patch-2
Update feature_request.md
2021-01-31 21:54:17 +11:00
Akjo4712
79edec14c8
Update feature_request.md 2021-01-31 11:21:47 +01:00
Hillel Coren
9962cc731b Merge branch 'master' of github.com:invoiceninja/invoiceninja 2021-01-31 08:41:49 +02:00
Hillel Coren
a3abff8766 Update lang file 2021-01-31 08:41:36 +02:00
David Bomba
825eb85d1c
Merge pull request #4801 from turbo124/master
Minor fix for fees and limits
2021-01-30 11:12:51 +11:00
Benjamin Beganović
e50766f471
Merge pull request #4789 from Akjo4712/patch-1
Fix Bold Text in Issue Template
2021-01-28 14:31:03 +01:00
Akjo4712
738bd0427e
Fix Bold Text in Issue Template 2021-01-28 14:29:41 +01:00
David Bomba
71146c9c94 Merge branch 'master' of https://github.com/turbo124/invoiceninja 2021-01-28 16:07:09 +11:00
David Bomba
d77e7622f7 Minor fixes 2021-01-28 16:06:57 +11:00
David Bomba
3afeed6868
Merge pull request #4762 from turbo124/master
Refactor client and vendor numbers
2021-01-26 16:20:33 +11:00
David Bomba
57185f1917 Refactor client and vendor numbers 2021-01-26 16:20:04 +11:00
David Bomba
360f47754b
Merge pull request #4760 from turbo124/master
Fixes for client / vendor numbering
2021-01-25 23:29:27 +11:00
David Bomba
cde03d132b Fixes for client / vendor numbering 2021-01-25 21:32:29 +11:00
=
fc4cbf335a Typo 2021-01-23 22:15:08 +11:00
David Bomba
623dd69182
Merge pull request #4749 from turbo124/master
Fixes for migration
2021-01-23 20:19:24 +11:00
David Bomba
248f95b295 Fixes for migration 2021-01-23 20:18:29 +11:00
David Bomba
a941cb387e
Merge pull request #4748 from turbo124/master
fixes for travis
2021-01-23 19:56:32 +11:00
David Bomba
b245147844 fixes for travos 2021-01-23 19:52:48 +11:00
David Bomba
53e3b9a940
Merge pull request #4746 from turbo124/master
Update Framework Dependencies
2021-01-22 21:54:25 +11:00
David Bomba
ee92e15668 Update framework dependencies 2021-01-22 21:51:59 +11:00
David Bomba
fa5281f5e5 Security fixes + remove Cybersource gateway 2021-01-22 21:18:05 +11:00
David Bomba
15020e201b Fixes for composer.lock and .json 2021-01-22 15:20:35 +11:00
David Bomba
5c48a83705 Security updates 2021-01-22 08:43:12 +11:00
Hillel Coren
3cc3548f69 Correct typo 2021-01-21 07:59:30 +02:00
Hillel Coren
f7d1d95e3a Update lang file 2021-01-20 20:49:30 +02:00
Hillel Coren
8b7b4b138e Update version 2021-01-20 10:54:49 +02:00
David Bomba
d994994ac5
Merge pull request #4731 from turbo124/master
Fixes for contact imports
2021-01-20 18:42:50 +11:00
David Bomba
309cb5f1f7 Fixes for contact imports 2021-01-20 09:35:19 +11:00
Hillel Coren
692594fe64 Update version 2021-01-18 09:16:22 +02:00
David Bomba
68295d6bcc
Merge pull request #4696 from turbo124/master
Fixes for contacts migration
2021-01-15 22:37:06 +11:00
David Bomba
d84af6be3a Fixes for contacts migration 2021-01-15 22:34:04 +11:00
Hillel Coren
39adbf3367 Update version 2021-01-14 09:05:09 +02:00
David Bomba
251043137d
Merge pull request #4676 from turbo124/master
Remove logo from company migration
2021-01-13 20:14:58 +11:00
David Bomba
2f9749ce03 remove logo from company migration 2021-01-13 20:13:48 +11:00
Hillel Coren
fdc9d6b4ff Update version 2021-01-10 12:33:51 +02:00
David Bomba
bcab38b364
Merge pull request #4662 from turbo124/master
Document migration:
2021-01-10 21:20:00 +11:00
David Bomba
c9bf7131b5 Document migration: 2021-01-10 21:16:40 +11:00
Hillel Coren
dbbd84b222 Update lang files 2021-01-06 08:36:44 +02:00
Hillel Coren
4234e50408 Update version 2021-01-06 08:32:09 +02:00
David Bomba
67a7778ce5
Merge pull request #4635 from turbo124/master
change properties for task and task_status in migration
2021-01-06 10:37:36 +11:00
David Bomba
b7eb94548f change properties for task and task_status in migration 2021-01-06 10:36:37 +11:00
David Bomba
ab2063ab2f Update issue templates 2021-01-05 13:27:51 +11:00
=
20de4ede89 Merge branch 'master' of https://github.com/invoiceninja/invoiceninja 2021-01-01 20:10:55 +11:00
=
f4714e6603 fixes for payment type 2021-01-01 20:10:40 +11:00
Hillel Coren
f24c270139 Update version 2021-01-01 08:23:50 +02:00
David Bomba
18cde4bc5f
Merge pull request #4589 from beganovich/v4-toggle-parent-when-force
(v4) Toggle parent company when clicking on force checkbox (migration)
2020-12-30 10:20:57 +11:00
=
95989143f9 Fixes for import 2020-12-30 08:59:01 +11:00
=
02ef83213a Merge branch 'master' of https://github.com/invoiceninja/invoiceninja 2020-12-30 08:32:33 +11:00
=
12c885a377 fixes for recurring import 2020-12-30 08:32:19 +11:00
Benjamin Beganović
c469ef6671 force toggle parent company 2020-12-29 12:37:40 +01:00
Hillel Coren
b26399713d Update version 2020-12-29 08:07:24 +02:00
=
14f8150546 Migrate invitations 2020-12-29 10:48:13 +11:00
=
b4b28385e5 Fixes for company gateways 2020-12-28 17:04:44 +11:00
=
e83eb6d657 Improve migration for gateways and fees and limits 2020-12-28 10:14:58 +11:00
David Bomba
0a793d898a Update issue templates 2020-12-26 08:35:12 +11:00
=
de40281e22 Fixes for migrating online payment gateways 2020-12-25 14:15:43 +11:00
Hillel Coren
00718d15df Update version 2020-12-17 08:12:55 +02:00
David Bomba
38ee8352d8
Merge pull request #4494 from turbo124/master
Add account variables into migration
2020-12-15 08:53:05 +11:00
David Bomba
def4d1aa9f Add account variables into migration 2020-12-15 08:52:29 +11:00
Benjamin Beganović
d35b5f66ad
Merge pull request #4466 from beganovich/v4-support-for-api-key
(v4) Add support for X-Api-Token in migration
2020-12-14 10:12:34 +01:00
Benjamin Beganović
40bb6ad9ab
Merge branch 'master' into v4-support-for-api-key 2020-12-14 10:12:01 +01:00
Hillel Coren
39a58f909e Update lang files 2020-12-10 21:33:43 +02:00
Benjamin Beganović
aa9af574b6 Add support for X-Api-Token: 2020-12-10 15:04:59 +01:00
Hillel Coren
b541e56071 Update version 2020-11-28 20:30:49 +02:00
David Bomba
c1ec590782
Merge pull request #4367 from mlq/fix/migration-ksort
Call ksort correctly for migration files
2020-11-27 06:56:02 +11:00
Moritz Lipp
f3dcd2924c Call ksort correctly for migration files 2020-11-26 15:53:45 +01:00
Hillel Coren
61d3737816 Update version 2020-11-25 12:31:45 +02:00
David Bomba
40005f3f25
Merge pull request #4359 from turbo124/master
Fixes for migrations
2020-11-25 20:07:08 +11:00
David Bomba
7968b2bb5f Fixes for migrations 2020-11-25 20:06:37 +11:00
David Bomba
ae52ea5971 Random migration datasets 2020-11-25 11:23:51 +11:00
David Bomba
fdaf513267 sort alphabetically 2020-11-24 14:20:44 +11:00
David Bomba
715998e67a Reorder migrations 2020-11-23 23:55:17 +11:00
David Bomba
033bb5c957
Merge pull request #4353 from turbo124/master
Fixes for docs
2020-11-23 22:16:24 +11:00
David Bomba
f9df879a3e Fixes for docs 2020-11-23 22:15:59 +11:00
David Bomba
1a1865045d
Merge pull request #4352 from turbo124/master
Fixes for docs
2020-11-23 22:15:14 +11:00
David Bomba
6188b4d9cb Fixes for docs 2020-11-23 22:14:56 +11:00
David Bomba
fc35c00603
Merge pull request #4351 from turbo124/master
Fixes for docs
2020-11-23 22:14:13 +11:00
David Bomba
7aad5ae124 Fixes for docs 2020-11-23 22:13:53 +11:00
David Bomba
6f15447c5f
Merge pull request #4350 from turbo124/master
Fixes for docs
2020-11-23 22:12:13 +11:00
David Bomba
9017ff4344 Fixes for docs 2020-11-23 22:11:55 +11:00
David Bomba
86993ba456
Merge pull request #4349 from turbo124/master
Improve docs for v4 / v5
2020-11-23 22:09:30 +11:00
David Bomba
9ccfc96055 Fixes for documentation 2020-11-23 22:04:58 +11:00
David Bomba
53e40a4e6a Fixes for purging existing company 2020-11-23 13:07:32 +11:00
David Bomba
72d8c567bd
Merge pull request #4348 from turbo124/master
Migration fixes
2020-11-23 09:52:36 +11:00
David Bomba
587c3fd2e9 remove carbon array from migration resources and transform to date string 2020-11-23 08:25:49 +11:00
David Bomba
52d15bc388 Fixes for carbon types 2020-11-22 16:42:12 +11:00
Benjamin Beganović
ec6b3a00cc
Merge pull request #4331 from beganovich/v4-fix-issue-with-getting-companies-for-migration
(v4) Fix issue with getting companies for migration
2020-11-19 12:32:21 +01:00
Benjamin Beganović
716b818a41 Fix issue with listing companies in v4
- Endpoint typo fix
2020-11-19 12:29:49 +01:00
David Bomba
eb6cddeb08 minor fixes for export transformers 2020-11-17 07:32:36 +11:00
David Bomba
12c928c5a0 Merge branch 'master' of https://github.com/turbo124/invoiceninja 2020-11-17 07:31:24 +11:00
David Bomba
a94808cf65 minor fixes for migration 2020-11-16 21:32:58 +11:00
David Bomba
1254ba0ee0
Merge pull request #4289 from beganovich/v4-migration-fixes
(v4) (wip) (no-merge) Migration fixes & improvements
2020-11-14 17:03:07 +11:00
David Bomba
8351d5a9fb
Merge branch 'master' into v4-migration-fixes 2020-11-14 17:02:58 +11:00
Hillel Coren
a42597098f Update lang file 2020-11-12 20:26:17 +02:00
Benjamin Beganović
666a9398d3 Fix the way we process migration 2020-11-12 11:04:50 +01:00
Benjamin Beganović
4019ff08d6 Final touch on CompleteService 2020-11-11 18:26:33 +01:00
David Bomba
31d8ecc2d0
Merge pull request #4288 from beganovich/v4-fix-for-migration-start
(v4) Fix absolute route /migration/start
2020-11-11 04:25:46 +11:00
Benjamin Beganović
d24d2e4b93 Support multiple migrations at once 2020-11-10 16:20:02 +01:00
Benjamin Beganović
c4b147d7c8 Fix absolute route /migration/start 2020-11-10 14:29:38 +01:00
=
cdc054a10f Fixes for migration 2020-11-07 21:13:21 +11:00
=
d2e9d3d8b4 Remove is_deleted from v4 side of task_statuses 2020-11-07 18:57:48 +11:00
Hillel Coren
cadd1a3b44 Update version 2020-11-05 10:51:09 +02:00
David Bomba
a86c5006af Fixes for company gateway schema 2020-11-04 11:36:30 +11:00
David Bomba
146df11c1f Refactor company logo URL 2020-11-04 11:21:28 +11:00
Hillel Coren
3190e5ac4d Update language files 2020-11-01 17:49:26 +02:00
Hillel Coren
33c1c7e646 Update version 2020-11-01 17:44:27 +02:00
Hillel Coren
08efcc0342 Minor fixes 2020-11-01 17:34:29 +02:00
Hillel Coren
d8a0462e1b Merge branch 'master' of github.com:invoiceninja/invoiceninja 2020-11-01 16:03:18 +02:00
Hillel Coren
3ab4d62bdf Remove Skrill to fix #4199 2020-11-01 16:02:56 +02:00
David Bomba
3e0b7a0659
Merge pull request #4245 from turbo124/master
Fix for tasks deleted_at
2020-11-01 21:06:16 +11:00
David Bomba
5561b9dc05 Fix for tasks deleted_at 2020-11-01 21:05:59 +11:00
David Bomba
69beb3b05d
Merge pull request #4244 from turbo124/master
Fix for task deleted_at
2020-11-01 21:02:18 +11:00
David Bomba
71d7b3217a Fix for task deleted_at 2020-11-01 21:01:51 +11:00
Hillel Coren
6c54a7fa2b Update lang file 2020-11-01 11:58:11 +02:00
David Bomba
0749bc570c
Merge pull request #4243 from turbo124/master
Fixes for migration
2020-11-01 20:46:24 +11:00
David Bomba
f39efb939b Fixes for migration 2020-11-01 20:45:51 +11:00
David Bomba
964be94407
Merge pull request #4241 from turbo124/master
Fix for expiry failure
2020-11-01 19:58:09 +11:00
David Bomba
303bfce10f Fix for expiry failure 2020-11-01 19:57:44 +11:00
David Bomba
8e223c83a5
Merge pull request #4238 from turbo124/master
Migration work for V4 -> V5
2020-10-31 16:36:38 +11:00
David Bomba
c19cebef80 Fixes for migration 2020-10-31 11:45:49 +11:00
David Bomba
fac2c80fe7 migration for vendors 2020-10-31 08:12:45 +11:00
David Bomba
b6e6518492 Fixes for type status 2020-10-30 23:15:45 +11:00
David Bomba
d5ea6fec60 Fixes for query 2020-10-30 23:01:18 +11:00
David Bomba
0bdda6a529 Migration for projects 2020-10-30 22:55:14 +11:00
David Bomba
38c017fdf8 Migration for projects 2020-10-30 22:53:50 +11:00
David Bomba
aff75e1d59 Tasks, Expenses migration 2020-10-30 19:52:45 +11:00
David Bomba
0d1c279f64 Fixes for payment meta data types 2020-10-28 16:26:08 +11:00
Hillel Coren
0962e0e301 Update lang file 2020-10-15 22:31:37 +03:00
David Bomba
5d6b86b6c2 Fix for type casting language id 2020-10-14 07:46:55 +11:00
David Bomba
f6315cb640 Disable custom designs in migration to V5 2020-10-07 10:09:34 +11:00
David Bomba
5ecd4da153 minor fix for recurring numbers 2020-10-06 11:44:22 +11:00
David Bomba
d79c936875
Merge pull request #4130 from beganovich/fix-migration-urls
(v4) Fixed migration urls
2020-10-05 19:46:57 +11:00
Benjamin Beganović
ac49832ffd Cross account migration message update 2020-10-05 10:29:11 +02:00
Benjamin Beganović
0621c215e7 Fix migration urls 2020-10-05 09:50:32 +02:00
David Bomba
2384e3d3e8 Recurring migration 2020-10-05 08:56:02 +11:00
David Bomba
9afd6c6fc2 recurring migration 2020-10-02 16:34:12 +10:00
David Bomba
4c87d6864a Recurring migrations 2020-10-02 08:19:29 +10:00
David Bomba
42ada7e3f8 Recurring migration 2020-10-02 07:38:42 +10:00
David Bomba
1e10bc3613 Working on recurring migration 2020-09-29 09:25:45 +10:00
David Bomba
f34df17d36
Merge pull request #4114 from turbo124/master
Fixes for migration V4 -> V5
2020-09-29 07:12:16 +10:00
David Bomba
a3029c4c74 Fixes for invoice item types 2020-09-29 07:11:23 +10:00
Hillel Coren
bac27c8609 Update lang file 2020-08-30 07:03:47 +03:00
David Bomba
e0cda24c09 Add referral_code to company transformer 2020-08-28 15:50:10 +10:00
Hillel Coren
49242a1469 Clarify v5 status 2020-08-27 15:28:54 +03:00
David Bomba
266abc8dd1
Merge pull request #3966 from bugzbrown/patch-1
Update Kanban column to allow vertical scroll
2020-08-05 23:04:34 +10:00
Gregory Brown
2e1a49a0cf
Update style on Kanban column to allow vert scroll
Update the Kanban Column to allow for vertical scroll with the screen. This makes it easier for your to scroll horizontally when you have a very big list.
It's a quick workaround that fixes user experience for users with small screens and big lists.
2020-08-05 09:22:19 -03:00
David Bomba
7cbda60f46
Merge pull request #3961 from turbo124/master
Improve handling for fees and limits and stale contacts
2020-08-05 10:14:16 +10:00
David Bomba
fe563fd423 Add additional fields for migration to V5 2020-08-05 10:13:05 +10:00
David Bomba
ab652c6ad0 Fixes for fees and limits - migration 2020-08-05 09:19:24 +10:00
David Bomba
4d4b318674
Merge pull request #3917 from woodspire/fix/missing-subscription-events
Add subscription events.
2020-07-21 15:05:26 +10:00
Felix Labrecque
7f3ebffb36 Add subscription events. 2020-07-20 17:37:58 -04:00
David Bomba
1f81f03e45
Merge pull request #3864 from turbo124/master
Remove invites from transformer
2020-06-30 13:19:19 +10:00
David Bomba
608734b652 Remove invites from transformer 2020-06-30 13:18:55 +10:00
David Bomba
d7d40172cf
Merge pull request #3863 from turbo124/master
Fixes for migrations including implementing migrations for payment terms
2020-06-30 10:46:07 +10:00
David Bomba
406445dbb3 Catch null floats 2020-06-30 10:44:50 +10:00
David Bomba
8ee1240cf7 Merge branch 'master' of https://github.com/invoiceninja/invoiceninja 2020-06-30 09:06:32 +10:00
David Bomba
d7f5657dda Minor fix for null client balancE 2020-06-30 09:02:18 +10:00
Hillel Coren
40cca935dd Update languages 2020-06-28 14:23:18 +03:00
Hillel Coren
48fb1d0bff Merge branch 'master' of github.com:invoiceninja/invoiceninja 2020-06-28 14:09:42 +03:00
Hillel Coren
b35c20e511 Update version 2020-06-28 14:09:32 +03:00
David Bomba
fd443cc4c2
Merge pull request #3845 from beganovich/v1-2606-fix-migration-routing
Fix absolute redirecting with url() helper
2020-06-27 07:47:24 +10:00
Benjamin Beganović
4b3d3d4460 Fix absolute redirecting with url() helper 2020-06-26 12:38:45 +02:00
Hillel Coren
bac1ed5e0c Update lang file 2020-06-25 18:50:32 +03:00
Hillel Coren
286a1a608f Merge branch 'master' of github.com:invoiceninja/invoiceninja 2020-06-25 18:45:23 +03:00
Hillel Coren
f45f1895a8 Update lang files 2020-06-25 18:40:16 +03:00
Hillel Coren
bee9547a87
Merge pull request #3803 from Sibin387/patch-1
handle id==0 and cancellation of confirm dialog box
2020-06-17 08:17:39 +03:00
Sibin
382593a8a2
handle id==0 and cancellation of confirm dialog box
handle id==0 and cancellation of confirm dialog box
2020-06-15 23:56:08 +05:30
David Bomba
1f1acaa500 Fixes for oauth images 2020-06-01 07:42:36 +10:00
David Bomba
743dca7335
Fixes for migrations including implementing migrations for payment terms (#3761) 2020-05-28 08:50:33 +10:00
David Bomba
5a8148c09f Fixes for migrations including implementing migrations for payment terms 2020-05-27 21:27:46 +10:00
Sebastian Gumprich
5fdb21cbf9
add yunohost to install options (#3728) 2020-05-23 08:28:36 +10:00
Benjamin Beganović
59ad5724d5
Change (#3731) 2020-05-22 07:23:48 +10:00
David Bomba
ab620c00fc
Update README.md 2020-05-20 09:02:19 +10:00
Leon Aves
edd57e1ca5
Import correct Document class into HasLogo trait. (#3711)
If you aren't using the local driver for logo storage, the attempts to use Document::getDirectFileUrl in this file will fail as it tries to autoload from Traits, Document needs to be explicitly imported.
2020-05-19 09:52:28 +10:00
Hillel Coren
485b46f235
Update README.md 2020-05-18 16:23:06 +03:00
Hillel Coren
674e5e253f
Merge pull request #3713 from codedge/stackoverflow
Add StackOverflow, fix broken links
2020-05-18 16:22:28 +03:00
Holger Lösken
d4724b417c Add StackOverflow, fix broken links 2020-05-18 15:01:00 +02:00
David Bomba
eaa261dad8 Minor fixes for migration 2020-05-05 10:31:58 +10:00
Hillel Coren
db0c5fc578
Merge pull request #3670 from dominikkaminski/patch-1
Fix wrong tax calculation
2020-05-03 00:40:36 +03:00
Dominik "kuberninski" Kaminski
e16900ef11
Fix wrong tax calculation
Tax calculation is wrong in pdf when 'inclusive_taxes' is enabled. This fixes #3668
2020-05-02 17:31:19 +02:00
Benjamin Beganović
56a9bc2bdd
[v1] Include validation erorrs for migration (#3660)
* Add

* Fix
2020-04-29 22:33:55 +10:00
Hillel Coren
33f70de451
Update README.md 2020-04-14 17:18:02 +03:00
Benjamin Beganović
d36cb2fafa
Refactor steps controller (#3627) 2020-04-14 08:20:38 +10:00
Hillel Coren
eea9015cd3
Merge pull request #3606 from DanAtIntegrateIT/master
Update GoCardlessV2RedirectPaymentDriver.php
2020-04-08 17:57:09 +03:00
Dan Holliday
aac4d8308d Update GoCardlessV2RedirectPaymentDriver.php 2020-04-08 15:50:27 +01:00
Benjamin Beganović
0b8b4a7505
Export company_user with users (#3597) 2020-04-08 06:43:34 +10:00
Benjamin Beganović
602a15f648
Export 'company_key' with migrations (#3600)
- Removed 'Hosted' option for migrating
- Passing companyKey property when sending request
2020-04-08 06:43:20 +10:00
Benjamin Beganović
0c7620a381
Catching possible account errors (#3596)
Co-authored-by: David Bomba <turbo124@gmail.com>
2020-04-07 07:23:57 +10:00
Benjamin Beganović
4323968240
Prevent cross site migration (#3594)
- php-cs-fixer applied
- Added new 'cross_migration_message'
- errors.blade.php - unescape characters
2020-04-07 07:21:46 +10:00
Hillel Coren
3239a1c041 Merge branch 'master' of github.com:invoiceninja/invoiceninja 2020-04-02 08:50:04 +03:00
Hillel Coren
7ee7146d00 Update lang files 2020-04-02 08:49:24 +03:00
David Bomba
f11503567e
Fixes for migrations (#3578) 2020-04-02 09:01:24 +11:00
Hillel Coren
5c61c1c0f4 Update version 2020-03-31 19:57:20 +03:00
Hillel Coren
30b5b33d0d Update languages 2020-03-31 13:00:02 +03:00
Hillel Coren
38875f719a Update GrapesJS 2020-03-29 10:07:30 +03:00
Hillel Coren
bb3603acd3 Rebuild JS files 2020-03-29 09:39:57 +03:00
Hillel Coren
ca6e981f29 Fix client redirect 2020-03-29 09:28:30 +03:00
Hillel Coren
5a33c42278 Fix typo 2020-03-29 09:27:13 +03:00
Hillel Coren
fceb82ea51 Clarify credit error 2020-03-29 09:25:52 +03:00
Hillel Coren
43f38d9d84 Update readme 2020-03-29 07:43:45 +03:00
Hillel Coren
6bb4bc3e69 White label invoice fix 2020-03-29 07:32:27 +03:00
Hillel Coren
927cd5a9a6 Fix lang file 2020-03-29 07:30:42 +03:00
Hillel Coren
be00050615 Clarify Stripe help 2020-03-29 07:24:39 +03:00
Hillel Coren
3308df7fff Clarify help text 2020-03-29 07:16:07 +03:00
Hillel Coren
f157b76b5f Added ETB currency 2020-03-29 06:47:13 +03:00
Hillel Coren
70efb7a7f2 Clarify help text 2020-03-29 06:31:40 +03:00
Hillel Coren
fc26e77c95 Fix typo in texts 2020-03-15 10:11:39 +02:00
Hillel Coren
c3a0c2a465 UI tweaks and hide on hosted 2020-03-12 09:42:31 +02:00
David Bomba
f6fced6b82
Minor fixes for migration (#3484) 2020-03-12 17:25:51 +11:00
Benjamin Beganović
b4f83a5755
Export 'invitations' with invoices (#3479)
* Export 'invitations' with invoices

* Export 'invitations' with quotes

* Send resource as 3rd parameter
2020-03-12 07:53:06 +11:00
Benjamin Beganović
9d5c3bd951
[v1] Add check for 0 value (#3473)
* Add check for 0 value

* David said, this one is better
2020-03-11 10:06:57 +11:00
Benjamin Beganović
8bb70c7e9a
[V1] Update payment fields for export (#3469)
* Update export payment fields

* Add currency_id, export
2020-03-11 08:12:08 +11:00
Benjamin Beganović
3f25b62ab6
[V1] Update 'migrations:export' message (#3470)
* Update command message

* Export migrations command improvements
2020-03-11 08:11:31 +11:00
Benjamin Beganović
d9a3b5453a
Make sure user is allowed to migrate (#3468) 2020-03-11 08:08:10 +11:00
Hillel Coren
be96bef3f0
Merge pull request #3448 from maikelreyes/master
Fix aging fields for statements
2020-03-07 22:09:41 +02:00
Maikel Reyes
89a243f88c Fin aging fields for statements 2020-03-07 00:14:31 -05:00
David Bomba
9070dfe55b
Export migrations to folder (#3429)
* Export migrations to folder

* Remove local property
2020-03-06 07:30:48 +11:00
David Bomba
67044f9dd3
Migration improvements: (#3422)
- Added option to force the migration
- Cleaned imports
2020-03-05 07:30:10 +11:00
Benjamin Beganović
bde276ad67
(V1) Improve migration (#3415)
* Imports improvements:
- Fix sorting
- Fix duplicate imports (Cannot use App\Http\Controllers\BaseController as BaseController because the name is already in use)

* Wrap up, sending migration file:
- Removed 'enable_invoice_quantity'
2020-03-04 09:44:30 +11:00
David Bomba
65b30189b3
Bump symfony/http-foundation from 3.4.20 to 3.4.36 (#3404)
Bumps [symfony/http-foundation](https://github.com/symfony/http-foundation) from 3.4.20 to 3.4.36.
- [Release notes](https://github.com/symfony/http-foundation/releases)
- [Changelog](https://github.com/symfony/http-foundation/blob/master/CHANGELOG.md)
- [Commits](https://github.com/symfony/http-foundation/compare/v3.4.20...v3.4.36)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2020-03-01 21:14:43 +11:00
David Bomba
78f1df21ae
Bump symfony/dependency-injection from 3.4.6 to 3.4.35 (#3403)
Bumps [symfony/dependency-injection](https://github.com/symfony/dependency-injection) from 3.4.6 to 3.4.35.
- [Release notes](https://github.com/symfony/dependency-injection/releases)
- [Changelog](https://github.com/symfony/dependency-injection/blob/master/CHANGELOG.md)
- [Commits](https://github.com/symfony/dependency-injection/compare/v3.4.6...v3.4.35)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2020-03-01 21:14:17 +11:00
Hillel Coren
17cddb902c Merge branch 'master' of github.com:invoiceninja/invoiceninja 2020-03-01 09:57:06 +02:00
Hillel Coren
bf7142ce43 Update lang file 2020-03-01 09:56:56 +02:00
Benjamin Beganović
ff455c8ed9
Exporting migration data via HTTP (v1) (#3364)
* Migration: Option to select the migration type

* Migration: Logic for redirecting based on steps

* Work for migrations:
- Added authentication view, service
- Account connecting
- Scaffold services
- Companies service
- (wip) Sending data to v2

* Migration: Sending migration file

* Wrap up the migration first stage

* Split company per request / no bundle

Co-authored-by: David Bomba <turbo124@gmail.com>
2020-02-25 08:17:16 +11:00
David Bomba
8020204c8d
Migrate Company Gateways and Client Tokens (#3369)
* Working on migrations for Company Gateways and Client Tokens

* Working on exporting company gateways and documents
2020-02-25 08:16:19 +11:00
Hillel Coren
a500bfb583 Update lang file 2020-02-23 12:38:52 +02:00
Hillel Coren
6365187a1c Merge mobile lang script 2020-02-23 12:36:40 +02:00
Hillel Coren
9db94c9354 Merge mobile lang script 2020-02-23 12:34:36 +02:00
Hillel Coren
c4d2c31e06 Fix for product report 2020-02-23 12:15:33 +02:00
Hillel Coren
1a554ea1e3 Fix for calculate payouts 2020-02-23 12:13:39 +02:00
Hillel Coren
fa5cb690ba Merge branch 'master' of github.com:invoiceninja/invoiceninja 2020-02-23 12:01:50 +02:00
Hillel Coren
cf873bd197 Fix for calculate payouts 2020-02-23 12:01:37 +02:00
Benjamin Beganović
9085a27361
Export 'documents' (#3343)
* Export 'documents'

* Export 'invoice_id' & 'expense_id' with documents

* Code cleanup
2020-02-19 07:53:38 +11:00
David Bomba
2a8d2c7f5e additional fixes for Checkout 2020-02-17 06:52:00 +11:00
David Bomba
ac11681073
fixes for checkout.com (#3336) 2020-02-16 17:41:00 +11:00
WillieWookiee
1ab5bc1f6b
Fixing cancel button. (#3309)
This fix addresses a bug where if you create a quote and then go into the client portal to add a credit card, the cancel button will take you back to the first quote of the client regardless if it is a draft or not. This will now take you back to the previous page.
2020-02-12 07:53:06 +11:00
Benjamin Beganović
6defaebf34
Export settings with company array (#3264) (#3268) 2020-01-30 14:34:58 +11:00
Hillel Coren
ae8457c588 Fix for postmark logs 2020-01-28 19:53:59 +02:00
Benjamin Beganović
e31ab11364 Export contacts with client array (#3251) 2020-01-28 07:54:54 +11:00
David Bomba
24daf955d8
Fix taxes (#3246) 2020-01-25 14:19:22 +11:00
David Bomba
c2dfc3055c Remove last_login from migration array of clients 2020-01-24 13:17:51 +11:00
Benjamin Beganović
b67b97430c Export data for the migration (master) (#3239)
* Scaffold migration controller & steps

* - Basic controller scaffold
- Intro step
- Downloading data

* Import step for the v2

* Export file using account_key property

* Fix button displaying

* Client export mappings

* Company export mappings

* Add withTrashed() for clients

* Export users mappings

* Export invoices mappings

* Export company properties as array

* add withTrashed() for invoices

* Downloading the json file

* Fix 'account_id' for company export
- Code cleanup & leftovers

* Creating zip for logo & storing json as file (wip)

* Zipping & sending migration files (wip)

* Exporting & mappings for quotes

* Re-enable headers for downloading

* Mappings for tax rates

* Mappings for the products

* Fix formattings & php doc messages

* Bring back headers for downloading

* Export 'company_id', 'user_id' with clients

* Export 'id' with users resource

* Remove unused parameter

* Export mappings for payments

* Export 'line_items' with invoices

* Export credits & export client id

* Export 'credits' within main JSON

* Mappings for line_items

* Fix 'is_amount_discount' for invoice export

* Exporting migration file as .zip archive

* Improve settings card for migration

* Removed duplicated keys in texts.php for en

* Fix formatting in en/texts.php

* Integrate the migration part with rest of UI

* Export company as object, not array

* Clean up

* Fixes for V1

* Apply formatting

* Remove comments
- Added 'ext-zip' in composer.json
- Fixed $credits transform

* Remove unused params

Co-authored-by: David Bomba <turbo124@gmail.com>
2020-01-24 07:34:30 +11:00
trevDev()
0dfaf043bf allow scrolling in time-tracker task form (#3211) 2020-01-15 21:24:28 +10:00
Hillel Coren
6b4d8fba4a Update check data 2019-12-22 10:16:22 +02:00
Hillel Coren
e702f24937 Merge branch 'master' of github.com:invoiceninja/invoiceninja 2019-12-22 08:54:22 +02:00
Hillel Coren
d9d949666f Update prices 2019-12-22 08:54:13 +02:00
David Bomba
755c1f75d1
Fix for Istanbul timezone (#3164) 2019-12-19 21:51:23 +11:00
Hillel Coren
8a5621396b Add IP exception 2019-12-15 14:33:46 +02:00
Hillel Coren
618743ea1a Update version 2019-12-03 11:53:50 +02:00
Hillel Coren
69e8f9a2f3 Minor fixes 2019-12-03 11:50:38 +02:00
Hillel Coren
17c36b351d Minor fixes 2019-12-03 11:27:30 +02:00
Hillel Coren
893d200e2c Merge branch 'master' of github.com:invoiceninja/invoiceninja 2019-11-20 08:43:14 +02:00
Hillel Coren
a9b511738f Import currency fix 2019-11-20 08:43:05 +02:00
David Bomba
2ec4d6702c
Fix for regression (#3070) 2019-11-14 22:07:23 +11:00
Hillel Coren
202d644d53 Update lang files 2019-11-04 11:01:18 +02:00
Hillel Coren
6ebc492454 Update lang files 2019-10-27 11:53:22 +02:00
Hillel Coren
22bc25253e Merge branch 'master' of github.com:invoiceninja/invoiceninja 2019-10-27 11:41:28 +02:00
Hillel Coren
5959eb8eb5 Minor fixes 2019-10-27 11:41:19 +02:00
Hillel Coren
0524ac764d
Merge pull request #3011 from dicarlosystems/allow-mtrans-to-replace
Allow module trans helper to replace values (#3006)
2019-10-23 16:33:12 +03:00
Christopher Di Carlo
e6e690b32f Allow module trans helper to replace values (#3006) 2019-10-23 07:17:48 -04:00
Hillel Coren
e763a2b233 Update lang files 2019-10-06 12:25:40 +03:00
Hillel Coren
29ed0dd94b Bug fixes 2019-09-29 12:28:34 +03:00
Hillel Coren
fbb5826178 Duplicate payments 2019-09-29 12:02:30 +03:00
Hillel Coren
0054616020 Update GrapesJS 2019-09-29 10:27:15 +03:00
Hillel Coren
416da8b98d
Merge pull request #2952 from joshuadwire/master
Upgrade to Laravel 5.5 and PHP 7.3
2019-09-15 12:23:45 +03:00
Joshua Dwire
8ce81d15ba Upgrade modules and debugbar dependencies 2019-09-14 14:37:51 -04:00
Joshua Dwire
f222390ce8 Upgrade to Laravel 5.5 and PHP 7.3 2019-09-12 19:30:10 -04:00
Hillel Coren
49c4aa6a75 Updated readme 2019-09-05 12:14:33 +03:00
Hillel Coren
d6f2859d3d Merge branch 'master' of github.com:invoiceninja/invoiceninja 2019-09-05 07:14:50 +03:00
Hillel Coren
c0300c9e0d Update docs 2019-09-05 07:14:40 +03:00
Hillel Coren
36290336c3
Merge pull request #2944 from joshuadwire/master
Fix support for currencies with varying precisions
2019-08-30 10:56:32 +03:00
Joshua Dwire
20c12533f8 Fix support for currencies with varying precisions 2019-08-29 17:15:58 -04:00
Hillel Coren
34600446d9 Update lang files 2019-08-29 13:18:02 +03:00
Hillel Coren
96991da621 IAP 2019-08-29 13:12:27 +03:00
Hillel Coren
a4a757f288 Fix for ninja Stripe payments 2019-08-29 13:10:09 +03:00
Hillel Coren
0ee357e97a In app purchase 2019-08-28 10:14:05 +03:00
Hillel Coren
c1fa41ee30 Update lang file 2019-08-20 22:05:14 +03:00
Hillel Coren
195074a4b0 Working on tests 2019-08-12 12:09:40 +03:00
Hillel Coren
790a2a8a2d Merge branch 'master' of github.com:invoiceninja/invoiceninja 2019-08-12 12:01:43 +03:00
Hillel Coren
16623103cd Working on tests 2019-08-12 12:01:34 +03:00
Joshua Dwire
3cc9a580e2 Update PHP version number in Digital Ocean docs (#2927) 2019-08-11 12:11:03 +10:00
Hillel Coren
8c8a91ba4d
Merge pull request #2926 from joshuadwire/master
Add Digital Ocean image docs (master)
2019-08-10 20:52:25 +03:00
Joshua Dwire
caa3a34a4a Add Digital Ocean image docs 2019-08-10 10:50:33 -04:00
David Bomba
4ee0512827
Update README.md 2019-08-03 17:10:37 +10:00
hjone72
ba8c0eb035 Fix for subscription fails on getDisplayName() (#2909)
As per https://github.com/invoiceninja/invoiceninja/issues/2908
2019-07-24 14:04:21 +10:00
Hillel Coren
3cc9eb0bec Update version 2019-07-21 11:03:12 +03:00
Hillel Coren
bbb72875e3 Bug fix 2019-07-21 10:59:31 +03:00
Hillel Coren
f2416b3b91 Update lang files 2019-07-21 09:52:42 +03:00
Hillel Coren
23571848d5 Minor fixes 2019-07-21 09:28:42 +03:00
Hillel Coren
5b76c0a2d1 Make 'email history' easier to find 2019-07-21 09:19:39 +03:00
Hillel Coren
9943b89b2d API fixes 2019-07-21 09:13:03 +03:00
Francisco Ferreira
5f998b0ea8 Fixes recurring invoices end date parsing (port of #2905 to master) (#2906)
* Invoice edit view: fix date parsing

View gets the date already formatted and Carbon isn't always able to parse that format automatically, so convert it back to SQL format

* RecurringInvoiceDatatable: fix status label
2019-07-18 16:26:59 +10:00
Francisco Ferreira
2a290765e9 Account: allow to set more custom Invoice labels (#2904) 2019-07-18 16:26:24 +10:00
Francisco Ferreira
b0e680ee5c Stripe improvements (port #2863 to master) (#2903)
* View credit card: send more billing data to Stripe when available

As commit 8e8414ab49 shows, this commit had more code that was wrongly commited in ff064367d6 by another author

* Stripe: fill payment card details
2019-07-18 16:26:08 +10:00
Hillel Coren
989f114154 Fix for composer install 2019-07-13 23:11:26 +03:00
Hillel Coren
6a41952552
Merge pull request #2898 from joshuadwire/master
Merge Stripe 3D Secure into master
2019-07-13 22:19:50 +03:00
Joshua Dwire
cb37141643 Use the old Charge API for bank account payments 2019-07-09 17:16:37 -04:00
Joshua Dwire
ff064367d6 Support Stripe 3D secure 2019-07-09 17:16:20 -04:00
Hillel Coren
ec2aedbba0
Merge pull request #2884 from arubacao/patch-1
Sync services.php with upstream repo
2019-07-05 08:50:51 +03:00
arubacao
161f160ba7
Update services.php 2019-07-01 23:40:24 +02:00
arubacao
36ab20ae3a
Sync services.php with upstream repo
https://raw.githubusercontent.com/laravel/laravel/5.4/config/services.php
2019-07-01 17:08:27 +02:00
Hillel Coren
3a45a48a1a Merge task->invoice->withTrashed() 2019-05-27 18:10:40 +03:00
Hillel Coren
e3dcf4aa47 Fix for tests 2019-05-26 21:57:43 +03:00
Hillel Coren
91079b66d6 Update version 2019-05-26 21:29:46 +03:00
Hillel Coren
099e61343d Update lang files 2019-05-26 21:22:23 +03:00
Hillel Coren
72769deacc Update docs 2019-05-26 19:32:09 +03:00
Hillel Coren
6656bf3ab2 Update docs 2019-05-26 19:30:55 +03:00
Hillel Coren
1a5f89a905 Fix overlapping text 2019-05-26 18:40:08 +03:00
Hillel Coren
6c5af5c593 Fix recurring invoice status label 2019-05-26 18:31:28 +03:00
Hillel Coren
a6a4eac8fd Fix for Stripe import issue 2019-05-26 18:24:00 +03:00
Hillel Coren
a83f441b98 Fix imported payment date 2019-05-26 15:24:22 +03:00
Hillel Coren
003a5be614 Added client address to invoice/quote reports 2019-05-26 11:23:27 +03:00
Hillel Coren
c6a6926b7f Set WorldPay to offsite 2019-05-26 10:50:08 +03:00
Hillel Coren
5e7ee641a6 Adjustments to check data 2019-05-23 21:33:07 +03:00
Hillel Coren
d41e0b4759 Merges #2322 2019-05-23 21:28:35 +03:00
Hillel Coren
73da9d4acc Fix duplicate confirmation emails 2019-05-23 21:17:47 +03:00
Hillel Coren
14684c8ba7 Add currencies 2019-05-22 14:41:09 +03:00
David Bomba
9332d00b69
Set artisan to executable (#2827) 2019-05-09 08:51:37 +10:00
Hillel Coren
53fdf2434a Update readme 2019-04-29 15:35:22 +03:00
Hillel Coren
ef1d55f1fe Update docs 2019-04-25 15:53:49 +03:00
Hillel Coren
3b5bfd4eaf Update readme 2019-04-25 15:50:46 +03:00
David Bomba
3733583eef
Revert "Allow installing composer dependencies with PHP 7.3" (#2797) 2019-04-23 19:08:53 +10:00
Hillel Coren
89c21aa943
Merge pull request #2794 from nstapelbroek/install-on-php73
Allow installing composer dependencies with PHP 7.3
2019-04-23 00:43:06 +03:00
Nico Stapelbroek
24d5d387ff Run CI against PHP 7.3 2019-04-20 09:36:46 +02:00
Nico Stapelbroek
b070782dd2 Update mpdf/mpdf with its dependencies
Details: 0 installs, 5 updates, 0 removals
  - Updating myclabs/deep-copy (1.7.0 => 1.9.1)
  - Updating paragonie/random_compat (v2.0.17 => v2.0.18)
  - Updating setasign/fpdi (1.6.2 => v2.2.0)
  - Updating psr/log (1.0.2 => 1.1.0)
  - Updating mpdf/mpdf (v7.1.0 => v8.0.0)

Also undid URL change in google/protobuf
2019-04-20 09:35:36 +02:00
Hillel Coren
9db21e6f3c quote to invoice private notes #2742 2019-04-17 18:36:54 +03:00
Hillel Coren
40621823dc XSS fixes 2019-04-17 18:29:09 +03:00
Hillel Coren
a1d781c435 Fix endless reminder translation 2019-04-17 18:18:19 +03:00
Hillel Coren
72831b0cef Update version 2019-03-26 08:42:08 +02:00
Hillel Coren
d543cfcc3f Merge proposal PDF fix 2019-03-26 08:40:03 +02:00
Hillel Coren
b818ff95b6 Update docs 2019-03-26 08:38:03 +02:00
Hillel Coren
02fb5cede8 Fix for task tranformer 2019-03-26 08:33:39 +02:00
Hillel Coren
28d66567b4 Merge branch 'master' of github.com:invoiceninja/invoiceninja 2019-03-19 07:38:24 +02:00
Hillel Coren
b88ea8aa2c Remove fix for duplicate invitations 2019-03-19 07:38:13 +02:00
Hillel Coren
7546978f2b
Merge pull request #2724 from Kilian/master
Fix Typo: Speciy -> Specify
2019-03-14 12:16:48 +02:00
kilian
5f576a8852 Fix Typo: Speciy -> Specify 2019-03-14 11:12:38 +01:00
Hillel Coren
fb62be10cb Change Payment Express back to onsite gateway 2019-03-12 08:44:45 +02:00
Hillel Coren
4a28d9d455 Fix for Payment Express 2019-03-10 09:45:44 +02:00
Hillel Coren
ecaf16f9bd Fix placholder translation 2019-02-28 15:07:40 +02:00
Hillel Coren
736f1683d1 Update version 2019-02-28 09:47:32 +02:00
Hillel Coren
1a6e940888 Update language files 2019-02-28 09:45:38 +02:00
Hillel Coren
4426be8303 Add currencies 2019-02-28 09:39:07 +02:00
Hillel Coren
ef3d1cb966 Fix for invoice transformer 2019-02-28 09:27:19 +02:00
Hillel Coren
6c1bf7534a Hide export PDF option 2019-02-28 09:26:17 +02:00
Hillel Coren
27f0fb9474 Fix for tests 2019-02-25 15:25:53 +02:00
Hillel Coren
9dd1462d65 Fix for duplicate invitations 2019-02-25 12:42:14 +02:00
Hillel Coren
3e8565bd70 Added lang strings 2019-02-22 09:04:22 +02:00
Hillel Coren
fd1c73c472 Fix for GoCardless 2019-02-21 15:53:07 +02:00
Hillel Coren
5e269ed07a Update version 2019-02-19 18:01:59 +02:00
Hillel Coren
31a0641943 Update language files 2019-02-19 18:00:49 +02:00
Hillel Coren
4d471bccaf Fix for Google OAuth 2019-02-19 13:15:06 +02:00
Hillel Coren
2d4769137f Bug fixes 2019-02-19 12:55:39 +02:00
Hillel Coren
6fddd3a815 Added BOB currency 2019-02-19 12:15:34 +02:00
Hillel Coren
3a12da73bf Fix for Payment Express 2019-02-19 12:06:11 +02:00
Hillel Coren
fa45a02134 Add currencies 2019-02-19 11:47:29 +02:00
Hillel Coren
88b972be4b Bug fixes 2019-02-18 16:10:05 +02:00
Hillel Coren
02b78055d4 Bug fixes 2019-02-18 16:08:16 +02:00
Hillel Coren
16cc898e6a Bug fixes 2019-02-18 13:05:00 +02:00
Hillel Coren
08be751aa4 Hide report export to PDF option 2019-02-18 12:02:56 +02:00
Hillel Coren
738b531925 Try updating DomPDF 2019-02-18 11:49:48 +02:00
Hillel Coren
155f107238 Fix for tests 2019-02-17 14:22:00 +02:00
Hillel Coren
ea8f94bb51 Update .gitignore 2019-02-17 14:03:00 +02:00
Hillel Coren
cd8b7649a1 Fixes for mobile app 2019-02-17 13:52:56 +02:00
Wim de Ruijter
48c3b5fcff Made clear what "account with fees" means (#2664)
Should have done this in the previous PR: https://github.com/invoiceninja/invoiceninja/pull/2663
2019-02-12 07:12:42 +11:00
Hillel Coren
a0b85b491f
Merge pull request #2663 from wderuijter/patch-1
Made clear what "account with reminders" means
2019-02-11 12:47:17 +02:00
Wim de Ruijter
143b65ad9f
Made clear what "account with reminders" means 2019-02-11 11:34:22 +01:00
David Bomba
ad973919ad
Fix for Google Maps (#2651) 2019-02-03 11:52:02 +11:00
David Bomba
6b21e0bb8e
Update .travis.yml 2019-01-31 20:50:27 +11:00
David Bomba
b8ec0f6cab
Fixes for travis (#2645)
* add gitignores back into app

* gitignore bootstrap

* fixes for storage
2019-01-31 20:16:07 +11:00
David Bomba
1476f12a8d
.gitignore bootstrap (#2644)
* add gitignores back into app

* gitignore bootstrap
2019-01-31 19:54:06 +11:00
David Bomba
8677a72b7c
add gitignores back into app (#2643) 2019-01-31 19:50:04 +11:00
David Bomba
dc49fad81a
Update API docs (#2641)
* fix permissions

* Api update
2019-01-31 08:20:22 +11:00
David Bomba
aefe133ba0
fix permissions (#2637) 2019-01-30 22:25:37 +11:00
David Bomba
0fc66b0f02 Rebase to latest version of v4.5.9 2019-01-30 22:12:41 +11:00
David Bomba
5ccf7369ca Rebase to v4.5.9 2019-01-30 21:45:46 +11:00
446 changed files with 7232 additions and 18907 deletions

41
.github/ISSUE_TEMPLATE/bug_report.md vendored Normal file
View file

@ -0,0 +1,41 @@
---
name: Bug report
about: Create a report to help us improve
title: ''
labels: triage
assignees: ''
---
**What version of Invoice Ninja are you running? ie v4.5.25 / v5.0.30**
**What environment are you running?**
Docker
Shared Hosting
ZIP
Other
**Have you checked log files (storage/logs/) Please provide redacted output**
**Have you searched existing issues?**
**Have you reported this to Slack/forum before posting?**
**Describe the bug**
A clear and concise description of what the bug is.
**Steps To Reproduce**
Please list the steps to reproduce the issue
**Expected behavior**
A clear and concise description of what you expected to happen.
**Screenshots**
If applicable, add screenshots to help explain your problem.
**Additional context**
Add any other context about the problem here.
Note: Before posting don't forget to check our "Troubleshooting" category in the [docs](https://invoiceninja.github.io/docs/self-host-troubleshooting/) (https://invoiceninja.github.io/docs/self-host-troubleshooting/).
**(v5) Can you replicate the issue on our demo site? https://demo.invoiceninja.com**

View file

@ -0,0 +1,24 @@
---
name: Feature request
about: Suggest an idea for this project
title: ''
labels: feature request
assignees: ''
---
**What version of Invoice Ninja are you running? ie v4.5 / v5**
**What environment are you running?**
Docker
Shared Hosting
ZIP
Other
**Have you searched existing issues/requests?**
**Screenshots**
If applicable, add screenshots to help explain your request/question.
**Additional context**
Add any other context about the request/question here.

2
.gitignore vendored
View file

@ -11,6 +11,8 @@
/resources/assets/bower
/storage/*.key
/storage/documents
/storage/import
/storage/migrations
/bootstrap/compiled.php
/bootstrap/environment.php
/vendor

View file

@ -1,5 +1,8 @@
language: php
services:
- mysql
sudo: true
# Prevent tests from taking more than 50 minutes
@ -7,9 +10,7 @@ group: deprecated-2017Q4
php:
- 7.2
services:
- mysql
- 7.3
addons:
hosts:
@ -29,7 +30,7 @@ env:
before_install:
# set GitHub token and update composer
- if [ -n "$GH_TOKEN" ]; then composer config github-oauth.github.com ${GH_TOKEN}; fi;
- composer self-update && composer -V
- composer self-update 1.10.19 && composer -V
# - export USE_ZEND_ALLOC=0
- rvm use 1.9.3 --install --fuzzy

88
LICENSE
View file

@ -1,47 +1,47 @@
Elastic License 2.0 (ELv2)
Elastic License
Copyright (c) 2018 by Hillel Coren
Invoice Ninja * https://www.invoiceninja.com
"CREATE. SEND. GET PAID"
Acceptance
By using the software, you agree to all of the terms and conditions below.
All Rights Reserved
ATTRIBUTION ASSURANCE LICENSE (adapted from the original BSD license)
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the conditions below are met.
These conditions require a modest attribution to InvoiceNinja.com (the
"Author"), who hopes that its promotional value may help justify the
thousands of dollars in otherwise billable time invested in writing
this and other freely available, open-source software.
Copyright License
The licensor grants you a non-exclusive, royalty-free, worldwide, non-sublicensable, non-transferable license to use, copy, distribute, make available, and prepare derivative works of the software, in each case subject to the limitations and conditions below
1. Redistributions of source code, in whole or part and with or without
modification (the "Code"), must prominently display this GPG-signed
text in verifiable form.
2. Redistributions of the Code in binary form must be accompanied by
this GPG-signed text in any documentation and, each time the resulting
executable program or a program dependent thereon is launched, a
prominent display (e.g., splash screen or banner text) of the Author's
attribution information, which includes:
(a) Name ("Hillel Coren"),
(b) Professional identification ("Invoice Ninja"), and
(c) URL ("https://www.invoiceninja.com").
3. Neither the name nor any trademark of the Author may be used to
endorse or promote products derived from this software without specific
prior written permission.
4. Users are entirely responsible, to the exclusion of the Author and
any other persons, for compliance with (1) regulations set by owners or
administrators of employed equipment, (2) licensing terms of any other
software, and (3) local regulations regarding use, including those
regarding import, export, and use of encryption software.
Limitations
You may not provide the software to third parties as a hosted or managed service, where the service provides users with access to any substantial set of the features or functionality of the software.
You may not move, change, disable, or circumvent the license key functionality in the software, and you may not remove or obscure any functionality in the software that is protected by the license key.
You may not alter, remove, or obscure any licensing, copyright, or other notices of the licensor in the software. Any use of the licensors trademarks is subject to applicable law.
Patents
The licensor grants you a license, under any patent claims the licensor can license, or becomes able to license, to make, have made, use, sell, offer for sale, import and have imported the software, in each case subject to the limitations and conditions in this license. This license does not cover any patent claims that you cause to be infringed by modifications or additions to the software. If you or your company make any written claim that the software infringes or contributes to infringement of any patent, your patent license for the software granted under these terms ends immediately. If your company makes such a claim, your patent license ends immediately for work on behalf of your company.
Notices
You must ensure that anyone who gets a copy of any part of the software from you also gets a copy of these terms.
If you modify the software, you must include in any modified copies of the software prominent notices stating that you have modified the software.
No Other Rights
These terms do not imply any licenses other than those expressly granted in these terms.
Termination
If you use the software in violation of these terms, such use is not licensed, and your licenses will automatically terminate. If the licensor provides you with a notice of your violation, and you cease all violation of this license no later than 30 days after you receive that notice, your licenses will be reinstated retroactively. However, if you violate these terms after such reinstatement, any additional violation of these terms will cause your licenses to terminate automatically and permanently.
No Liability
As far as the law allows, the software comes as is, without any warranty or condition, and the licensor will not be liable to you for any damages arising out of these terms or the use or nature of the software, under any kind of legal claim.
Definitions
The licensor is the entity offering these terms, and the software is the software the licensor makes available under these terms, including any portion of it.
you refers to the individual or entity agreeing to these terms.
your company is any legal entity, sole proprietorship, or other kind of organization that you work for, plus all organizations that have control over, are under the control of, or are under common control with that organization. control means ownership of substantially all the assets of an entity, or the power to direct its management and policies by vote, contract, or otherwise. Control can be direct or indirect.
your licenses are all the licenses granted to you for the software under these terms.
use means anything you do with the software requiring one of your licenses.
trademark means trademarks, service marks, and similar rights.
For more information regarding the interpretation of this license please see here: https://invoiceninja.github.io/docs/legal/license/
THIS FREE SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
EVENT SHALL THE AUTHOR OR ANY CONTRIBUTOR BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
EFFECTS OF UNAUTHORIZED OR MALICIOUS NETWORK ACCESS;
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

View file

@ -4,7 +4,7 @@
# Invoice Ninja
[![Build Status](https://travis-ci.org/invoiceninja/invoiceninja.svg?branch=develop)](https://travis-ci.org/invoiceninja/invoiceninja)
[![Build Status](https://travis-ci.org/invoiceninja/invoiceninja.svg?branch=master)](https://travis-ci.org/invoiceninja/invoiceninja)
[![Docs](https://readthedocs.org/projects/invoice-ninja/badge/?version=latest)](https://invoice-ninja.readthedocs.io/en/latest/?badge=latest)
## [Hosted](https://www.invoiceninja.com) | [Self-Hosted](https://www.invoiceninja.org)
@ -13,7 +13,7 @@
Just make sure to add the `invoice-ninja` tag to your question.
#### Note: v5 is now in beta. To upgrade from v4 you need to [install v5](https://invoiceninja.github.io/docs/self-host-installation/) as a separate app and then use the migration tool in the latest version of v4 on Settings > Account Management.
#### Note: v5 is now tagged Stable! To upgrade from v4 you need to [install v5](https://invoiceninja.github.io/docs/self-host-installation/) as a separate app and then use the migration tool in the latest version of v4 on Settings > Account Management.
All Pro and Enterprise features from the hosted app are included in the open-source code. We offer a $30 per year white-label license to remove the Invoice Ninja branding from client facing parts of the app.
@ -21,8 +21,9 @@ The self-host zip includes all third party libraries whereas downloading the cod
* [Features](https://www.invoiceninja.com/invoicing-features/)
* [Videos](https://www.youtube.com/channel/UCXAHcBvhW05PDtWYIq7WDFA/videos)
* [User Guide](https://invoice-ninja.readthedocs.io/en/latest/)
* [User Guide](https://docs.invoiceninja.com/)
* [Support Forum](https://www.invoiceninja.com/forums/forum/support/)
* [StackOverflow](https://stackoverflow.com/tags/invoice-ninja/)
## Referral Program
* Earn 50% of Pro & Enterprise Plans up to 4 years - [Learn more](https://www.invoiceninja.com/referral-program/)
@ -34,10 +35,12 @@ The self-host zip includes all third party libraries whereas downloading the cod
## Installation Options
* [Ansible](https://github.com/invoiceninja/ansible-installer)
* [Self-Host Zip](https://invoice-ninja.readthedocs.io/en/latest/install.html)
* [Self-Host Zip](https://docs.invoiceninja.com/install.html)
* [Docker File](https://hub.docker.com/r/invoiceninja/invoiceninja/)
* [Cloudron](https://cloudron.io/store/com.invoiceninja.cloudronapp.html)
* [Softaculous](https://www.softaculous.com/apps/ecommerce/Invoice_Ninja)
* [Lando](https://github.com/invoiceninja/invoiceninja/issues/2880)
* [Yunohost](https://github.com/YunoHost-Apps/invoiceninja_ynh)
## Recommended Providers
* [Stripe](https://stripe.com/)
@ -66,7 +69,7 @@ The self-host zip includes all third party libraries whereas downloading the cod
* [Bold Compass](https://boldcompass.com/customize-invoice-ninja/)
## Contributing
All contributors are welcome!
All contributors are welcome!
For information on how contribute to Invoice Ninja, please see our [contributing guide](CONTRIBUTING.md).
## Credits
@ -89,5 +92,5 @@ For information on how contribute to Invoice Ninja, please see our [contributing
* [Mike Skaggs](https://github.com/titan-fail)
## License
Invoice Ninja is released under the Attribution Assurance License.
Invoice Ninja is released under the Attribution Assurance License.
See [LICENSE](LICENSE) for details.

View file

@ -42,7 +42,6 @@ class CalculatePayouts extends Command
*/
public function handle()
{
$this->info('Running CalculatePayouts...');
$type = strtolower($this->option('type'));
switch ($type) {
@ -61,7 +60,6 @@ class CalculatePayouts extends Command
$userMap = [];
foreach ($servers as $server) {
$this->info('Processing users: ' . $server->name);
config(['database.default' => $server->name]);
$users = User::where('referral_code', '!=', '')
@ -72,7 +70,6 @@ class CalculatePayouts extends Command
}
foreach ($servers as $server) {
$this->info('Processing companies: ' . $server->name);
config(['database.default' => $server->name]);
$companies = Company::where('referral_code', '!=', '')
@ -80,19 +77,27 @@ class CalculatePayouts extends Command
->whereNotNull('payment_id')
->get();
$this->info('User,Client,Date,Amount,Reference');
foreach ($companies as $company) {
if (!isset($userMap[$company->referral_code])) {
continue;
}
$user = $userMap[$company->referral_code];
$payment = $company->payment;
if ($payment) {
$client = $payment->client;
$this->info("User: $user");
$this->info("Client: " . $client->getDisplayName());
foreach ($client->payments as $payment) {
$amount = $payment->getCompletedAmount();
$this->info("Date: $payment->payment_date, Amount: $amount, Reference: $payment->transaction_reference");
$this->info('"' . $user . '",' .
'"' . $client->getDisplayName() . '",' .
$payment->payment_date . ',' .
$amount . ',' .
$payment->transaction_reference
);
}
}
}

View file

@ -75,6 +75,8 @@ class CheckData extends Command
config(['database.default' => $database]);
}
$this->checkContacts();
if (! $this->option('client_id')) {
$this->checkBlankInvoiceHistory();
$this->checkPaidToDate();
@ -82,10 +84,8 @@ class CheckData extends Command
}
//$this->checkInvoices();
$this->checkTranslations();
$this->checkInvoiceBalances();
$this->checkClientBalances();
$this->checkContacts();
$this->checkUserAccounts();
//$this->checkLogoFiles();
@ -97,6 +97,7 @@ class CheckData extends Command
$this->checkFailedJobs();
}
$this->checkTranslations();
$this->logMessage('Done: ' . strtoupper($this->isValid ? RESULT_SUCCESS : RESULT_FAILURE));
$errorEmail = env('ERROR_EMAIL');
@ -130,6 +131,7 @@ class CheckData extends Command
$this->logMessage($language->locale . ' is invalid: ' . $text);
}
/*
preg_match('/(.script)/', strtolower($text), $matches);
if (count($matches)) {
foreach ($matches as $match) {
@ -141,6 +143,7 @@ class CheckData extends Command
break;
}
}
*/
}
}

View file

@ -2,16 +2,11 @@
namespace App\Console\Commands;
use App\Models\Ticket;
use App\Models\TicketCategory;
use App\Models\TicketComment;
use App\Models\TicketTemplate;
use App\Ninja\Repositories\AccountRepository;
use App\Ninja\Repositories\ClientRepository;
use App\Ninja\Repositories\ExpenseRepository;
use App\Ninja\Repositories\InvoiceRepository;
use App\Ninja\Repositories\PaymentRepository;
use App\Ninja\Repositories\TicketRepository;
use App\Ninja\Repositories\VendorRepository;
use App\Ninja\Repositories\TaskRepository;
use App\Ninja\Repositories\ProjectRepository;
@ -20,7 +15,6 @@ use App\Models\TaxRate;
use App\Models\Project;
use App\Models\ExpenseCategory;
use Auth;
use Carbon\Carbon;
use Faker\Factory;
use Illuminate\Console\Command;
use Utils;
@ -54,12 +48,8 @@ class CreateTestData extends Command
* @param ExpenseRepository $expenseRepo
* @param TaskRepository $taskRepo
* @param AccountRepository $accountRepo
* @param TicketRepository $ticketRepo
* @param ProjectRepository $projectRepo
*/
public function __construct(
TicketRepository $ticketRepo,
ClientRepository $clientRepo,
InvoiceRepository $invoiceRepo,
PaymentRepository $paymentRepo,
@ -81,7 +71,6 @@ class CreateTestData extends Command
$this->taskRepo = $taskRepo;
$this->projectRepo = $projectRepo;
$this->accountRepo = $accountRepo;
$this->ticketRepo = $ticketRepo;
}
/**
@ -114,8 +103,6 @@ class CreateTestData extends Command
Auth::loginUsingId(1);
}
//$this->createTicketStubs();
//$this->createTicketTemplates();
$this->createClients();
$this->createVendors();
$this->createOtherObjects();
@ -146,85 +133,7 @@ class CreateTestData extends Command
$this->createInvoices($client);
$this->createInvoices($client, true);
$this->createTasks($client);
$this->createTickets($client);
}
}
private function createTicketTemplates()
{
$ticketTemplate = TicketTemplate::createNew();
$ticketTemplate->name = 'Default response';
$ticketTemplate->description = $this->faker->realText(50);
$ticketTemplate->save();
$ticketTemplate = TicketTemplate::createNew();
$ticketTemplate->name = 'Updated ticket';
$ticketTemplate->description = $this->faker->realText(50);
$ticketTemplate->save();
$ticketTemplate = TicketTemplate::createNew();
$ticketTemplate->name = 'Ticket closed';
$ticketTemplate->description = $this->faker->realText(50);
$ticketTemplate->save();
$ticketTemplate = TicketTemplate::createNew();
$ticketTemplate->name = 'Generic response';
$ticketTemplate->description = $this->faker->realText(50);
$ticketTemplate->save();
}
/**
* @param $client
*/
private function createTickets($client)
{
$this->info('creating tickets');
for ($i = 0; $i < $this->count; $i++)
{
$maxTicketNumber = Ticket::getNextTicketNumber(Auth::user()->account->id);
$this->info('next ticket number = '.$maxTicketNumber);
$data = [
'priority_id'=> TICKET_PRIORITY_LOW,
'category_id'=> 1,
'client_id' => $client->id,
'is_deleted'=> 0,
'is_internal'=> (bool)random_int(0, 1),
'status_id'=> random_int(1,3),
'category_id'=> 1,
'subject'=> $this->faker->realText(10),
'description'=> $this->faker->realText(50),
'tags'=> json_encode($this->faker->words($nb = 5, $asText = false)),
'private_notes'=> $this->faker->realText(50),
'ccs'=> json_encode([]),
'contact_key'=> $client->getPrimaryContact()->contact_key,
'due_date'=> Carbon::now(),
'ticket_number' => $maxTicketNumber ? $maxTicketNumber : 1,
'action' => TICKET_SAVE_ONLY,
];
$ticket = $this->ticketRepo->save($data);
$ticketComment = TicketComment::createNew($ticket);
$ticketComment->description = $this->faker->realText(70);
$ticketComment->contact_key = $client->getPrimaryContact()->contact_key;
$ticket->comments()->save($ticketComment);
$ticketComment = TicketComment::createNew($ticket);
$ticketComment->description = $this->faker->realText(40);
$ticketComment->user_id = 1;
$ticket->comments()->save($ticketComment);
$this->info("Ticket: - {$ticket->ticket_number} - {$client->account->account_ticket_settings->ticket_number_start} - {$maxTicketNumber}");
// $this->createTasks($client);
}
}

View file

@ -0,0 +1,115 @@
<?php
namespace App\Console\Commands;
use App\Libraries\Utils;
use App\Models\User;
use App\Traits\GenerateMigrationResources;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\Auth;
class ExportMigrations extends Command
{
use GenerateMigrationResources;
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'migrations:export {--user=} {--random=}';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Export account migrations to folder.';
/**
* Create a new command instance.
*
* @return void
*/
public function __construct()
{
parent::__construct();
}
/**
* Execute the console command.
*
* @return mixed
*/
public function handle()
{
$this->info('Note: Migrations will be stored inside of (storage/migrations) folder.');
if($this->option('user')) {
$record = User::findOrFail($this->option('user'));
return $this->export($record);
}
if($this->option('random')){
User::all()->random(200)->each(function ($user){
$this->export($user);
});
return;
}
$users = User::all();
foreach($users as $user) {
Auth::login($user);
$this->export($user);
}
}
private function export($user)
{
$this->account = $user->account;
$date = date('Y-m-d');
$accountKey = $this->account->account_key;
$output = fopen('php://output', 'w') or Utils::fatalError();
$fileName = "{$accountKey}-{$date}-invoiceninja";
$data['data'] = [
'account' => $this->getAccount(),
'company' => $this->getCompany(),
'users' => $this->getUsers(),
'tax_rates' => $this->getTaxRates(),
'payment_terms' => $this->getPaymentTerms(),
'clients' => $this->getClients(),
'company_gateways' => $this->getCompanyGateways(),
'client_gateway_tokens' => $this->getClientGatewayTokens(),
'vendors' => $this->getVendors(),
'projects' => $this->getProjects(),
'products' => $this->getProducts(),
'credits' => $this->getCreditsNotes(),
'invoices' => $this->getInvoices(),
'recurring_invoices' => $this->getRecurringInvoices(),
'quotes' => $this->getQuotes(),
'payments' => array_merge($this->getPayments(), $this->getCredits()),
'documents' => $this->getDocuments(),
'expense_categories' => $this->getExpenseCategories(),
'task_statuses' => $this->getTaskStatuses(),
'expenses' => $this->getExpenses(),
'tasks' => $this->getTasks(),
'documents' => $this->getDocuments(),
];
$file = storage_path("migrations/{$fileName}.zip");
$zip = new \ZipArchive();
$zip->open($file, \ZipArchive::CREATE | \ZipArchive::OVERWRITE);
$zip->addFromString('migration.json', json_encode($data, JSON_PRETTY_PRINT));
$zip->close();
$this->info('User with id #' . $user->id . ' exported.');
}
}

View file

@ -362,7 +362,6 @@ class InitLookup extends Command
DB::statement('truncate lookup_invitations');
DB::statement('truncate lookup_proposal_invitations');
DB::statement('truncate lookup_account_tokens');
DB::statement('truncate lookup_ticket_invitations');
DB::statement('SET FOREIGN_KEY_CHECKS = 1');
}

View file

@ -1,113 +0,0 @@
<?php
namespace App\Console\Commands;
use Illuminate\Console\Command;
use Nwidart\Modules\Commands\GeneratorCommand;
use Nwidart\Modules\Support\Stub;
use Nwidart\Modules\Traits\ModuleCommandTrait;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputOption;
class MakeModuleSettings extends GeneratorCommand
{
use ModuleCommandTrait;
/**
* The name and signature of the console command.
*
* @var string
*/
// protected $signature = 'ninja:make-module-settings {name : Module name} {--route : Add routes }';
protected $name = 'ninja:make-module-settings';
protected $argumentName = 'module';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Create module settings';
/**
* Create a new command instance.
*
* @return void
*/
public function __construct()
{
parent::__construct();
}
public function getTemplateContents()
{
$module = $this->laravel['modules']->findOrFail($this->getModuleName());
$path = str_replace('/', '\\', config('modules.paths.generator.module-settings-view'));
return (new Stub('/module-settings-view.stub', [
'MODULE_NAME' => $module->getName(),
'LOWER_NAME' => $module->getLowerName(),
'SHOW_ROUTES' => $this->option('route') ? true : false
]))->render();
}
public function handle() {
$this->info('Creating settings view template for ' . $this->getModuleName());
$module = $this->laravel['modules']->findOrFail($this->getModuleName());
parent::handle();
// add default routes if option specified
$route = $this->option('route');
if ($route) {
file_put_contents(
$this->getModuleRoutesFilePath(),
(new Stub('/module-settings-routes.stub', [
'MODULE_NAME' => $module->getName(),
'LOWER_NAME' => $module->getLowerName(),
]))->render(),
FILE_APPEND
);
$this->info('Added routes to module routes.php.');
}
}
protected function getModuleRoutesFilePath() {
$path = $this->laravel['modules']->getModulePath($this->getModuleName());
$seederPath = $this->laravel['modules']->config('paths.generator.module-settings-routes');
return $path . $seederPath . '/routes.php';
}
public function getDestinationFilePath()
{
$path = $this->laravel['modules']->getModulePath($this->getModuleName());
$seederPath = $this->laravel['modules']->config('paths.generator.module-settings-view');
return $path . $seederPath . '/' . $this->getFileName();
}
protected function getArguments()
{
return [
['module', InputArgument::REQUIRED, 'The name of the module.']
];
}
protected function getOptions()
{
return [
['route', null, InputOption::VALUE_NONE, 'Add default routes.', null]
];
}
/**
* @return string
*/
protected function getFileName()
{
return 'settings.blade.php';
}
}

View file

@ -1,79 +0,0 @@
<?php
namespace App\Console\Commands;
use App\Jobs\SendOverdueTicketNotification;
use App\Models\Ticket;
use Carbon\Carbon;
use Illuminate\Console\Command;
use Symfony\Component\Console\Input\InputOption;
/**
* Class SendOverdueTickets.
*/
class SendOverdueTickets extends Command
{
/**
* @var string
*/
protected $name = 'ninja:send-overdue-tickets';
/**
* @var string
*/
protected $description = 'Send overdue tickets';
public function __construct()
{
parent::__construct();
}
public function handle()
{
$this->info(date('r') . ' Running SendOverdueTickets...');
if ($database = $this->option('database'))
config(['database.default' => $database]);
$this->sendReminders();
$this->info(date('r') . ' Done');
}
private function sendReminders()
{
$tickets = Ticket::with('account', 'account.account_ticket_settings')
->where('due_date', '<', Carbon::now())
->whereIn('status_id', [1,2])
->where('overdue_notification_sent', '=', 0)
->whereHas('account.account_ticket_settings', function ($query) {
$query->where('alert_ticket_overdue_agent_id', '>', '0');
})->get();
foreach($tickets as $ticket)
dispatch(new SendOverdueTicketNotification($ticket));
}
/**
* @return array
*/
protected function getArguments()
{
return [];
}
/**
* @return array
*/
protected function getOptions()
{
return [
['database', null, InputOption::VALUE_OPTIONAL, 'Database', null],
];
}
}

View file

@ -125,7 +125,7 @@ class SendReminders extends Command
private function chargeLateFees()
{
$accounts = $this->accountRepo->findWithFees();
$this->info(date('r ') . $accounts->count() . ' accounts found with fees');
$this->info(date('r ') . $accounts->count() . ' accounts found with fees enabled');
foreach ($accounts as $account) {
if (! $account->hasFeature(FEATURE_EMAIL_TEMPLATES_REMINDERS)) {
@ -141,14 +141,8 @@ class SendReminders extends Command
$account->loadLocalizationSettings($invoice->client); // support trans to add fee line item
$number = preg_replace('/[^0-9]/', '', $reminder);
if ($invoice->isQuote()) {
$amount = $account->account_email_settings->{"late_fee_quote{$number}_amount"};
$percent = $account->account_email_settings->{"late_fee_quote{$number}_percent"};
} else {
$amount = $account->account_email_settings->{"late_fee{$number}_amount"};
$percent = $account->account_email_settings->{"late_fee{$number}_percent"};
}
$amount = $account->account_email_settings->{"late_fee{$number}_amount"};
$percent = $account->account_email_settings->{"late_fee{$number}_percent"};
$this->invoiceRepo->setLateFee($invoice, $amount, $percent);
}
}
@ -158,7 +152,7 @@ class SendReminders extends Command
private function sendReminderEmails()
{
$accounts = $this->accountRepo->findWithReminders();
$this->info(date('r ') . count($accounts) . ' accounts found with reminders');
$this->info(date('r ') . count($accounts) . ' accounts found with reminders enabled');
foreach ($accounts as $account) {
if (! $account->hasFeature(FEATURE_EMAIL_TEMPLATES_REMINDERS)) {
@ -190,18 +184,6 @@ class SendReminders extends Command
$this->info(date('r') . ' Send email: ' . $invoice->id);
dispatch(new SendInvoiceEmail($invoice, $invoice->user_id, 'reminder4'));
}
// endless quote reminders
$invoices = $this->invoiceRepo->findNeedingEndlessReminding($account, true);
$this->info(date('r ') . $account->name . ': ' . $invoices->count() . ' endless quotes found');
foreach ($invoices as $invoice) {
if ($invoice->last_sent_date == date('Y-m-d')) {
continue;
}
$this->info(date('r') . ' Send email: ' . $invoice->id);
dispatch(new SendInvoiceEmail($invoice, $invoice->user_id, 'quote_reminder4'));
}
}
}
@ -229,7 +211,7 @@ class SendReminders extends Command
// send email as user
auth()->onceUsingId($user->id);
$report = dispatch_now(new RunReport($scheduledReport->user, $reportType, $config, $account, true));
$report = dispatch_now(new RunReport($scheduledReport->user, $reportType, $config, true));
$file = dispatch_now(new ExportReportResults($scheduledReport->user, $config['export_format'], $reportType, $report->exportParams));
if ($file) {
@ -258,24 +240,13 @@ class SendReminders extends Command
if (config('ninja.exchange_rates_enabled')) {
$this->info(date('r') . ' Loading latest exchange rates...');
$url = config('ninja.exchange_rates_url');
$apiKey = config('ninja.exchange_rates_api_key');
$url = str_replace('{apiKey}', $apiKey, $url);
$response = CurlUtils::get($url);
$response = CurlUtils::get(config('ninja.exchange_rates_url'));
$data = json_decode($response);
if ($data && property_exists($data, 'rates') && property_exists($data, 'base')) {
$base = config('ninja.exchange_rates_base');
// should calculate to different base
$recalculate = ($data->base != $base);
if ($data && property_exists($data, 'rates')) {
Currency::whereCode(config('ninja.exchange_rates_base'))->update(['exchange_rate' => 1]);
foreach ($data->rates as $code => $rate) {
if($recalculate) {
$rate = 1 / $data->rates->{$base} * $rate;
}
Currency::whereCode($code)->update(['exchange_rate' => $rate]);
}
} else {

View file

@ -1,8 +0,0 @@
Route::group(['middleware' => ['web', 'lookup:user', 'auth:user'], 'namespace' => 'Modules\$MODULE_NAME$\Http\Controllers'], function()
{
Route::get('settings/$LOWER_NAME$', function() {
return view('$LOWER_NAME$::settings');
});
Route::post('settings/$LOWER_NAME$', '$MODULE_NAME$Controller@saveSettings');
});

View file

@ -1,31 +0,0 @@
@extends('header')
@section('content')
@parent
@include('accounts.nav', ['selected' => '$MODULE_NAME$'])
<div class="row">
<div class="col-md-12">
{!! Former::open('settings/$MODULE_NAME$') !!}
<div class="panel panel-default">
<div class="panel-heading">
<h3 class="panel-title">$MODULE_NAME$ Settings</h3>
</div>
<div class="panel-group">
<div class="form-group"></div>
<div class="form-group">
<label class="control-label col-lg-4 col-sm-4"></label>
<div class="col-lg-8 col-sm-8">
{!! Button::success(trans('texts.save'))->submit()->large()->appendIcon(Icon::create('floppy-disk')) !!}
</div>
</div>
</div>
</div>
{!! Former::close() !!}
</div>
</div>
@stop

View file

@ -21,6 +21,7 @@ class Kernel extends ConsoleKernel
'App\Console\Commands\PruneData',
'App\Console\Commands\CreateTestData',
'App\Console\Commands\CreateLuisData',
'App\Console\Commands\MobileLocalization',
'App\Console\Commands\SendRenewalInvoices',
'App\Console\Commands\ChargeRenewalInvoices',
'App\Console\Commands\SendReminders',
@ -30,9 +31,7 @@ class Kernel extends ConsoleKernel
'App\Console\Commands\InitLookup',
'App\Console\Commands\CalculatePayouts',
'App\Console\Commands\UpdateKey',
'App\Console\Commands\MobileLocalization',
'App\Console\Commands\SendOverdueTickets',
'App\Console\Commands\MakeModuleSettings',
'App\Console\Commands\ExportMigrations',
];
/**
@ -47,13 +46,13 @@ class Kernel extends ConsoleKernel
$logFile = storage_path() . '/logs/cron.log';
$schedule
->command('ninja:send-invoices')
->command('ninja:send-invoices --force')
->sendOutputTo($logFile)
->withoutOverlapping()
->hourly();
$schedule
->command('ninja:send-reminders')
->command('ninja:send-reminders --force')
->sendOutputTo($logFile)
->daily();
}

View file

@ -19,7 +19,6 @@ if (! defined('APP_NAME')) {
define('ENTITY_INVOICE_ITEM', 'invoice_item');
define('ENTITY_INVITATION', 'invitation');
define('ENTITY_RECURRING_INVOICE', 'recurring_invoice');
define('ENTITY_RECURRING_QUOTE', 'recurring_quote');
define('ENTITY_PAYMENT', 'payment');
define('ENTITY_CREDIT', 'credit');
define('ENTITY_QUOTE', 'quote');
@ -48,13 +47,6 @@ if (! defined('APP_NAME')) {
define('ENTITY_PROPOSAL_SNIPPET', 'proposal_snippet');
define('ENTITY_PROPOSAL_CATEGORY', 'proposal_category');
define('ENTITY_PROPOSAL_INVITATION', 'proposal_invitation');
define('ENTITY_TICKET', 'ticket');
define('ENTITY_TICKET_COMMENT', 'ticket_comment');
define('ENTITY_TICKET_STATUS', 'ticket_status');
define('ENTITY_TICKET_CATEGORY', 'ticket_category');
define('ENTITY_TICKET_RELATION', 'ticket_relation');
define('ENTITY_TICKET_TEMPLATE', 'ticket_template');
define('ENTITY_TICKET_INVITATION', 'ticket_invitation');
$permissionEntities = [
ENTITY_CLIENT,
@ -67,11 +59,10 @@ if (! defined('APP_NAME')) {
ENTITY_PROJECT,
ENTITY_PROPOSAL,
ENTITY_QUOTE,
'dashboard',
'reports',
ENTITY_TICKET,
ENTITY_TASK,
ENTITY_VENDOR,
ENTITY_RECURRING_INVOICE,
];
define('PERMISSION_ENTITIES', json_encode($permissionEntities));
@ -104,7 +95,6 @@ if (! defined('APP_NAME')) {
define('ACCOUNT_MAP', 'import_map');
define('ACCOUNT_EXPORT', 'export');
define('ACCOUNT_TAX_RATES', 'tax_rates');
define('ACCOUNT_TICKETS', 'tickets');
define('ACCOUNT_PRODUCTS', 'products');
define('ACCOUNT_ADVANCED_SETTINGS', 'advanced_settings');
define('ACCOUNT_INVOICE_SETTINGS', 'invoice_settings');
@ -172,16 +162,6 @@ if (! defined('APP_NAME')) {
define('ACTIVITY_TYPE_DELETE_TASK', 45);
define('ACTIVITY_TYPE_RESTORE_TASK', 46);
define('ACTIVITY_TYPE_UPDATE_EXPENSE', 47);
define('ACTIVITY_TYPE_USER_UPDATE_TICKET', 48);
define('ACTIVITY_TYPE_USER_CLOSE_TICKET', 49);
define('ACTIVITY_TYPE_USER_MERGE_TICKET', 50);
define('ACTIVITY_TYPE_USER_SPLIT_TICKET', 51);
define('ACTIVITY_TYPE_CONTACT_OPEN_TICKET', 52);
define('ACTIVITY_TYPE_CONTACT_REOPEN_TICKET', 53);
define('ACTIVITY_TYPE_USER_REOPEN_TICKET', 54);
define('ACTIVITY_TYPE_CONTACT_REPLY_TICKET', 55);
define('ACTIVITY_TYPE_USER_VIEW_TICKET', 56);
define('DEFAULT_INVOICE_NUMBER', '0001');
define('RECENTLY_VIEWED_LIMIT', 20);
@ -381,7 +361,7 @@ if (! defined('APP_NAME')) {
define('NINJA_APP_URL', env('NINJA_APP_URL', 'https://app.invoiceninja.com'));
define('NINJA_DOCS_URL', env('NINJA_DOCS_URL', 'https://invoice-ninja.readthedocs.io/en/latest'));
define('NINJA_DATE', '2000-01-01');
define('NINJA_VERSION', '4.5.37' . env('NINJA_VERSION_SUFFIX'));
define('NINJA_VERSION', '4.5.40' . env('NINJA_VERSION_SUFFIX'));
define('NINJA_TERMS_VERSION', '1.0.1');
define('SOCIAL_LINK_FACEBOOK', env('SOCIAL_LINK_FACEBOOK', 'https://www.facebook.com/invoiceninja'));
@ -513,10 +493,6 @@ if (! defined('APP_NAME')) {
define('TEMPLATE_REMINDER2', 'reminder2');
define('TEMPLATE_REMINDER3', 'reminder3');
define('TEMPLATE_REMINDER4', 'reminder4');
define('TEMPLATE_QUOTE_REMINDER1', 'quote_reminder1');
define('TEMPLATE_QUOTE_REMINDER2', 'quote_reminder2');
define('TEMPLATE_QUOTE_REMINDER3', 'quote_reminder3');
define('TEMPLATE_QUOTE_REMINDER4', 'quote_reminder4');
define('CUSTOM_MESSAGE_DASHBOARD', 'dashboard');
define('CUSTOM_MESSAGE_UNPAID_INVOICE', 'unpaid_invoice');
@ -598,7 +574,6 @@ if (! defined('APP_NAME')) {
define('FEATURE_MORE_INVOICE_DESIGNS', 'more_invoice_designs');
define('FEATURE_QUOTES', 'quotes');
define('FEATURE_TASKS', 'tasks');
define('FEATURE_TICKETS', 'tickets');
define('FEATURE_EXPENSES', 'expenses');
define('FEATURE_REPORTS', 'reports');
define('FEATURE_BUY_NOW_BUTTONS', 'buy_now_buttons');
@ -688,45 +663,6 @@ if (! defined('APP_NAME')) {
// Fix for mPDF: https://github.com/kartik-v/yii2-mpdf/issues/9
define('_MPDF_TTFONTDATAPATH', storage_path('framework/cache/'));
/** STD constatns */
if(!defined('STDIN')) define('STDIN', fopen('php://stdin', 'r'));
if(!defined('STDOUT')) define('STDOUT', fopen('php://stdout', 'w'));
if(!defined('STDERR')) define('STDERR', fopen('php://stderr', 'w'));
/** Tickets constants */
define('TICKET_PRIORITY_LOW', 10);
define('TICKET_PRIORITY_MEDIUM', 20);
define('TICKET_PRIORITY_HIGH', 30);
define('TICKET_STATUS_NEW', 1);
define('TICKET_STATUS_OPEN',2);
define('TICKET_STATUS_CLOSED',3);
define('TICKET_STATUS_MERGED',4);
define('TICKET_CLIENT_NEW', 'ticket_client_new');
define('TICKET_CLIENT_UPDATE', 'ticket_client_update');
define('TICKET_INBOUND_NEW', 'ticket_inbound_new');
define('TICKET_INBOUND_NEW_INTERNAL', 'ticket_inbound_new_internal');
define('TICKET_INBOUND_REPLY', 'ticket_inbound_reply');
define('TICKET_INBOUND_CONTACT_REPLY', 'ticket_inbound_contact_reply');
define('TICKET_INBOUND_AGENT_REPLY', 'ticket_inbound_agent_reply');
define('TICKET_INBOUND_ADMIN_REPLY', 'ticket_inbound_admin_reply');
define('TICKET_AGENT_UPDATE', 'ticket_agent_update');
define('TICKET_AGENT_NEW', 'ticket_agent_new');
define('TICKET_MERGE', 'ticket_merge');
define('TICKET_ASSIGNED', 'ticket_assigned');
define('TICKET_OVERDUE', 'ticket_overdue');
define('TICKET_AGENT_CLOSED', 'ticket_agent_closed');
define('TICKET_SAVE_ONLY', 'ticket_save_only');
/* Default ticket statuses - Category - support*/
$supportTicketStatuses = [
trans('texts.new'),
trans('texts.open'),
trans('texts.closed'),
trans('texts.merged')
];
function uctrans($text, $data = [])
{
$locale = Session::get(SESSION_LOCALE);

View file

@ -30,26 +30,4 @@ class Domain
{
return 'maildelivery@' . static::getDomainFromId($id);
}
public static function getPostmarkTokenFromId($id)
{
switch($id)
{
case static::INVOICENINJA_COM:
return config('services.postmark_token');
case static::INVOICE_SERVICES:
return config('services.postmark_token_2');
}
}
public static function getSupportDomainFromId($id)
{
switch($id)
{
case static::INVOICENINJA_COM:
return config('ninja.tickets.ticket_support_domain');
case static::INVOICE_SERVICES:
return config('ninja.tickets.ticket_support_domain_2');
}
}
}

View file

@ -1,29 +0,0 @@
<?php
namespace App\Events;
use App\Models\Ticket;
use Illuminate\Queue\SerializesModels;
/**
* Class TicketUserViewed.
*/
class TicketUserViewed extends Event
{
use SerializesModels;
/**
* @var Ticket
*/
public $ticket;
/**
* Create a new event instance.
*
* @param Ticket $ticket
*/
public function __construct(Ticket $ticket)
{
$this->ticket = $ticket;
}
}

View file

@ -107,9 +107,9 @@ class AccountApiController extends BaseAPIController
$user = Auth::user();
$account = $user->account;
if ($createToken)
if ($createToken) {
$this->accountRepo->createTokens($user, $request->token_name);
}
$users = $this->accountRepo->findUsers($user, 'account.account_tokens');
$transformer = new UserAccountTransformer($account, $request->serializer, $request->token_name);

View file

@ -8,11 +8,9 @@ use App\Events\UserSettingsChanged;
use App\Events\UserSignedUp;
use App\Http\Requests\SaveClientPortalSettings;
use App\Http\Requests\SaveEmailSettings;
use App\Http\Requests\SaveTicketSettings;
use App\Http\Requests\UpdateAccountRequest;
use App\Models\Account;
use App\Models\AccountGateway;
use App\Models\AccountTicketSettings;
use App\Models\Affiliate;
use App\Models\Document;
use App\Models\Gateway;
@ -23,7 +21,6 @@ use App\Models\License;
use App\Models\PaymentTerm;
use App\Models\Product;
use App\Models\TaxRate;
use App\Models\TicketTemplate;
use App\Models\User;
use App\Models\AccountEmailSettings;
use App\Ninja\Mailers\ContactMailer;
@ -33,7 +30,6 @@ use App\Ninja\Repositories\ReferralRepository;
use App\Services\AuthService;
use App\Services\PaymentService;
use App\Services\TemplateService;
use Monolog\Handler\Curl\Util;
use Nwidart\Modules\Facades\Module;
use Auth;
use Cache;
@ -293,9 +289,7 @@ class AccountController extends BaseController
} elseif ($section == ACCOUNT_INVOICE_SETTINGS) {
return self::showInvoiceSettings();
} elseif ($section == ACCOUNT_IMPORT_EXPORT) {
return View::make('accounts.import_export', [
'title' => trans('texts.import_export'),
]);
return View::make('accounts.import_export', ['title' => trans('texts.import_export')]);
} elseif ($section == ACCOUNT_MANAGEMENT) {
return self::showAccountManagement();
} elseif ($section == ACCOUNT_INVOICE_DESIGN || $section == ACCOUNT_CUSTOMIZE_DESIGN) {
@ -308,8 +302,6 @@ class AccountController extends BaseController
return self::showProducts();
} elseif ($section === ACCOUNT_TAX_RATES) {
return self::showTaxRates();
} elseif ($section === ACCOUNT_TICKETS) {
return self::showTickets();
} elseif ($section === ACCOUNT_PAYMENT_TERMS) {
return self::showPaymentTerms();
} elseif ($section === ACCOUNT_SYSTEM_SETTINGS) {
@ -518,29 +510,11 @@ class AccountController extends BaseController
$data = [
'account' => Auth::user()->account,
'title' => trans('texts.product_library'),
];
return View::make('accounts.products', $data);
}
/**
* @return mixed
*/
private function showTickets()
{
$data = [
'account' => Auth::user()->account,
'account_ticket_settings' => Auth::user()->account->account_ticket_settings,
'templates' => TicketTemplate::scope()->get(),
'title' => trans('texts.ticket_settings'),
'section' => ACCOUNT_TICKETS,
];
return View::make('accounts.tickets', $data);
}
/**
* @return \Illuminate\Contracts\View\View
*/
@ -659,7 +633,6 @@ class AccountController extends BaseController
$data['invoiceFonts'] = Cache::get('fonts');
$data['section'] = $section;
$data['pageSizes'] = array_combine(InvoiceDesign::$pageSizes, InvoiceDesign::$pageSizes);
$data['showModuleSettings'] = Utils::hasModuleSettings();
$design = false;
foreach ($data['invoiceDesigns'] as $item) {
@ -721,14 +694,8 @@ class AccountController extends BaseController
'account' => $account,
'products' => Product::scope()->orderBy('product_key')->get(),
'gateway_types' => $options,
];
if (Utils::isSelfHost()) {
$js = $account->client_view_js ? $account->client_view_js : '';
$data['client_view_js'] = $js;
}
return View::make('accounts.client_portal', $data);
}
@ -752,7 +719,6 @@ class AccountController extends BaseController
];
}
$data['title'] = trans('texts.email_templates');
$data['showModuleSettings'] = Utils::hasModuleSettings();
return View::make('accounts.templates_and_reminders', $data);
}
@ -820,7 +786,6 @@ class AccountController extends BaseController
$user->save();
$account->live_preview = Input::get('live_preview') ? true : false;
$account->realtime_preview = Input::get('realtime_preview') ? true : false;
// Automatically disable live preview when using a large font
$fonts = Cache::get('fonts')->filter(function ($font) use ($account) {
@ -891,7 +856,6 @@ class AccountController extends BaseController
$account->fill($request->all());
$account->client_view_css = $request->client_view_css;
$account->client_view_js = $request->client_view_js;
$account->subdomain = $request->subdomain;
$account->iframe_url = $request->iframe_url;
$account->is_custom_domain = $request->is_custom_domain;
@ -941,29 +905,21 @@ class AccountController extends BaseController
$account->account_email_settings->$bodyField = ($body == $account->getDefaultEmailTemplate($type) ? null : $body);
}
foreach ([TEMPLATE_REMINDER1, TEMPLATE_REMINDER2, TEMPLATE_REMINDER3, TEMPLATE_QUOTE_REMINDER1, TEMPLATE_QUOTE_REMINDER2, TEMPLATE_QUOTE_REMINDER3] as $type) {
foreach ([TEMPLATE_REMINDER1, TEMPLATE_REMINDER2, TEMPLATE_REMINDER3] as $type) {
$enableField = "enable_{$type}";
$account->account_email_settings->$enableField = Input::get($enableField) ? true : false;
$account->account_email_settings->{"num_days_{$type}"} = Input::get("num_days_{$type}");
$account->account_email_settings->{"field_{$type}"} = Input::get("field_{$type}");
$account->account_email_settings->{"direction_{$type}"} = Input::get("field_{$type}") == REMINDER_FIELD_INVOICE_DATE ? REMINDER_DIRECTION_AFTER : Input::get("direction_{$type}");
$account->$enableField = Input::get($enableField) ? true : false;
$account->{"num_days_{$type}"} = Input::get("num_days_{$type}");
$account->{"field_{$type}"} = Input::get("field_{$type}");
$account->{"direction_{$type}"} = Input::get("field_{$type}") == REMINDER_FIELD_INVOICE_DATE ? REMINDER_DIRECTION_AFTER : Input::get("direction_{$type}");
$number = preg_replace('/[^0-9]/', '', $type);
if (strpos($type, 'quote') !== false) {
$account->account_email_settings->{"late_fee_quote{$number}_amount"} = Input::get("late_fee_quote{$number}_amount");
$account->account_email_settings->{"late_fee_quote{$number}_percent"} = Input::get("late_fee_quote{$number}_percent");
} else {
$account->account_email_settings->{"late_fee{$number}_amount"} = Input::get("late_fee{$number}_amount");
$account->account_email_settings->{"late_fee{$number}_percent"} = Input::get("late_fee{$number}_percent");
}
$account->account_email_settings->{"late_fee{$number}_amount"} = Input::get("late_fee{$number}_amount");
$account->account_email_settings->{"late_fee{$number}_percent"} = Input::get("late_fee{$number}_percent");
}
$account->account_email_settings->enable_reminder4 = Input::get('enable_reminder4') ? true : false;
$account->enable_reminder4 = Input::get('enable_reminder4') ? true : false;
$account->account_email_settings->frequency_id_reminder4 = Input::get('frequency_id_reminder4');
$account->account_email_settings->enable_quote_reminder4 = Input::get('enable_quote_reminder4') ? true : false;
$account->account_email_settings->frequency_id_quote_reminder4 = Input::get('frequency_id_quote_reminder4');
$account->save();
$account->account_email_settings->save();
@ -973,29 +929,6 @@ class AccountController extends BaseController
return Redirect::to('settings/'.ACCOUNT_TEMPLATES_AND_REMINDERS);
}
/**
* @return \Illuminate\Http\RedirectResponse
*/
public function saveTickets(SaveTicketSettings $request)
{
$account_ticket_settings = Auth::user()->account->account_ticket_settings;
$account_ticket_settings->fill($request->all());
$account_ticket_settings->save();
Session::flash('message', trans('texts.updated_settings'));
return Redirect::to('settings/'.ACCOUNT_TICKETS);
}
public function checkUniqueLocalPart()
{
if(AccountTicketSettings::checkUniqueLocalPart(Input::get('support_email_local_part'), Auth::user()->account))
return RESULT_SUCCESS;
else
return RESULT_FAILURE;
}
/**
* @return \Illuminate\Http\RedirectResponse
*/
@ -1066,8 +999,6 @@ class AccountController extends BaseController
$account->quote_terms = Input::get('quote_terms');
$account->auto_convert_quote = Input::get('auto_convert_quote');
$account->auto_archive_quote = Input::get('auto_archive_quote');
$account->require_approve_quote = Input::get('require_approve_quote');
$account->allow_approve_expired_quote = Input::get('allow_approve_expired_quote');
$account->auto_archive_invoice = Input::get('auto_archive_invoice');
$account->auto_email_invoice = Input::get('auto_email_invoice');
$account->recurring_invoice_number_prefix = Input::get('recurring_invoice_number_prefix');
@ -1080,7 +1011,6 @@ class AccountController extends BaseController
$account->credit_number_pattern = trim(Input::get('credit_number_pattern'));
$account->reset_counter_frequency_id = Input::get('reset_counter_frequency_id');
$account->reset_counter_date = $account->reset_counter_frequency_id ? Utils::toSqlDate(Input::get('reset_counter_date')) : null;
$account->custom_fields_options = request()->custom_fields_options;
if (Input::has('recurring_hour')) {
$account->recurring_hour = Input::get('recurring_hour');
@ -1272,7 +1202,6 @@ class AccountController extends BaseController
*/
public function saveUserDetails()
{
/** @var \App\Models\User $user */
$user = Auth::user();
$email = trim(strtolower(Input::get('email')));
@ -1302,7 +1231,6 @@ class AccountController extends BaseController
$user->email = $email;
$user->phone = trim(Input::get('phone'));
$user->dark_mode = Input::get('dark_mode');
$user->signature = Input::get('signature');
if (! Auth::user()->is_admin) {
$user->notify_sent = Input::get('notify_sent');
@ -1322,8 +1250,6 @@ class AccountController extends BaseController
}
}
$this->saveUserAvatar(Input::file('avatar'), $user);
$user->save();
event(new UserSettingsChanged());
@ -1333,89 +1259,6 @@ class AccountController extends BaseController
}
}
/**
* @param $avatar
* @param $user
*/
private function saveUserAvatar($avatar, $user)
{
/* Logo image file */
if ($uploaded = $avatar) {
$path = $avatar->getRealPath();
$disk = $user->getAvatarDisk();
$extension = strtolower($uploaded->getClientOriginalExtension());
if (empty(Document::$types[$extension]) && ! empty(Document::$extraExtensions[$extension])) {
$documentType = Document::$extraExtensions[$extension];
} else {
$documentType = $extension;
}
if (! in_array($documentType, ['jpeg', 'png', 'gif'])) {
Session::flash('warning', 'Unsupported file type');
} else {
$documentTypeData = Document::$types[$documentType];
$filePath = $uploaded->path();
$size = filesize($filePath);
if ($size / 1000 > MAX_DOCUMENT_SIZE) {
Session::flash('error', trans('texts.logo_warning_too_large'));
} else {
if ($documentType != 'gif') {
$user->avatar = str_random(21).'.'.$documentType;
try {
$imageSize = getimagesize($filePath);
$user->avatar_width = $imageSize[0];
$user->avatar_height = $imageSize[1];
$user->avatar_size = $size;
// make sure image isn't interlaced
if (extension_loaded('fileinfo')) {
$image = Image::make($path);
$image->interlace(false);
$imageStr = (string) $image->encode($documentType);
$disk->put($user->avatar, $imageStr);
$user->avatar_size = strlen($imageStr);
} else {
if (Utils::isInterlaced($filePath)) {
$user->clearAvatar();
Session::flash('error', trans('texts.logo_warning_invalid'));
} else {
$stream = fopen($filePath, 'r');
$disk->getDriver()->putStream($user->avatar, $stream, ['mimetype' => $documentTypeData['mime']]);
fclose($stream);
}
}
} catch (Exception $exception) {
$user->clearAvatar();
Session::flash('error', trans('texts.logo_warning_invalid'));
}
} else {
if (extension_loaded('fileinfo')) {
$user->avatar = str_random(32).'.png';
$image = Image::make($path);
$image = Image::canvas($image->width(), $image->height(), '#FFFFFF')->insert($image);
$imageStr = (string) $image->encode('png');
$disk->put($user->avatar, $imageStr);
$user->avatar_size = strlen($imageStr);
$user->avatar_width = $image->width();
$user->avatar_height = $image->height();
} else {
Session::flash('error', trans('texts.logo_warning_fileinfo'));
}
}
}
}
$user->save();
}
}
/**
* @return \Illuminate\Http\RedirectResponse
*/
@ -1484,28 +1327,6 @@ class AccountController extends BaseController
return Redirect::to('settings/'.ACCOUNT_COMPANY_DETAILS);
}
/**
* @return mixed
*/
public function removeAvatar()
{
$user = Auth::user();
if (! Utils::isNinjaProd() && $user->hasAvatar()) {
$user->getAvatarDisk()->delete($user->avatar);
}
$user->avatar = null;
$user->avatar_size = null;
$user->avatar_width = null;
$user->avatar_height = null;
$user->save();
Session::flash('message', trans('texts.removed_logo'));
return Redirect::to('settings/'.ACCOUNT_USER_DETAILS);
}
/**
* @return string
*/

View file

@ -106,9 +106,6 @@ class ClientController extends BaseController
if ($user->can('create', ENTITY_RECURRING_INVOICE)) {
$actionLinks[] = ['label' => trans('texts.new_recurring_invoice'), 'url' => URL::to('/recurring_invoices/create/'.$client->public_id)];
}
if ($user->can('create', ENTITY_RECURRING_QUOTE)) {
$actionLinks[] = ['label' => trans('texts.new_recurring_quote'), 'url' => URL::to('/recurring_quotes/create/'.$client->public_id)];
}
if (! empty($actionLinks)) {
$actionLinks[] = \DropdownButton::DIVIDER;
@ -136,7 +133,6 @@ class ClientController extends BaseController
'credit' => $client->getTotalCredit(),
'title' => trans('texts.view_client'),
'hasRecurringInvoices' => $account->isModuleEnabled(ENTITY_RECURRING_INVOICE) && Invoice::scope()->recurring()->withArchived()->whereClientId($client->id)->count() > 0,
'hasRecurringQuotes' => $account->isModuleEnabled(ENTITY_RECURRING_INVOICE) && Invoice::scope()->recurringQuote()->withArchived()->whereClientId($client->id)->count() > 0,
'hasQuotes' => $account->isModuleEnabled(ENTITY_QUOTE) && Invoice::scope()->quotes()->withArchived()->whereClientId($client->id)->count() > 0,
'hasTasks' => $account->isModuleEnabled(ENTITY_TASK) && Task::scope()->withArchived()->whereClientId($client->id)->count() > 0,
'hasExpenses' => $account->isModuleEnabled(ENTITY_EXPENSE) && Expense::scope()->withArchived()->whereClientId($client->id)->count() > 0,

View file

@ -146,14 +146,13 @@ class ClientPortalController extends BaseController
}
}
$showApprove = ($invoice->isQuote() && $account->require_approve_quote) ? true: false;
$showApprove = $invoice->quote_invoice_id ? false : true;
if ($invoice->invoice_status_id >= INVOICE_STATUS_APPROVED) {
$showApprove = false;
}
$data += [
'account' => $account,
'approveRequired' => $account->require_approve_quote,
'showApprove' => $showApprove,
'showBreadcrumbs' => false,
'invoice' => $invoice->hidePrivateFields(),
@ -324,7 +323,7 @@ class ClientPortalController extends BaseController
->make();
}
public function recurringInvoiceIndex($quotes = false)
public function recurringInvoiceIndex()
{
if (! $contact = $this->getContact()) {
return $this->returnError();
@ -344,19 +343,12 @@ class ClientPortalController extends BaseController
$columns[] = 'auto_bill';
}
$title = trans('texts.recurring_invoices');
$entityType = ENTITY_RECURRING_INVOICE;
if ($quotes) {
$title = trans('texts.recurring_quotes');
$entityType = ENTITY_RECURRING_QUOTE;
}
$data = [
'color' => $color,
'account' => $account,
'client' => $client,
'title' => $title,
'entityType' => $entityType,
'title' => trans('texts.recurring_invoices'),
'entityType' => ENTITY_RECURRING_INVOICE,
'columns' => Utils::trans($columns),
'sortColumn' => 1,
];
@ -364,11 +356,6 @@ class ClientPortalController extends BaseController
return response()->view('public_list', $data);
}
public function recurringQuoteIndex()
{
return self::recurringInvoiceIndex(true);
}
public function invoiceIndex()
{
if (! $contact = $this->getContact()) {
@ -414,15 +401,6 @@ class ClientPortalController extends BaseController
return $this->invoiceRepo->getClientRecurringDatatable($contact->id);
}
public function recurringQuoteDatatable()
{
if (! $contact = $this->getContact()) {
return '';
}
return $this->invoiceRepo->getClientRecurringDatatable($contact->id, ENTITY_RECURRING_QUOTE);
}
public function paymentIndex()
{
if (! $contact = $this->getContact()) {
@ -524,7 +502,6 @@ class ClientPortalController extends BaseController
$data = [
'color' => $color,
'account' => $account,
'client' => $contact->client,
'title' => trans('texts.quotes'),
'entityType' => ENTITY_QUOTE,
'columns' => Utils::trans(['quote_number', 'quote_date', 'quote_total', 'due_date', 'status']),
@ -652,7 +629,7 @@ class ClientPortalController extends BaseController
return $this->documentRepo->getClientDatatable($contact->id, ENTITY_DOCUMENT, Input::get('sSearch'));
}
public function returnError($error = false)
private function returnError($error = false)
{
if (request()->phantomjs) {
abort(404);
@ -665,7 +642,7 @@ class ClientPortalController extends BaseController
]);
}
public function getContact()
private function getContact()
{
$contactKey = session('contact_key');
@ -1072,6 +1049,4 @@ class ClientPortalController extends BaseController
return view('clients.statement', $data);
}
}

View file

@ -1,202 +0,0 @@
<?php
namespace App\Http\Controllers;
use App\Http\Requests\UpdateClientPortalTicketRequest;
use App\Libraries\Utils;
use App\Models\Ticket;
use App\Ninja\Repositories\TicketRepository;
use App\Services\TicketService;
use Illuminate\Support\Facades\Redirect;
use Illuminate\Support\Facades\Session;
class ClientPortalTicketController extends ClientPortalController
{
/**
* @var TicketRepository
*/
private $ticketRepo;
/**
* @var TicketService
*/
private $ticketService;
/**
* ClientPortalTicketController constructor.
* @param TicketRepository $ticketRepo
* @param TicketService $ticketService
*/
public function __construct(TicketRepository $ticketRepo, TicketService $ticketService)
{
$this->ticketRepo = $ticketRepo;
$this->ticketService = $ticketService;
}
/**
* @return \Illuminate\Http\Response
*/
public function index()
{
$contact = $this->getContact();
if ((!$contact || (!$contact->account->enable_client_portal)))
return $this->returnError();
$account = $contact->account;
$data = [
'color' => $account->primary_color ? $account->primary_color : '#0b4d78',
'account' => $account,
'title' => trans('texts.tickets'),
'entityType' => ENTITY_TICKET,
'columns' => Utils::trans(['ticket_number', 'subject', 'created_at', 'status']),
'sortColumn' => 0,
];
return response()->view('public_list', $data);
}
/**
* @return bool
*/
public function ticketDatatable()
{
if (! $contact = $this->getContact())
return false;
return $this->ticketService->getClientDatatable($contact->client->id);
}
/**
* @param $invitationKey
* @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View
*/
public function viewTicket($invitationKey)
{
if (! $invitation = $this->ticketRepo->findInvitationByKey($invitationKey))
return $this->returnError(trans('texts.ticket_not_found'));
$account = $invitation->account;
$ticket = $invitation->ticket;
$data = [
'ticket' => $ticket,
'account' => $account,
'ticketInvitation' => $invitation,
];
return view('invited.ticket', $data);
}
/**
* @param $ticketid
* @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View
*/
public function view($ticketId)
{
if (! $contact = $this->getContact())
$this->returnError();
$account = $contact->account;
$ticket = Ticket::scope($ticketId, $account->id)
->with('comments', 'documents')
->first();
$data['method'] = 'PUT';
$data['entityType'] = ENTITY_TICKET;
$data = array_merge($data, self::getViewModel($contact, $ticket));
return view('tickets.portal.ticket_view', $data);
}
/**
*
*/
public function update(UpdateClientPortalTicketRequest $request)
{
$contact = $this->getContact();
$data = $request->input();
$data['document_ids'] = $request->document_ids;
$data['contact_key'] = $contact->contact_key;
$data['method'] = 'PUT';
$data['entityType'] = ENTITY_TICKET;
$data['action'] = TICKET_INBOUND_CONTACT_REPLY;
$ticket = $this->ticketService->save($data, $request->entity());
$ticket->load('documents');
if(!$ticket)
$this->returnError();
$data = array_merge($data, self::getViewModel($contact, $ticket));
Session::flash('message', trans('texts.updated_ticket'));
return view('tickets.portal.ticket_view', $data);
}
public function create()
{
if (! $contact = $this->getContact())
$this->returnError();
$data['method'] = 'POST';
$data['entityType'] = ENTITY_TICKET;
$data = array_merge($data, self::getViewModel($contact));
return view('tickets.portal.ticket_view', $data);
}
public function store(UpdateClientPortalTicketRequest $request)
{
if (! $contact = $this->getContact())
$this->returnError();
$data = $request->input();
$data['document_ids'] = $request->document_ids;
$data['contact_key'] = $contact->contact_key;
$data['action'] = TICKET_CLIENT_NEW;
$data['is_internal'] = 0;
$ticket = $this->ticketService->save($data, $request->entity());
Session::flash('message', trans('texts.updated_ticket'));
return Redirect::to('/client/tickets/'.$ticket->public_id);
}
private static function getViewModel($contact, $ticket = false)
{
return [
'color' => $contact->account->primary_color ? $contact->account->primary_color : '#0b4d78',
'ticket' => $ticket,
'contact' => $contact,
'account' => $contact->account,
'title' => $ticket ? trans('texts.ticket')." ".$ticket->ticket_number : trans('texts.new_ticket'),
'comments' => $ticket ? $ticket->comments() :null,
'url' => $ticket ? 'client/tickets/' . $ticket->public_id : 'client/tickets/create',
//'timezone' => $ticket ? $ticket->account->timezone->name : DEFAULT_TIMEZONE,
'datetimeFormat' => $contact->account->getMomentDateTimeFormat(),
'account_ticket_settings' => $contact->account->account_ticket_settings
];
}
}

View file

@ -3,11 +3,9 @@
namespace App\Http\Controllers;
use App\Models\Client;
use App\Models\Currency;
use App\Models\Expense;
use App\Ninja\Repositories\DashboardRepository;
use Auth;
use App\Libraries\MoneyUtils;
use Utils;
use View;
@ -36,7 +34,7 @@ class DashboardController extends BaseController
$metrics = $dashboardRepo->totals($accountId, $userId, $viewAll);
$paidToDate = $dashboardRepo->paidToDate($account, $userId, $viewAll);
$averageInvoice = $dashboardRepo->averages($account, $userId, $viewAll);
$balances = $dashboardRepo->balances($account, $userId, $viewAll);
$balances = $dashboardRepo->balances($accountId, $userId, $viewAll);
$activities = $dashboardRepo->activities($accountId, $userId, $viewAll);
$pastDue = $dashboardRepo->pastDue($accountId, $userId, $viewAll);
$upcoming = $dashboardRepo->upcoming($accountId, $userId, $viewAll);
@ -44,60 +42,6 @@ class DashboardController extends BaseController
$expenses = $dashboardRepo->expenses($account, $userId, $viewAll);
$tasks = $dashboardRepo->tasks($accountId, $userId, $viewAll);
// calculate paid to date totals
$paidToDateTotal = 0;
foreach($paidToDate as $item) {
$paidToDateTotal += ($item->value * $item->exchange_rate);
}
// calculate average invoice totals
$invoiceTotal = 0;
$invoiceTotalCount = 0;
foreach ($averageInvoice as $item) {
$invoiceTotalCount += $item->invoice_count;
if (! $item->exchange_rate) {
$invoiceTotal += $item->invoice_avg * $item->invoice_count;
continue;
}
$invoiceTotal += ($item->invoice_avg * $item->invoice_count / $item->exchange_rate);
}
$averageInvoiceTotal = $invoiceTotalCount ? ($invoiceTotal / $invoiceTotalCount) : 0;
// calculate balances totals
$balancesTotals = 0;
$currencies = [];
foreach ($balances as $item) {
if ($item->currency_id == $account->getCurrencyId()) {
$balancesTotals += $item->value;
continue;
}
if (! isset($currencies[$item->currency_id])) {
$currencies[$item->currency_id] = Currency::where('id', $item->currency_id)->firstOrFail();
}
try {
$balancesTotals += MoneyUtils::convert($item->value, $currencies[$item->currency_id]->code, $account->currency->code);
} catch (\Exception $e) {
Utils::logError($e);
$balancesTotals += $item->value;
}
}
// calculate expenses totals
$expensesTotals = 0;
foreach ($expenses as $item) {
if ($item->currency_id == $account->getCurrencyId()) {
$expensesTotals += $item->value;
continue;
}
$expensesTotals += ($item->value * $item->exchange_rate);
}
$showBlueVinePromo = false;
if ($user->is_admin && env('BLUEVINE_PARTNER_UNIQUE_ID')) {
$showBlueVinePromo = ! $account->company->bluevine_status
@ -123,14 +67,10 @@ class DashboardController extends BaseController
'account' => $user->account,
'user' => $user,
'paidToDate' => $paidToDate,
'paidToDateTotal' => $paidToDateTotal,
'balances' => $balances,
'balancesTotals' => $balancesTotals,
'averageInvoice' => $averageInvoice,
'averageInvoiceTotal' => $averageInvoiceTotal,
'invoicesSent' => $metrics ? $metrics->invoices_sent : 0,
'activeClients' => $metrics ? $metrics->active_clients : 0,
'invoiceExchangeRateMissing' => $account->getInvoiceExchangeRateCustomFieldIndex() ? false : true,
'activities' => $activities,
'pastDue' => $pastDue,
'upcoming' => $upcoming,
@ -140,7 +80,6 @@ class DashboardController extends BaseController
'showBreadcrumbs' => false,
'currencies' => $this->getCurrencyCodes(),
'expenses' => $expenses,
'expensesTotals' => $expensesTotals,
'tasks' => $tasks,
'showBlueVinePromo' => $showBlueVinePromo,
'showWhiteLabelExpired' => $showWhiteLabelExpired,

View file

@ -5,7 +5,6 @@ namespace App\Http\Controllers;
use App\Http\Requests\CreateDocumentRequest;
use App\Http\Requests\DocumentRequest;
use App\Http\Requests\UpdateDocumentRequest;
use App\Models\Contact;
use App\Models\Document;
use App\Ninja\Repositories\DocumentRepository;
use Redirect;
@ -57,7 +56,6 @@ class DocumentController extends BaseController
public function getPreview(DocumentRequest $request)
{
$document = $request->entity();
if (empty($document->preview)) {
@ -120,7 +118,6 @@ class DocumentController extends BaseController
'code' => 200,
];
}
return Response::json($response, 200);
}
}

View file

@ -54,7 +54,7 @@ class InvoiceApiController extends BaseAPIController
* response="default",
* description="an ""unexpected"" error"
* )
* );
* )
*/
public function index()
{
@ -104,7 +104,7 @@ class InvoiceApiController extends BaseAPIController
* response="default",
* description="an ""unexpected"" error"
* )
* );
* )
*/
public function show(InvoiceRequest $request)
{
@ -130,7 +130,7 @@ class InvoiceApiController extends BaseAPIController
* response="default",
* description="an ""unexpected"" error"
* )
* );
* )
*/
public function store(CreateInvoiceAPIRequest $request)
{
@ -455,7 +455,7 @@ class InvoiceApiController extends BaseAPIController
* response="default",
* description="an ""unexpected"" error"
* )
* );
* )
*/
public function destroy(UpdateInvoiceAPIRequest $request)
{

View file

@ -81,14 +81,6 @@ class InvoiceController extends BaseController
return $this->recurringInvoiceService->getDatatable($accountId, $clientPublicId, ENTITY_RECURRING_INVOICE, $search);
}
public function getRecurringQuotesDatatable($clientPublicId = null)
{
$accountId = Auth::user()->account_id;
$search = Input::get('sSearch');
return $this->recurringInvoiceService->getDatatable($accountId, $clientPublicId, ENTITY_RECURRING_QUOTE, $search);
}
public function edit(InvoiceRequest $request, $publicId, $clone = false)
{
$account = Auth::user()->account;
@ -109,6 +101,7 @@ class InvoiceController extends BaseController
$entityType = $clone == INVOICE_TYPE_STANDARD ? ENTITY_INVOICE : ENTITY_QUOTE;
$invoice->id = $invoice->public_id = null;
$invoice->is_public = false;
$invoice->is_recurring = $invoice->is_recurring && $clone == INVOICE_TYPE_STANDARD;
$invoice->invoice_type_id = $clone;
$invoice->invoice_number = $account->getNextNumber($invoice);
$invoice->due_date = null;
@ -145,11 +138,7 @@ class InvoiceController extends BaseController
'invoice_settings' => Auth::user()->hasFeature(FEATURE_INVOICE_SETTINGS),
];
$lastSent = null;
if($invoice->is_recurring && $invoice->last_sent_date)
{
$lastSent = ($invoice->subEntityType() == ENTITY_RECURRING_INVOICE) ? $invoice->recurring_invoices->last() : $invoice->recurring_quotes->last();
}
$lastSent = ($invoice->is_recurring && $invoice->last_sent_date) ? $invoice->recurring_invoices->last() : null;
if (! Auth::user()->hasPermission('view_client')) {
$clients = $clients->where('clients.user_id', '=', Auth::user()->id);
@ -210,10 +199,11 @@ class InvoiceController extends BaseController
return View::make('invoices.edit', $data);
}
public function create(InvoiceRequest $request, $clientPublicId = 0, $entityType = ENTITY_INVOICE)
public function create(InvoiceRequest $request, $clientPublicId = 0, $isRecurring = false)
{
$account = Auth::user()->account;
$entityType = $isRecurring ? ENTITY_RECURRING_INVOICE : ENTITY_INVOICE;
$clientId = null;
if ($request->client_id) {
@ -244,12 +234,7 @@ class InvoiceController extends BaseController
public function createRecurring(InvoiceRequest $request, $clientPublicId = 0)
{
return self::create($request, $clientPublicId, ENTITY_RECURRING_INVOICE);
}
public function createRecurringQuote(InvoiceRequest $request, $clientPublicId = 0)
{
return self::create($request, $clientPublicId, ENTITY_RECURRING_QUOTE);
return self::create($request, $clientPublicId, true);
}
private static function getViewModel($invoice)
@ -529,10 +514,6 @@ class InvoiceController extends BaseController
$entityType = ENTITY_RECURRING_INVOICE;
}
if (strpos(\Request::server('HTTP_REFERER'), 'recurring_quotes')) {
$entityType = ENTITY_RECURRING_QUOTE;
}
return $this->returnBulk($entityType, $action, $ids);
}

View file

@ -2,21 +2,43 @@
namespace App\Http\Controllers\Migration;
use App\Models\Credit;
use App\Models\User;
use App\Models\Invoice;
use App\Models\Payment;
use App\Models\Product;
use App\Models\TaxRate;
use App\Http\Controllers\BaseController;
use App\Http\Requests\MigrationAuthRequest;
use App\Http\Requests\MigrationCompaniesRequest;
use App\Http\Requests\MigrationEndpointRequest;
use App\Http\Requests\MigrationTypeRequest;
use App\Libraries\Utils;
use Illuminate\Http\Request;
use App\Models\Account;
use App\Services\Migration\AuthService;
use App\Services\Migration\CompanyService;
use App\Services\Migration\CompleteService;
use App\Traits\GenerateMigrationResources;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Storage;
use App\Http\Controllers\BaseController;
class StepsController extends BaseController
{
private $account;
use GenerateMigrationResources;
public function __construct()
{
$this->middleware('migration');
}
private $access = [
'auth' => [
'steps' => ['MIGRATION_TYPE'],
'redirect' => '/migration/start',
],
'endpoint' => [
'steps' => ['MIGRATION_TYPE'],
'redirect' => '/migration/start',
],
'companies' => [
'steps' => ['MIGRATION_TYPE', 'MIGRATION_ACCOUNT_TOKEN'],
'redirect' => '/migration/auth',
],
];
/**
* @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View
@ -39,573 +61,237 @@ class StepsController extends BaseController
return view('migration.download');
}
public function handleType(MigrationTypeRequest $request)
{
session()->put('MIGRATION_TYPE', $request->option);
if ($request->option == 0) {
session()->put('MIGRATION_ENDPOINT', 'https://invoicing.co');
return redirect(
url('/migration/auth')
);
// return redirect(
// url('/migration/endpoint')
// );
}
return redirect(
url('/migration/endpoint')
);
}
public function endpoint()
{
if ($this->shouldGoBack('endpoint')) {
return redirect(
url($this->access['endpoint']['redirect'])
);
}
return view('migration.endpoint');
}
public function handleEndpoint(MigrationEndpointRequest $request)
{
if ($this->shouldGoBack('endpoint')) {
return redirect(
url($this->access['endpoint']['redirect'])
);
}
session()->put('MIGRATION_ENDPOINT', rtrim($request->endpoint,'/'));
return redirect(
url('/migration/auth')
);
}
public function auth()
{
if ($this->shouldGoBack('auth')) {
return redirect(
url($this->access['auth']['redirect'])
);
}
return view('migration.auth');
}
public function handleAuth(MigrationAuthRequest $request)
{
if ($this->shouldGoBack('auth')) {
return redirect(
url($this->access['auth']['redirect'])
);
}
if (auth()->user()->email !== $request->email) {
return back()->with('responseErrors', [trans('texts.cross_migration_message')]);
}
$authentication = (new AuthService($request->email, $request->password, $request->has('api_secret') ? $request->api_secret : null))
->endpoint(session('MIGRATION_ENDPOINT'))
->start();
if ($authentication->isSuccessful()) {
session()->put('MIGRATION_ACCOUNT_TOKEN', $authentication->getAccountToken());
session()->put('MIGRAITON_API_SECRET', $authentication->getApiSecret());
return redirect(
url('/migration/companies')
);
}
return back()->with('responseErrors', $authentication->getErrors());
}
public function companies()
{
if ($this->shouldGoBack('companies')) {
return redirect(
url($this->access['companies']['redirect'])
);
}
$companyService = (new CompanyService())
->start();
if ($companyService->isSuccessful()) {
return view('migration.companies', ['companies' => $companyService->getCompanies()]);
}
return response()->json([
'message' => 'Oops, looks like something failed. Please try again.'
], 500);
}
public function handleCompanies(MigrationCompaniesRequest $request)
{
if ($this->shouldGoBack('companies')) {
return redirect(
url($this->access['companies']['redirect'])
);
}
try {
$migrationData = $this->generateMigrationData($request->all());
$completeService = (new CompleteService(session('MIGRATION_ACCOUNT_TOKEN')))
->data($migrationData)
->endpoint(session('MIGRATION_ENDPOINT'))
->start();
}
finally {
if ($completeService->isSuccessful()) {
return view('migration.completed');
}
return view('migration.completed', ['customMessage' => $completeService->getErrors()[0]]);
}
}
public function completed()
{
return view('migration.completed');
}
/**
* ==================================
* Rest of functions that are used as 'actions', not controller methods.
* ==================================
*/
public function shouldGoBack(string $step)
{
$redirect = true;
foreach ($this->access[$step]['steps'] as $step) {
if (session()->has($step)) {
$redirect = false;
} else {
$redirect = true;
}
}
return $redirect;
}
/**
* Handle data downloading for the migration.
*
* @return \Illuminate\Http\JsonResponse
* @return string
*/
public function handleDownload()
public function generateMigrationData(array $data): array
{
$this->account = Auth::user()->account;
set_time_limit(0);
$date = date('Y-m-d');
$accountKey = $this->account->account_key;
$migrationData = [];
$output = fopen('php://output', 'w') or Utils::fatalError();
foreach ($data['companies'] as $company) {
$account = Account::where('account_key', $company['id'])->firstOrFail();
$fileName = "{$accountKey}-{$date}-invoiceninja";
$this->account = $account;
$data = [
'company' => $this->getCompany(),
'users' => $this->getUsers(),
'tax_rates' => $this->getTaxRates(),
'clients' => $this->getClients(),
'products' => $this->getProducts(),
'invoices' => $this->getInvoices(),
'quotes' => $this->getQuotes(),
'payments' => array_merge($this->getPayments(), $this->getCredits()),
'credits' => $this->getCreditsNotes(),
];
$date = date('Y-m-d');
$accountKey = $this->account->account_key;
$file = storage_path("{$fileName}.zip");
$output = fopen('php://output', 'w') or Utils::fatalError();
$zip = new \ZipArchive();
$zip->open($file, \ZipArchive::CREATE | \ZipArchive::OVERWRITE);
$zip->addFromString('migration.json', json_encode($data));
$zip->close();
$fileName = "{$accountKey}-{$date}-invoiceninja";
header('Content-Type: application/zip');
header('Content-Length: ' . filesize($file));
header("Content-Disposition: attachment; filename={$fileName}.zip");
readfile($file);
unlink($file);
return response()->json($data);
}
/**
* Export company and map to the v2 fields.
*
* @return array
*/
protected function getCompany()
{
return [
'account_id' => $this->account->id,
'industry_id' => $this->account->industry_id,
'ip' => $this->account->ip,
'company_key' => $this->account->account_key,
'logo' => $this->account->logo,
'convert_products' => $this->account->convert_products,
'fill_products' => $this->account->fill_products,
'update_products' => $this->account->update_products,
'show_product_details' => $this->account->show_product_notes,
'custom_surcharge_taxes1' => $this->account->custom_invoice_taxes1,
'custom_surcharge_taxes2' => $this->account->custom_invoice_taxes2,
'enable_invoice_quantity' => !$this->account->hide_quantity,
'subdomain' => $this->account->subdomain,
'size_id' => $this->account->size_id,
'enable_modules' => $this->account->enabled_modules,
'custom_fields' => $this->account->custom_fields,
//'uses_inclusive_taxes' => $this->account->inclusive_taxes,
'created_at' => $this->account->created_at ? $this->account->created_at->toDateString() : null,
'updated_at' => $this->account->updated_at ? $this->account->updated_at->toDateString() : null,
'settings' => $this->getCompanySettings(),
];
}
public function getCompanySettings()
{
// In v1: custom_invoice_taxes1 & custom_invoice_taxes2, v2: 'invoice_taxes'. What do to with this?
// V1: invoice_number_prefix, v2: invoice_number_pattern.. same with quote_number, client_number,
return [
'timezone_id' => $this->account->timezone_id,
'date_format_id' => $this->account->date_format_id,
'currency_id' => $this->account->currency_id,
'name' => $this->account->name,
'address1' => $this->account->address1,
'address2' => $this->account->address2,
'city' => $this->account->city,
'state' => $this->account->state,
'postal_code' => $this->account->postal_code,
'country_id' => $this->account->country_id,
'invoice_terms' => $this->account->invoice_terms,
'enabled_item_tax_rates' => $this->account->invoice_item_taxes,
'invoice_design_id' => $this->account->invoice_design_id,
'phone' => $this->account->work_phone,
'email' => $this->account->work_email,
'language_id' => $this->account->language_id,
'custom_value1' => $this->account->custom_value1,
'custom_value2' => $this->account->custom_value2,
'hide_paid_to_date' => $this->account->hide_paid_to_date,
'vat_number' => $this->account->vat_number,
'shared_invoice_quote_counter' => $this->account->share_counter, // @verify,
'id_number' => $this->account->id_number,
'invoice_footer' => $this->account->invoice_footer,
'pdf_email_attachment' => $this->account->pdf_email_attachment,
'font_size' => $this->account->font_size,
'invoice_labels' => $this->account->invoice_labels,
'military_time' => $this->account->military_time,
'invoice_number_pattern' => $this->account->invoice_number_pattern,
'quote_number_pattern' => $this->account->quote_number_pattern,
'quote_terms' => $this->account->quote_terms,
'website' => $this->account->website,
'auto_convert_quote' => $this->account->auto_convert_quote,
'all_pages_footer' => $this->account->all_pages_footer,
'all_pages_header' => $this->account->all_pages_header,
'show_currency_code' => $this->account->show_currency_code,
'enable_client_portal_password' => $this->account->enable_portal_password,
'send_portal_password' => $this->account->send_portal_password,
'recurring_number_prefix' => $this->account->recurring_invoice_number_prefix, // @verify
'enable_client_portal' => $this->account->enable_client_portal,
'invoice_fields' => $this->account->invoice_fields,
'company_logo' => $this->account->logo,
'embed_documents' => $this->account->invoice_embed_documents,
'document_email_attachment' => $this->account->document_email_attachment,
'enable_client_portal_dashboard' => $this->account->enable_client_portal_dashboard,
'page_size' => $this->account->page_size,
'show_accept_invoice_terms' => $this->account->show_accept_invoice_terms,
'show_accept_quote_terms' => $this->account->show_accept_quote_terms,
'require_invoice_signature' => $this->account->require_invoice_signature,
'require_quote_signature' => $this->account->require_quote_signature,
'client_number_counter' => $this->account->client_number_counter,
'client_number_pattern' => $this->account->client_number_pattern,
'payment_terms' => $this->account->payment_terms,
'reset_counter_frequency_id' => $this->account->reset_counter_frequency_id,
'payment_type_id' => $this->account->payment_type_id,
'reset_counter_date' => $this->account->reset_counter_date,
'tax_name1' => $this->account->tax_name1,
'tax_rate1' => $this->account->tax_rate1,
'tax_name2' => $this->account->tax_name2,
'tax_rate2' => $this->account->tax_rate2,
'quote_design_id' => $this->account->quote_design_id,
'credit_number_counter' => $this->account->credit_number_counter,
'credit_number_pattern' => $this->account->credit_number_pattern,
'default_task_rate' => $this->account->task_rate,
'inclusive_taxes' => $this->account->inclusive_taxes,
'signature_on_pdf' => $this->account->signature_on_pdf,
'ubl_email_attachment' => $this->account->ubl_email_attachment,
'auto_archive_invoice' => $this->account->auto_archive_invoice,
'auto_archive_quote' => $this->account->auto_archive_quote,
'auto_email_invoice' => $this->account->auto_email_invoice,
];
}
/**
* @return array
*/
public function getTaxRates()
{
$rates = TaxRate::where('account_id', $this->account->id)
->withTrashed()
->get();
$transformed = [];
foreach ($rates as $rate) {
$transformed[] = [
'name' => $rate->name,
'rate' => $rate->rate,
'company_id' => $rate->account_id,
'user_id' => $rate->user_id,
'created_at' => $rate->created_at ? $rate->created_at->toDateString() : null,
'updated_at' => $rate->updated_at ? $rate->updated_at->toDateString() : null,
'deleted_at' => $rate->deleted_at ? $rate->deleted_at->toDateString() : null,
$localMigrationData['data'] = [
'account' => $this->getAccount(),
'company' => $this->getCompany(),
'users' => $this->getUsers(),
'tax_rates' => $this->getTaxRates(),
'payment_terms' => $this->getPaymentTerms(),
'clients' => $this->getClients(),
'company_gateways' => $this->getCompanyGateways(),
'client_gateway_tokens' => $this->getClientGatewayTokens(),
'vendors' => $this->getVendors(),
'projects' => $this->getProjects(),
'products' => $this->getProducts(),
'credits' => $this->getCreditsNotes(),
'invoices' => $this->getInvoices(),
'recurring_invoices' => $this->getRecurringInvoices(),
'quotes' => $this->getQuotes(),
'payments' => array_merge($this->getPayments(), $this->getCredits()),
'documents' => $this->getDocuments(),
'expense_categories' => $this->getExpenseCategories(),
'task_statuses' => $this->getTaskStatuses(),
'expenses' => $this->getExpenses(),
'tasks' => $this->getTasks(),
'documents' => $this->getDocuments(),
'ninja_tokens' => $this->getNinjaToken(),
];
$localMigrationData['force'] = array_key_exists('force', $company);
Storage::makeDirectory('migrations');
$file = Storage::path("migrations/{$fileName}.zip");
//$file = storage_path("migrations/{$fileName}.zip");
ksort($localMigrationData);
$zip = new \ZipArchive();
$zip->open($file, \ZipArchive::CREATE | \ZipArchive::OVERWRITE);
$zip->addFromString('migration.json', json_encode($localMigrationData, JSON_PRETTY_PRINT));
$zip->close();
$localMigrationData['file'] = $file;
$migrationData[] = $localMigrationData;
}
return $transformed;
}
return $migrationData;
/**
* @return array
*/
protected function getClients()
{
$clients = [];
foreach ($this->account->clients()->withTrashed()->get() as $client) {
$clients[] = [
'id' => $client->id,
'company_id' => $client->account_id,
'user_id' => $client->user_id,
'name' => $client->name,
'balance' => $client->balance,
'paid_to_date' => $client->paid_to_date,
'address1' => $client->address1,
'address2' => $client->address2,
'city' => $client->city,
'state' => $client->state,
'postal_code' => $client->postal_code,
'country_id' => $client->country_id,
'phone' => $client->work_phone,
'private_notes' => $client->private_notes,
'website' => $client->website,
'industry_id' => $client->industry_id,
'size_id' => $client->size_id,
'is_deleted' => $client->is_deleted,
'vat_number' => $client->vat_number,
'id_number' => $client->id_number,
'custom_value1' => $client->custom_value1,
'custom_value2' => $client->custom_value2,
'shipping_address1' => $client->shipping_address1,
'shipping_address2' => $client->shipping_address2,
'shipping_city' => $client->shipping_city,
'shipping_state' => $client->shipping_state,
'shipping_postal_code' => $client->shipping_postal_code,
'shipping_country_id' => $client->shipping_country_id,
'contacts' => $this->getClientContacts($client->contacts),
];
}
return $clients;
}
/**
* @param $contacts
* @return array
*/
protected function getClientContacts($contacts)
{
$transformed = [];
foreach($contacts as $contact) {
$transformed[] = [
'id' => $contact->id,
'company_id' => $contact->account_id,
'user_id' => $contact->user_id,
'client_id' => $contact->client_id,
'first_name' => $contact->first_name,
'last_name' => $contact->last_name,
'phone' => $contact->phone,
'custom_value1' => $contact->custom_value1,
'custom_value2' => $contact->custom_value2,
'email' => $contact->email,
'is_primary' => $contact->is_primary,
'send_invoice' => $contact->send_invoice,
'confirmed' => $contact->confirmation_token ? true : false,
'last_login' => $contact->last_login,
'password' => $contact->password,
'remember_token' => $contact->remember_token,
'contact_key' => $contact->contact_key,
];
}
return $transformed;
}
/**
* @return array
*/
protected function getProducts()
{
$products = Product::where('account_id', $this->account->id)
->withTrashed()
->get();
$transformed = [];
foreach ($products as $product) {
$transformed[] = [
'company_id' => $product->account_id,
'user_id' => $product->user_id,
'custom_value1' => $product->custom_value1,
'custom_value2' => $product->custom_value2,
'product_key' => $product->product_key,
'notes' => $product->notes,
'cost' => $product->cost,
'quantity' => $product->qty,
'tax_name1' => $product->tax_name1,
'tax_name2' => $product->tax_name2,
'tax_rate1' => $product->tax_rate1,
'tax_rate2' => $product->tax_rate2,
'created_at' => $product->created_at ? $product->created_at->toDateString() : null,
'updated_at' => $product->updated_at ? $product->updated_at->toDateString() : null,
'deleted_at' => $product->deleted_at ? $product->deleted_at->toDateString() : null,
];
}
return $transformed;
}
/**
* @return array
*/
public function getUsers()
{
$users = User::where('account_id', $this->account->id)
->withTrashed()
->get();
$transformed = [];
foreach ($users as $user) {
$transformed[] = [
'id' => $user->id,
'first_name' => $user->first_name,
'last_name' => $user->last_name,
'phone' => $user->phone,
'email' => $user->email,
'confirmation_code' => $user->confirmation_code,
'failed_logins' => $user->failed_logins,
'referral_code' => $user->referral_code,
'oauth_user_id' => $user->oauth_user_id,
'oauth_provider_id' => $user->oauth_provider_id,
'google_2fa_secret' => $user->google_2fa_secret,
'accepted_terms_version' => $user->accepted_terms_version,
'password' => $user->password,
'remember_token' => $user->remember_token,
'created_at' => $user->created_at ? $user->created_at->toDateString() : null,
'updated_at' => $user->updated_at ? $user->updated_at->toDateString() : null,
'deleted_at' => $user->deleted_at ? $user->deleted_at->toDateString() : null,
];
}
return $transformed;
}
private function getCreditsNotes()
{
$credits = [];
foreach ($this->account->invoices()->where('amount', '<', '0')->withTrashed()->get() as $credit) {
$credits[] = [
'id' => $credit->id,
'client_id' => $credit->client_id,
'user_id' => $credit->user_id,
'company_id' => $credit->account_id,
'status_id' => $credit->invoice_status_id,
'design_id' => $credit->invoice_design_id,
'number' => $credit->invoice_number,
'discount' => $credit->discount,
'is_amount_discount' => $credit->is_amount_discount ?: false,
'po_number' => $credit->po_number,
'date' => $credit->invoice_date,
'last_sent_date' => $credit->last_sent_date,
'due_date' => $credit->due_date,
'is_deleted' => $credit->is_deleted,
'footer' => $credit->invoice_footer,
'public_notes' => $credit->public_notes,
'private_notes' => $credit->private_notes,
'terms' => $credit->terms,
'tax_name1' => $credit->tax_name1,
'tax_name2' => $credit->tax_name2,
'tax_rate1' => $credit->tax_rate1,
'tax_rate2' => $credit->tax_rate2,
'custom_value1' => $credit->custom_value1,
'custom_value2' => $credit->custom_value2,
'next_send_date' => null,
'amount' => $credit->amount,
'balance' => $credit->balance,
'partial' => $credit->partial,
'partial_due_date' => $credit->partial_due_date,
'line_items' => $this->getInvoiceItems($credit->invoice_items),
'created_at' => $credit->created_at ? $credit->created_at->toDateString() : null,
'updated_at' => $credit->updated_at ? $credit->updated_at->toDateString() : null,
'deleted_at' => $credit->deleted_at ? $credit->deleted_at->toDateString() : null,
];
}
return $credits;
}
/**
* @return array
*/
protected function getInvoices()
{
$invoices = [];
foreach ($this->account->invoices()->where('amount', '>=', '0')->withTrashed()->get() as $invoice) {
$invoices[] = [
'id' => $invoice->id,
'client_id' => $invoice->client_id,
'user_id' => $invoice->user_id,
'company_id' => $invoice->account_id,
'status_id' => $invoice->invoice_status_id,
'design_id' => $invoice->invoice_design_id,
'number' => $invoice->invoice_number,
'discount' => $invoice->discount,
'is_amount_discount' => $invoice->is_amount_discount ?: false,
'po_number' => $invoice->po_number,
'date' => $invoice->invoice_date,
'last_sent_date' => $invoice->last_sent_date,
'due_date' => $invoice->due_date,
'is_deleted' => $invoice->is_deleted,
'footer' => $invoice->invoice_footer,
'public_notes' => $invoice->public_notes,
'private_notes' => $invoice->private_notes,
'uses_inclusive_taxes' => $this->account->inclusive_taxes,
'terms' => $invoice->terms,
'tax_name1' => $invoice->tax_name1,
'tax_name2' => $invoice->tax_name2,
'tax_rate1' => $invoice->tax_rate1,
'tax_rate2' => $invoice->tax_rate2,
'custom_value1' => $invoice->custom_value1,
'custom_value2' => $invoice->custom_value2,
'next_send_date' => null,
'amount' => $invoice->amount,
'balance' => $invoice->balance,
'partial' => $invoice->partial,
'partial_due_date' => $invoice->partial_due_date,
'line_items' => $this->getInvoiceItems($invoice->invoice_items),
'created_at' => $invoice->created_at ? $invoice->created_at->toDateString() : null,
'updated_at' => $invoice->updated_at ? $invoice->updated_at->toDateString() : null,
'deleted_at' => $invoice->deleted_at ? $invoice->deleted_at->toDateString() : null,
];
}
return $invoices;
}
/**
* @param $items
* @return array
*/
public function getInvoiceItems($items)
{
$transformed = [];
foreach ($items as $item) {
$transformed[] = [
'id' => $item->id,
'quantity' => $item->qty,
'cost' => $item->cost,
'product_key' => $item->product_key,
'notes' => $item->notes,
'discount' => $item->discount,
'tax_name1' => $item->tax_name1,
'tax_rate1' => $item->tax_rate1,
'date' => $item->created_at,
'custom_value1' => $item->custom_value1,
'custom_value2' => $item->custom_value2,
'line_item_type_id' => $item->invoice_item_type_id,
];
}
return $transformed;
}
/**
* @return array
*/
public function getQuotes()
{
$transformed = [];
$quotes = Invoice::where('account_id', $this->account->id)
->where('invoice_type_id', '=', INVOICE_TYPE_QUOTE)
->withTrashed()
->get();
foreach ($quotes as $quote) {
$transformed[] = [
'id' => $quote->id,
'client_id' => $quote->client_id,
'user_id' => $quote->user_id,
'company_id' => $quote->account_id,
'status_id' => $quote->invoice_status_id,
'design_id' => $quote->invoice_design_id,
'number' => $quote->invoice_number,
'discount' => $quote->discount,
'is_amount_discount' => $quote->is_amount_discount ?: false,
'po_number' => $quote->po_number,
'date' => $quote->invoice_date,
'last_sent_date' => $quote->last_sent_date,
'due_date' => $quote->due_date,
'is_deleted' => $quote->is_deleted,
'footer' => $quote->invoice_footer,
'public_notes' => $quote->public_notes,
'private_notes' => $quote->private_notes,
'terms' => $quote->terms,
'tax_name1' => $quote->tax_name1,
'tax_name2' => $quote->tax_name2,
'tax_rate1' => $quote->tax_rate1,
'tax_rate2' => $quote->tax_rate2,
'custom_value1' => $quote->custom_value1,
'custom_value2' => $quote->custom_value2,
'next_send_date' => null,
'amount' => $quote->amount,
'balance' => $quote->balance,
'partial' => $quote->partial,
'partial_due_date' => $quote->partial_due_date,
'created_at' => $quote->created_at ? $quote->created_at->toDateString() : null,
'updated_at' => $quote->updated_at ? $quote->updated_at->toDateString() : null,
'deleted_at' => $quote->deleted_at ? $quote->deleted_at->toDateString() : null,
];
}
return $transformed;
}
public function getPayments()
{
$transformed = [];
$payments = Payment::where('account_id', $this->account->id)
->withTrashed()
->get();
foreach ($payments as $payment) {
$transformed[] = [
'id' => $payment->id,
'invoices' => [
['invoice_id' => $payment->invoice_id, 'amount' => $payment->amount, 'refunded' => $payment->refunded],
],
'invoice_id' => $payment->invoice_id,
'company_id' => $payment->account_id,
'client_id' => $payment->client_id,
'user_id' => $payment->user_id,
'client_contact_id' => $payment->contact_id,
'invitation_id' => $payment->invitation_id,
'company_gateway_id' => $payment->account_gateway_id,
'type_id' => $payment->payment_type_id,
'status_id' => $payment->payment_status_id,
'amount' => $payment->amount,
'applied' => $payment->amount,
'refunded' => $payment->refunded,
'date' => $payment->payment_date,
'transaction_reference' => $payment->transaction_reference,
'payer_id' => $payment->payer_id,
'is_deleted' => $payment->is_deleted,
'updated_at' => $payment->updated_at ? $payment->updated_at->toDateString() : null,
'created_at' => $payment->created_at ? $payment->created_at->toDateString() : null,
'deleted_at' => $payment->deleted_at ? $payment->deleted_at->toDateString() : null,
];
}
return $transformed;
}
/**
* @return array
*/
private function getCredits()
{
$credits = Credit::where('account_id', $this->account->id)->where('balance', '>', '0')->whereIsDeleted(false)
->withTrashed()
->get();
$transformed = [];
foreach ($credits as $credit) {
$transformed[] = [
'client_id' => $credit->client_id,
'user_id' => $credit->user_id,
'company_id' => $credit->account_id,
'is_deleted' => $credit->is_deleted,
'amount' => $credit->balance,
'applied' => 0,
'refunded' => 0,
'date' => $credit->date,
'created_at' => $credit->created_at ? $credit->created_at->toDateString() : null,
'updated_at' => $credit->updated_at ? $credit->updated_at->toDateString() : null,
'deleted_at' => $credit->deleted_at ? $credit->deleted_at->toDateString() : null,
];
}
return $transformed;
// header('Content-Type: application/zip');
// header('Content-Length: ' . filesize($file));
// header("Content-Disposition: attachment; filename={$fileName}.zip");
}
}

View file

@ -17,7 +17,6 @@ use App\Ninja\Mailers\ContactMailer as Mailer;
use App\Ninja\Repositories\ClientRepository;
use App\Ninja\Repositories\InvoiceRepository;
use App\Services\InvoiceService;
use App\Services\RecurringInvoiceService;
use Auth;
use Cache;
use Input;
@ -34,7 +33,7 @@ class QuoteController extends BaseController
protected $invoiceService;
protected $entityType = ENTITY_INVOICE;
public function __construct(Mailer $mailer, InvoiceRepository $invoiceRepo, ClientRepository $clientRepo, InvoiceService $invoiceService, RecurringInvoiceService $recurringInvoiceService)
public function __construct(Mailer $mailer, InvoiceRepository $invoiceRepo, ClientRepository $clientRepo, InvoiceService $invoiceService)
{
// parent::__construct();
@ -42,7 +41,6 @@ class QuoteController extends BaseController
$this->invoiceRepo = $invoiceRepo;
$this->clientRepo = $clientRepo;
$this->invoiceService = $invoiceService;
$this->recurringInvoiceService = $recurringInvoiceService;
}
public function index()
@ -67,14 +65,6 @@ class QuoteController extends BaseController
return $this->invoiceService->getDatatable($accountId, $clientPublicId, ENTITY_QUOTE, $search);
}
public function getRecurringDatatable($clientPublicId = null)
{
$accountId = Auth::user()->account_id;
$search = Input::get('sSearch');
return $this->recurringInvoiceService->getDatatable($accountId, $clientPublicId, ENTITY_RECURRING_QUOTE, $search);
}
public function create(QuoteRequest $request, $clientPublicId = 0)
{
if (! Utils::hasFeature(FEATURE_QUOTES)) {
@ -167,7 +157,7 @@ class QuoteController extends BaseController
if ($invoice->due_date) {
$carbonDueDate = \Carbon::parse($invoice->due_date);
if (! $account->allow_approve_expired_quote && ! $carbonDueDate->isToday() && ! $carbonDueDate->isFuture()) {
if (! $carbonDueDate->isToday() && ! $carbonDueDate->isFuture()) {
return redirect("view/{$invitationKey}")->withError(trans('texts.quote_has_expired'));
}
}

View file

@ -1,41 +0,0 @@
<?php
namespace App\Http\Controllers;
use App\Ninja\Datatables\RecurringInvoiceDatatable;
use App\Ninja\Repositories\InvoiceRepository;
/**
* Class RecurringQuoteController.
*/
class RecurringQuoteController extends BaseController
{
/**
* @var InvoiceRepository
*/
protected $invoiceRepo;
/**
* RecurringQuoteController constructor.
*
* @param InvoiceRepository $invoiceRepo
*/
public function __construct(InvoiceRepository $invoiceRepo)
{
$this->invoiceRepo = $invoiceRepo;
}
/**
* @return mixed
*/
public function index()
{
$data = [
'title' => trans('texts.recurring_quotes'),
'entityType' => ENTITY_RECURRING_QUOTE,
'datatable' => new RecurringInvoiceDatatable(true, false, ENTITY_RECURRING_QUOTE),
];
return response()->view('list_wrapper', $data);
}
}

View file

@ -61,8 +61,6 @@ class ReportController extends BaseController
$action = Input::get('action');
$format = Input::get('format');
$account = Auth::user()->account;
if (Input::get('report_type')) {
$reportType = Input::get('report_type');
$dateField = Input::get('date_field');
@ -87,7 +85,6 @@ class ReportController extends BaseController
'product',
'profit_and_loss',
'task',
'task_details',
'tax_rate',
'quote',
];
@ -98,10 +95,10 @@ class ReportController extends BaseController
'reportTypes' => array_combine($reportTypes, Utils::trans($reportTypes)),
'reportType' => $reportType,
'title' => trans('texts.charts_and_reports'),
'account' => $account,
'account' => Auth::user()->account,
];
if ($account->hasFeature(FEATURE_REPORTS)) {
if (Auth::user()->account->hasFeature(FEATURE_REPORTS)) {
$isExport = $action == 'export';
$config = [
'date_field' => $dateField,
@ -114,8 +111,7 @@ class ReportController extends BaseController
'start_date' => $params['startDate'],
'end_date' => $params['endDate'],
];
$report = dispatch_now(new RunReport(auth()->user(), $reportType, $config, $account, $isExport));
$report = dispatch_now(new RunReport(auth()->user(), $reportType, $config, $isExport));
$params = array_merge($params, $report->exportParams);
switch ($action) {
case 'export':

View file

@ -45,7 +45,7 @@ class TaskApiController extends BaseAPIController
{
$tasks = Task::scope()
->withTrashed()
->with('client', 'invoice', 'project', 'task_status', 'user')
->with('client', 'invoice', 'project', 'task_status')
->orderBy('updated_at', 'desc');
return $this->listResponse($tasks);

47
app/Http/Controllers/TaskController.php Executable file → Normal file
View file

@ -7,7 +7,6 @@ use App\Http\Requests\TaskRequest;
use App\Http\Requests\UpdateTaskRequest;
use App\Models\Client;
use App\Models\Project;
use App\Models\Product;
use App\Models\Task;
use App\Models\TaskStatus;
use App\Ninja\Datatables\TaskDatatable;
@ -129,7 +128,6 @@ class TaskController extends BaseController
'task' => null,
'clientPublicId' => Input::old('client') ? Input::old('client') : ($request->client_id ?: 0),
'projectPublicId' => Input::old('project_id') ? Input::old('project_id') : ($request->project_id ?: 0),
'productPublicId' => Input::old('product_id') ? Input::old('product_id') : ($request->product_id ?: 0),
'method' => 'POST',
'url' => 'tasks',
'title' => trans('texts.new_task'),
@ -142,12 +140,6 @@ class TaskController extends BaseController
return View::make('tasks.edit', $data);
}
public function cloneTask(TaskRequest $request, $publicId)
{
return self::edit($request, $publicId, true);
}
/**
* Show the form for editing the specified resource.
*
@ -155,7 +147,7 @@ class TaskController extends BaseController
*
* @return \Illuminate\Contracts\View\View
*/
public function edit(TaskRequest $request, $publicId, $clone = false)
public function edit(TaskRequest $request)
{
$this->checkTimezone();
$task = $request->entity();
@ -180,36 +172,19 @@ class TaskController extends BaseController
$actions[] = DropdownButton::DIVIDER;
if (! $task->trashed()) {
if (! $clone) {
$actions[] = ['url' => 'javascript:submitAction("clone")', 'label' => trans("texts.clone_task")];
}
$actions[] = ['url' => 'javascript:submitAction("archive")', 'label' => trans('texts.archive_task')];
$actions[] = ['url' => 'javascript:onDeleteClick()', 'label' => trans('texts.delete_task')];
} else {
$actions[] = ['url' => 'javascript:submitAction("restore")', 'label' => trans('texts.restore_task')];
}
if ($clone) {
$task->id = null;
$task->public_id = null;
$task->deleted_at = null;
$method = 'POST';
$url = 'tasks';
}
else{
$method = 'PUT';
$url = 'tasks/'.$task->public_id;
}
$data = [
'task' => $task,
'entity' => $task,
'clientPublicId' => $task->client ? $task->client->public_id : 0,
'projectPublicId' => $task->project ? $task->project->public_id : 0,
'productPublicId' => $task->product ? $task->product->public_id : 0,
'method' => $method,
'url' => $url,
'method' => 'PUT',
'url' => 'tasks/'.$task->public_id,
'title' => trans('texts.edit_task'),
'actions' => $actions,
'timezone' => Auth::user()->account->timezone ? Auth::user()->account->timezone->name : DEFAULT_TIMEZONE,
@ -244,7 +219,6 @@ class TaskController extends BaseController
'clients' => Client::scope()->withActiveOrSelected($task ? $task->client_id : false)->with('contacts')->orderBy('name')->get(),
'account' => Auth::user()->account,
'projects' => Project::scope()->withActiveOrSelected($task ? $task->project_id : false)->with('client.contacts')->orderBy('name')->get(),
'products' => Product::scope()->withActiveOrSelected($task ? $task->product_id : false)->orderBy('product_key')->get(),
];
}
@ -255,13 +229,8 @@ class TaskController extends BaseController
*/
private function save($request, $publicId = null)
{
$action = Input::get('action');
if ( in_array($action, ['clone'])) {
return redirect()->to(sprintf('tasks/%s/clone', $publicId));
}
if (in_array($action, ['archive', 'delete', 'restore'])) {
return self::bulk();
}
@ -334,20 +303,12 @@ class TaskController extends BaseController
$account = Auth::user()->account;
$showProject = $lastProjectId != $task->project_id;
$item_data = [
$data[] = [
'publicId' => $task->public_id,
'description' => $task->present()->invoiceDescription($account, $showProject),
'duration' => $task->getHours(),
'cost' => $task->getRate(),
'productKey' => null,
];
if (!empty($task->product_id)) {
$item_data['productKey'] = $task->product->product_key;
}
$data[] = $item_data;
$lastProjectId = $task->project_id;
}

View file

@ -185,7 +185,7 @@ class TaxRateApiController extends BaseAPIController
{
$entity = $request->entity();
$entity->delete();
$this->taxRateRepo->delete($entity);
return $this->itemResponse($entity);
}

View file

@ -1,375 +0,0 @@
<?php
namespace App\Http\Controllers;
use App\Events\TicketUserViewed;
use App\Http\Requests\CreateTicketRequest;
use App\Http\Requests\TicketAddEntityRequest;
use App\Http\Requests\TicketInboundRequest;
use App\Http\Requests\TicketMergeRequest;
use App\Http\Requests\TicketRemoveEntityRequest;
use App\Http\Requests\TicketRequest;
use App\Http\Requests\UpdateTicketRequest;
use App\Libraries\Utils;
use App\Models\Client;
use App\Models\Ticket;
use App\Models\TicketComment;
use App\Models\TicketRelation;
use App\Models\User;
use App\Ninja\Datatables\TicketDatatable;
use App\Ninja\Repositories\TicketRepository;
use App\Services\TicketService;
use Barryvdh\LaravelIdeHelper\Eloquent;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Input;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Redirect;
use Illuminate\Support\Facades\Request;
use Illuminate\Support\Facades\View;
use Illuminate\Support\Facades\Session;
use DB;
/**
* Class TicketController
* @package App\Http\Controllers
*/
class TicketController extends BaseController
{
/**
* @var TicketService
*/
protected $ticketService;
/**
* @var
*/
protected $ticketRepository;
/**
* TicketController constructor.
* @param TicketService $ticketService
*/
public function __construct(TicketService $ticketService, TicketRepository $ticketRepository)
{
$this->ticketService = $ticketService;
$this->ticketRepo = $ticketRepository;
}
/**
* @return \Illuminate\Contracts\View\View
*/
public function index()
{
return View::make('list_wrapper', [
'entityType' => ENTITY_TICKET,
'datatable' => new TicketDatatable(),
'title' => trans('texts.tickets'),
]);
}
/**
* @param null $clientPublicId
* @return \Illuminate\Http\JsonResponse
*/
public function getDatatable($clientPublicId = null)
{
$search = Input::get('sSearch');
return $this->ticketService->getDatatable($search);
}
/**
* @param $publicId
* @return Redirect
*/
public function show($publicId)
{
Session::reflash();
return redirect("tickets/$publicId/edit");
}
/**
* @param TicketRequest $request
* @return View
*/
public function edit(TicketRequest $request)
{
$ticket = $request->entity();
$clients = false;
//If we are missing a client from the ticket, load clients for assignment
if($ticket->is_internal == TRUE && !$ticket->client_id)
$clients = Client::scope()->with('contacts')->get();
else if(!$ticket->client_id)
$clients = $this->ticketService->findClientsByContactEmail($ticket->contact_key);
$data = array_merge(self::getViewModel($ticket, $clients));
event(new TicketUserViewed($ticket));
return View::make('tickets.edit', $data);
}
/**
* @param UpdateTicketRequest $request
* @return View
*/
public function update(UpdateTicketRequest $request)
{
$data = $request->input();
$data['document_ids'] = $request->document_ids;
if($data['closed'] != '0000-00-00 00:00:00')
$data['action'] = TICKET_AGENT_CLOSED;
elseif(isset($data['description']) && strlen($data['description']) > 0)
$data['action'] = TICKET_AGENT_UPDATE;
else
$data['action'] = TICKET_SAVE_ONLY;
$ticket = $request->entity();
$ticket = $this->ticketService->save($data, $ticket);
$ticket->load('documents', 'relations');
$entityType = $ticket->getEntityType();
$message = trans("texts.updated_{$entityType}");
Session::flash('message', $message);
$data = array_merge($this->getViewmodel($ticket), $data);
return View::make('tickets.edit', $data);
}
/**
* @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector
*/
public function bulk()
{
$action = Input::get('action');
$ids = Input::get('public_id') ? Input::get('public_id') : Input::get('ids');
if ($action == 'purge' && ! auth()->user()->is_admin)
return redirect('dashboard')->withError(trans('texts.not_authorized'));
$count = $this->ticketService->bulk($ids, $action);
$message = Utils::pluralize($action.'d_ticket', $count);
Session::flash('message', $message);
if ($action == 'purge')
return redirect('dashboard')->withMessage($message);
else
return $this->returnBulk(ENTITY_TICKET, $action, $ids);
}
/**
* @param TicketRequest $request
* @param int $parentTicketId
* @return View
*/
public function create(TicketRequest $request, $parentTicketId = 0)
{
$parentTicket = Ticket::scope($parentTicketId)->first();
$parentTicketClientExists = false;
if ($parentTicket && method_exists($parentTicket, 'client')) {
$parentTicket->load('client');
$parentTicketClientExists = true;
}
//need to mock a ticket object or check if $request->old() exists and pass that in its place.
$mockTicket = [
'parent_ticket_id' => $parentTicketId ? $parentTicketId : null,
'subject' => '',
'description' => '',
'due_date' => '',
'client_public_id' => $parentTicketClientExists ? $parentTicket->client->public_id : null,
'agent_id' => null,
'is_internal' => $parentTicketClientExists ? true : false,
'private_notes' => '',
'priority_id' =>1,
];
$data = [
'users' => User::whereAccountId(Auth::user()->account_id)->get(),
'is_internal' => $request->parent_ticket_id ? true : false,
'parent_ticket' => $parentTicket ?: false,
'url' => 'tickets/',
'parent_tickets' => Ticket::scope()->where('status_id', '!=', 3)->whereNull('merged_parent_ticket_id')->OrderBy('public_id', 'DESC')->get(),
'method' => 'POST',
'title' => trans('texts.new_ticket'),
'account' => Auth::user()->account->load('clients.contacts', 'users'),
'timezone' => Auth::user()->account->timezone ? Auth::user()->account->timezone->name : DEFAULT_TIMEZONE,
'datetimeFormat' => Auth::user()->account->getMomentDateTimeFormat(),
'old' => $request->old() ? $request->old() : $mockTicket,
'clients' => Client::scope()->with('contacts')->get(),
];
return View::make('tickets.new_ticket', $data);
}
/**
* @param CreateTicketRequest $request
* @return Redirect
*/
public function store(CreateTicketRequest $request)
{
$input = $request->input();
$input['action'] = TICKET_AGENT_NEW;
$ticket = $this->ticketService->save($input, $request->entity());
return redirect("tickets/$ticket->public_id/edit");
}
/**
* @return array
*/
private static function getViewModel($ticket = false, $clients = false)
{
return [
'clients' => $clients,
//'status' => $ticket->status(),
'comments' => $ticket->comments(),
'account' => Auth::user()->account,
'url' => 'tickets/' . $ticket->public_id,
'ticket' => $ticket,
'entity' => $ticket,
'title' => trans('texts.edit_ticket'),
'timezone' => Auth::user()->account->timezone ? Auth::user()->account->timezone->name : DEFAULT_TIMEZONE,
'datetimeFormat' => Auth::user()->account->getMomentDateTimeFormat(),
'method' => 'PUT',
'isAdminUser' => Auth::user()->is_admin || Auth::user()->isTicketMaster() ? true : false,
];
}
/**
* @param Request $request
*/
public function inbound(TicketInboundRequest $request) : void
{
$ticket = $request->entity();
if(!$ticket)
Log::error('no ticket found - ? spam or new request?');
else
Log::error('ticket #'. $ticket->ticket_number .' found');
}
/**
* @param $publicId
* @return View
*/
public function merge($publicId)
{
$ticket = Ticket::scope($publicId)->first();
$data = [
'mergeableTickets' => $ticket->getClientMergeableTickets(),
'ticket' => $ticket,
'account' => Auth::user()->account,
'title' => trans('texts.ticket_merge'),
'method' => 'POST',
'url' => 'tickets/merge/',
'entity' => $ticket,
];
return View::make('tickets.merge', $data);
}
/**
* @param TicketMergeRequest $request
* @return Redirect
*/
public function actionMerge(TicketMergeRequest $request)
{
$ticket = $request->entity();
$this->ticketService->mergeTicket($ticket, $request->input());
Session::reflash();
return redirect("tickets/$request->updated_ticket_id/edit");
}
/**
* @return Collection
*/
public function getTicketRelationCollection(\Illuminate\Http\Request $request)
{
return $this->ticketService->getRelationCollection($request);
}
/**
* Add ticket relation entity.
* returns a formatted URL
* @return string
*/
public function addEntity(TicketAddEntityRequest $request)
{
return $request->addEntity();
}
/**
* Remove ticket
* @return primary ID
*/
public function removeEntity(TicketRemoveEntityRequest $request)
{
TicketRelation::destroy(request()->id);
return request()->id;
}
/**
* Algolia / Elasticsearch
* @return \Illuminate\Http\JsonResponse
*/
public function search()
{
if( config('ninja.scout_driver') != null) {
$result = TicketComment::search(request()->term)->where('agent_id', Auth::user()->id)->get()->pluck('description');
return response()->json($result);
}
}
}

View file

@ -1,200 +0,0 @@
<?php
namespace App\Http\Controllers;
use App\Http\Requests\CreateTicketTemplateRequest;
use App\Libraries\Utils;
use App\Models\TicketTemplate;
use App\Services\TicketTemplateService;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Input;
use Illuminate\Support\Facades\Redirect;
use Illuminate\Support\Facades\Session;
use Illuminate\Support\Facades\View;
class TicketTemplateController extends BaseController
{
/**
* @var TicketTemplateService
*/
protected $ticketTemplateService;
/**
* TicketTemplateController constructor.
* @param TicketTemplateService $ticketTemplateService
*/
public function __construct(TicketTemplateService $ticketTemplateService)
{
$this->ticketTemplateService = $ticketTemplateService;
}
/**
* @return mixed
*/
public function index()
{
return Redirect::to('settings/' . ACCOUNT_TICKETS . '#templates');
}
/**
* @param null $clientPublicId
* @return \Illuminate\Http\JsonResponse
*/
public function getDatatable($clientPublicId = null)
{
return $this->ticketTemplateService->getDatatable();
}
/**
* @param $publicId
* @return mixed
*/
public function show($publicId)
{
Session::reflash();
return Redirect::to("ticket_templates/$publicId/edit");
}
/**
* @param $publicId
* @return mixed
*/
public function edit($publicId)
{
$ticketTemplate = TicketTemplate::scope($publicId)->firstOrFail();
$data = self::getViewModel($ticketTemplate);
$data = array_merge($data, [
'method' => 'PUT',
'url' => '/ticket_templates/'.$publicId,
]);
return View::make('accounts.ticket_templates', $data);
}
/**
* @param $publicId
* @return mixed
*/
public function update($publicId)
{
return $this->save($publicId);
}
/**
* @param CreateTicketTemplateRequest $request
* @return mixed
*/
public function store(CreateTicketTemplateRequest $request)
{
return $this->save();
}
/**
* Displays the form for account creation.
*/
public function create()
{
$data = self::getViewModel(null);
$data = array_merge($data,[
'method' => 'POST',
'url' => '/ticket_templates/create',
'title' => trans('texts.add_template')
]);
return View::make('accounts.ticket_templates', $data);
}
/**
* @param $ticketTemplate
* @return array
*/
private function getViewModel($ticketTemplate)
{
$user = Auth::user();
$account = $user->account;
return [
'account' => $account,
'user' => $user,
'config' => false,
'ticket_templates' => $ticketTemplate,
];
}
/**
* @return mixed
*/
public function bulk()
{
$action = Input::get('bulk_action');
$ids = Input::get('bulk_public_id');
$count = $this->ticketTemplateService->bulk($ids, $action);
$message = Utils::pluralize($action.'d_ticket_template', $count);
Session::flash('message', $message);
return Redirect::to('settings/' . ACCOUNT_TICKETS . '#templates');
}
/**
* @param bool $ticketTemplatePublicId
* @return mixed
*/
public function save($ticketTemplatePublicId = false)
{
if ($ticketTemplatePublicId)
$ticketTemplate = TicketTemplate::scope($ticketTemplatePublicId)->firstOrFail();
else
$ticketTemplate = TicketTemplate::createNew();
$ticketTemplate->name = Input::get('name');
$ticketTemplate->description = Input::get('description');
$ticketTemplate->save();
$message = $ticketTemplatePublicId ? trans('texts.updated_ticket_template') : trans('texts.created_ticket_template');
Session::flash('message', $message);
return Redirect::to('settings/' . ACCOUNT_TICKETS . '#templates');
}
}

View file

@ -63,6 +63,6 @@ class Kernel extends HttpKernel
'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class,
'lookup' => \App\Http\Middleware\DatabaseLookup::class,
'permissions.required' => \App\Http\Middleware\PermissionsRequired::class,
'ticket' => \App\Http\Middleware\InboundTicketCheck::class,
'migration' => \App\Http\Middleware\EligibleForMigration::class,
];
}

View file

@ -6,7 +6,6 @@ use App\Models\Account;
use App\Models\Contact;
use App\Models\Invitation;
use App\Models\ProposalInvitation;
use App\Models\TicketInvitation;
use Auth;
use Utils;
use Closure;
@ -29,22 +28,14 @@ class Authenticate
public function handle($request, Closure $next, $guard = 'user')
{
$authenticated = Auth::guard($guard)->check();
$invitationKey = false;
if($request->invitation_key)
$invitationKey = $request->invitation_key;
elseif($request->proposal_invitation_key)
$invitationKey = $request->proposal_invitation_key;
elseif($request->ticket_invitation_key)
$invitationKey = $request->ticket_invitation_key;
$invitationKey = $request->invitation_key ?: $request->proposal_invitation_key;
if ($guard == 'client') {
if (! empty($request->invitation_key) || ! empty($request->proposal_invitation_key) || ! empty($request->ticket_invitation_key)) {
if (! empty($request->invitation_key) || ! empty($request->proposal_invitation_key)) {
$contact_key = session('contact_key');
if ($contact_key) {
$contact = $this->getContact($contact_key);
$invitation = $this->getInvitation($invitationKey, ! empty($request->proposal_invitation_key), ! empty($request->ticket_invitation_key));
$invitation = $this->getInvitation($invitationKey, ! empty($request->proposal_invitation_key));
if (! $invitation) {
return response()->view('error', [
@ -72,12 +63,12 @@ class Authenticate
$contact = false;
if ($contact_key) {
$contact = $this->getContact($contact_key);
} elseif ($invitationKey && $invitation = $this->getInvitation($invitationKey, ! empty($request->proposal_invitation_key), ! empty($request->ticket_invitation_key))) {
} elseif ($invitation = $this->getInvitation($invitationKey, ! empty($request->proposal_invitation_key))) {
$contact = $invitation->contact;
Session::put('contact_key', $contact->contact_key);
}
if (! $contact) {
return \Redirect::to('client/session_expired');
return \Redirect::to('client/login');
}
$account = $contact->account;
@ -136,7 +127,7 @@ class Authenticate
*
* @return \Illuminate\Database\Eloquent\Model|null|static
*/
protected function getInvitation($key, $isProposal = false, $isTicket = false)
protected function getInvitation($key, $isProposal = false)
{
if (! $key) {
return false;
@ -148,8 +139,6 @@ class Authenticate
if ($isProposal) {
$invitation = ProposalInvitation::withTrashed()->where('invitation_key', '=', $key)->first();
} elseif ($isTicket) {
$invitation = TicketInvitation::withTrashed()->where('invitation_key', '=', $key)->first();
} else {
$invitation = Invitation::withTrashed()->where('invitation_key', '=', $key)->first();
}

View file

@ -0,0 +1,22 @@
<?php
namespace App\Http\Middleware;
use Closure;
class Cors
{
/**
* Handle an incoming request.
*
* @param \Illuminate\Http\Request $request
* @param \Closure $next
* @return mixed
*/
public function handle($request, Closure $next)
{
return $next($request)
->header('Access-Control-Allow-Origin', '*')
->header('Access-Control-Allow-Methods', 'GET, POST');
}
}

View file

@ -2,7 +2,6 @@
namespace App\Http\Middleware;
use App\Models\LookupTicketInvitation;
use Illuminate\Http\Request;
use Closure;
use App\Models\LookupAccount;
@ -47,14 +46,10 @@ class DatabaseLookup
LookupInvitation::setServerByField('invitation_key', $key);
} elseif ($key = request()->proposal_invitation_key) {
LookupProposalInvitation::setServerByField('invitation_key', $key);
} elseif ($key = request()->ticket_invitation_key) {
LookupTicketInvitation::setServerByField('invitation_key', $key);
} elseif ($key = request()->contact_key ?: session('contact_key')) {
LookupContact::setServerByField('contact_key', $key);
} elseif ($key = request()->account_key) {
LookupAccount::setServerByField('account_key', $key);
} elseif($key = request()->MailboxHash) {
LookupTicketInvitation::setServerByField('ticket_hash', $key);
} else {
$subdomain = Utils::getSubdomain(\Request::server('HTTP_HOST'));
if ($subdomain != 'app') {

View file

@ -20,7 +20,6 @@ class DuplicateSubmissionCheck
{
if ($request->is('api/v1/*')
|| $request->is('save_sidebar_state')
|| $request->is('tickets/search')
|| $request->is('documents')) {
return $next($request);
}

View file

@ -0,0 +1,24 @@
<?php
namespace App\Http\Middleware;
use Closure;
class EligibleForMigration
{
/**
* Handle an incoming request.
*
* @param \Illuminate\Http\Request $request
* @param Closure $next
* @return mixed
*/
public function handle($request, Closure $next)
{
if (auth()->user()->eligibleForMigration()) {
return $next($request);
}
return redirect('/settings/account_management');
}
}

View file

@ -1,50 +0,0 @@
<?php
namespace App\Http\Middleware;
use App\Models\LookupAccount;
use App\Models\LookupTicketInvitation;
use App\Ninja\Tickets\Inbound\InboundTicketFactory;
use Closure;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Log;
class InboundTicketCheck
{
/**
* Handle an incoming request.
*
* @param Request $request
* @param Closure $next
*
* @return mixed
*/
public function handle(Request $request, Closure $next)
{
if (! config('ninja.multi_db_enabled'))
return $next($request);
$inbound = new InboundTicketFactory($request->input());
if($inbound->mailboxHash()){
//check if we can find the ticket_hash
LookupTicketInvitation::setServerByField('ticket_hash', $inbound->mailboxHash());
}
elseif($inbound->to()) {
//otherwise check if we can find the unique localpart.
$parts = explode("@", $inbound->to());
LookupAccount::setServerByField('support_email_local_part', $parts[0]);
}
/**
* If we don't trigger any of these if blocks, we need to die()
*/
return $next($request);
}
}

View file

@ -62,8 +62,9 @@ class StartupCheck
if (Utils::isSelfHost()) {
// Check if config:cache may have been run
if (app()->configurationIsCached()) {
echo 'Config caching is not currently supported, please run the following command to clear the cache.<pre>php artisan config:clear</pre>';
if (! env('APP_URL')) {
echo "<p>There appears to be a problem with your configuration, please check your .env file.</p>" .
"<p>If you've run 'php artisan config:cache' you will need to run 'php artisan config:clear'</p>.";
exit;
}

View file

@ -23,7 +23,6 @@ class VerifyCsrfToken extends BaseVerifier
'payment_hook/*',
'buy_now*',
'hook/bot/*',
'tickets/inbound',
];
/**

View file

@ -2,8 +2,6 @@
namespace App\Http\Requests;
use App\Models\Client;
class CreateClientRequest extends ClientRequest
{
/**
@ -13,7 +11,7 @@ class CreateClientRequest extends ClientRequest
*/
public function authorize()
{
return $this->user()->can('create', Client::class);
return $this->user()->can('create', ENTITY_CLIENT);
}
/**

View file

@ -2,8 +2,6 @@
namespace App\Http\Requests;
use App\Models\Client;
class CreateContactRequest extends ContactRequest
{
/**
@ -13,7 +11,7 @@ class CreateContactRequest extends ContactRequest
*/
public function authorize()
{
return $this->user()->can('create', Client::class);
return $this->user()->can('create', ENTITY_CONTACT);
}
/**

View file

@ -2,8 +2,6 @@
namespace App\Http\Requests;
use App\Models\Credit;
class CreateCreditRequest extends CreditRequest
{
/**
@ -13,7 +11,7 @@ class CreateCreditRequest extends CreditRequest
*/
public function authorize()
{
return $this->user()->can('create', Credit::class);
return $this->user()->can('create', ENTITY_CREDIT);
}
/**

View file

@ -11,7 +11,7 @@ class CreateCustomerRequest extends CustomerRequest
*/
public function authorize()
{
return $this->user()->can('create');
return $this->user()->can('create', ENTITY_CUSTOMER);
}
/**

View file

@ -10,7 +10,6 @@ class CreateDocumentRequest extends DocumentRequest
protected $autoload = [
ENTITY_INVOICE,
ENTITY_EXPENSE,
ENTITY_TICKET,
];
/**
@ -20,27 +19,8 @@ class CreateDocumentRequest extends DocumentRequest
*/
public function authorize()
{
if(session('contact_key'))
if($this->user()->hasFeature(FEATURE_DOCUMENTS))
return true;
if (! $this->user()->hasFeature(FEATURE_DOCUMENTS))
return false;
if ($this->invoice && $this->user()->cannot('edit', $this->invoice))
return false;
if ($this->expense && $this->user()->cannot('edit', $this->expense))
return false;
if($this->ticket && $this->user()->cannot('edit', $this->ticket))
return false;
return true;
//return $this->user()->can('create');
}
/**

View file

@ -2,8 +2,6 @@
namespace App\Http\Requests;
use App\Models\Expense;
class CreateExpenseCategoryRequest extends ExpenseCategoryRequest
{
// Expenses
@ -15,7 +13,7 @@ class CreateExpenseCategoryRequest extends ExpenseCategoryRequest
*/
public function authorize()
{
return $this->user()->can('create', Expense::class);
return $this->user()->can('create', ENTITY_EXPENSE_CATEGORY);
}
/**

View file

@ -2,8 +2,6 @@
namespace App\Http\Requests;
use App\Models\Expense;
class CreateExpenseRequest extends ExpenseRequest
{
// Expenses
@ -15,7 +13,7 @@ class CreateExpenseRequest extends ExpenseRequest
*/
public function authorize()
{
return $this->user()->can('create', Expense::class);
return $this->user()->can('create', ENTITY_EXPENSE);
}
/**

View file

@ -3,7 +3,6 @@
namespace App\Http\Requests;
use App\Models\Client;
use App\Models\Invoice;
class CreateInvoiceAPIRequest extends InvoiceRequest
{
@ -14,7 +13,7 @@ class CreateInvoiceAPIRequest extends InvoiceRequest
*/
public function authorize()
{
return $this->user()->can('create', Invoice::class);
return $this->user()->can('create', ENTITY_INVOICE);
}
/**

View file

@ -3,7 +3,6 @@
namespace App\Http\Requests;
use App\Models\Client;
use App\Models\Invoice;
class CreateInvoiceRequest extends InvoiceRequest
{
@ -15,10 +14,16 @@ class CreateInvoiceRequest extends InvoiceRequest
public function authorize()
{
if (request()->input('is_quote'))
return $this->user()->can('createEntity', ENTITY_QUOTE);
else
return $this->user()->can('create', Invoice::class);
return $this->user()->can('create', ENTITY_QUOTE);
else {
if(request()->input('is_recurring'))
$standardOrRecurringInvoice = ENTITY_RECURRING_INVOICE;
else
$standardOrRecurringInvoice = ENTITY_INVOICE;
return $this->user()->can('create', $standardOrRecurringInvoice);
}
}
/**

View file

@ -3,7 +3,6 @@
namespace App\Http\Requests;
use App\Models\Invoice;
use App\Models\Payment;
class CreatePaymentAPIRequest extends PaymentRequest
{
@ -14,7 +13,7 @@ class CreatePaymentAPIRequest extends PaymentRequest
*/
public function authorize()
{
return $this->user()->can('create', Payment::class);
return $this->user()->can('create', ENTITY_PAYMENT);
}
/**

View file

@ -3,7 +3,6 @@
namespace App\Http\Requests;
use App\Models\Invoice;
use App\Models\Payment;
class CreatePaymentRequest extends PaymentRequest
{
@ -14,7 +13,7 @@ class CreatePaymentRequest extends PaymentRequest
*/
public function authorize()
{
return $this->user()->can('create', Payment::class);
return $this->user()->can('create', ENTITY_PAYMENT);
}
/**

View file

@ -3,7 +3,6 @@
namespace App\Http\Requests;
use App\Models\Invoice;
use App\Models\PaymentTerm;
class CreatePaymentTermRequest extends PaymentTermRequest
{
@ -15,7 +14,7 @@ class CreatePaymentTermRequest extends PaymentTermRequest
public function authorize()
{
return $this->user()->can('create', PaymentTerm::class) || $this->user()->can('create', Invoice::class);
return $this->user()->can('create', ENTITY_PAYMENT_TERM);
}
/**

View file

@ -2,8 +2,6 @@
namespace App\Http\Requests;
use App\Models\Product;
class CreateProductRequest extends ProductRequest
{
/**
@ -13,7 +11,7 @@ class CreateProductRequest extends ProductRequest
*/
public function authorize()
{
return $this->user()->can('create', Product::class);
return $this->user()->can('create', ENTITY_PRODUCT);
}
/**

View file

@ -2,8 +2,6 @@
namespace App\Http\Requests;
use App\Models\Project;
class CreateProjectRequest extends ProjectRequest
{
/**
@ -13,7 +11,7 @@ class CreateProjectRequest extends ProjectRequest
*/
public function authorize()
{
return $this->user()->can('create', Project::class);
return $this->user()->can('create', ENTITY_PROJECT);
}
/**

View file

@ -2,9 +2,6 @@
namespace App\Http\Requests;
use App\Models\Proposal;
use App\Models\ProposalCategory;
class CreateProposalCategoryRequest extends ProposalCategoryRequest
{
/**
@ -14,7 +11,7 @@ class CreateProposalCategoryRequest extends ProposalCategoryRequest
*/
public function authorize()
{
return $this->user()->can('create', Proposal::class) || $this->user()->can('create', ProposalCategory::class);
return $this->user()->can('create', ENTITY_PROPOSAL_CATEGORY);
}
/**

View file

@ -2,8 +2,6 @@
namespace App\Http\Requests;
use App\Models\Proposal;
class CreateProposalRequest extends ProposalRequest
{
/**
@ -13,7 +11,7 @@ class CreateProposalRequest extends ProposalRequest
*/
public function authorize()
{
return $this->user()->can('create', Proposal::class);
return $this->user()->can('create', ENTITY_PROPOSAL);
}
/**

View file

@ -2,8 +2,6 @@
namespace App\Http\Requests;
use App\Models\Proposal;
class CreateProposalSnippetRequest extends ProposalSnippetRequest
{
/**
@ -13,7 +11,7 @@ class CreateProposalSnippetRequest extends ProposalSnippetRequest
*/
public function authorize()
{
return $this->user()->can('create', Proposal::class);
return $this->user()->can('create', ENTITY_PROPOSAL_SNIPPET);
}
/**

View file

@ -2,8 +2,6 @@
namespace App\Http\Requests;
use App\Models\Proposal;
class CreateProposalTemplateRequest extends ProposalTemplateRequest
{
/**
@ -13,7 +11,7 @@ class CreateProposalTemplateRequest extends ProposalTemplateRequest
*/
public function authorize()
{
return $this->user()->can('create', Proposal::class);
return $this->user()->can('create', ENTITY_PROPOSAL_TEMPLATE);
}
/**

View file

@ -2,8 +2,6 @@
namespace App\Http\Requests;
use App\Models\Expense;
class CreateRecurringExpenseRequest extends RecurringExpenseRequest
{
/**
@ -13,7 +11,7 @@ class CreateRecurringExpenseRequest extends RecurringExpenseRequest
*/
public function authorize()
{
return $this->user()->can('create', Expense::class);
return $this->user()->can('create', ENTITY_RECURRING_EXPENSE);
}
/**

View file

@ -2,8 +2,6 @@
namespace App\Http\Requests;
use App\Models\Task;
class CreateTaskRequest extends TaskRequest
{
/**
@ -13,7 +11,7 @@ class CreateTaskRequest extends TaskRequest
*/
public function authorize()
{
return $this->user()->can('create', Task::class);
return $this->user()->can('create', ENTITY_TASK);
}
/**

View file

@ -13,7 +13,7 @@ class CreateTaxRateRequest extends TaxRateRequest
*/
public function authorize()
{
return $this->user()->hasPermission('admin');
return $this->user()->can('create', ENTITY_TAX_RATE);
}
/**

View file

@ -1,69 +0,0 @@
<?php
namespace App\Http\Requests;
use App\Models\Client;
use App\Models\Ticket;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Log;
class CreateTicketRequest extends EntityRequest
{
/**
* Determine if the user is authorized to make this request.
*
* @return bool
*/
protected $autoload = [
ENTITY_CLIENT
];
public function authorize()
{
return $this->user()->can('create', Ticket::class);
}
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules()
{
$rules = [];
$rules['subject'] = 'required';
$rules['description'] = 'required';
if(request()->input('is_internal'))
$rules['agent_id'] = 'required';
else
$rules['client_public_id']= 'required';
return $rules;
}
public function sanitize()
{
$data = $this->all();
if(isset($data['client_public_id']) && $data['client_public_id'] > 0 && !isset($data['contact_key'])){
$client = Client::scope($data['client_public_id'])->first();
$contact = $client->getPrimaryContact();
$data['contact_key'] = $contact->contact_key;
}
if($data['parent_ticket_id'] > 0)
$data['parent_ticket_id'] = Ticket::getPrivateId($data['parent_ticket_id']);
//if(isset($data['agent_id']) && $data['agent_id'] == 0)
// $data['agent_id'] = Auth::user()->id;
$this->replace($data);
return $this->all();
}
}

View file

@ -2,8 +2,6 @@
namespace App\Http\Requests;
use App\Models\Vendor;
class CreateVendorRequest extends VendorRequest
{
/**
@ -13,7 +11,7 @@ class CreateVendorRequest extends VendorRequest
*/
public function authorize()
{
return $this->user()->can('create', Vendor::class);
return $this->user()->can('create', ENTITY_VENDOR);
}
/**

View file

@ -2,20 +2,7 @@
namespace App\Http\Requests;
use App\Models\Contact;
class DocumentRequest extends EntityRequest
{
protected $entityType = ENTITY_DOCUMENT;
public function authorize()
{
$contact = Contact::getContactIfLoggedIn();
if($contact && $contact->account->hasFeature(FEATURE_DOCUMENTS))
return true;
else
return $this->user()->can('view', $this->entity());
}
}

View file

@ -3,7 +3,6 @@
namespace App\Http\Requests;
use App\Libraries\HistoryUtils;
use App\Models\Contact;
use App\Models\EntityModel;
use Input;
use Utils;
@ -33,12 +32,6 @@ class EntityRequest extends Request
$publicId = $this->$field;
}
}
if (! $publicId) {
$field = $this->entityType;
if (! empty($this->$field)) {
$publicId = $this->$field;
}
}
if (! $publicId) {
$publicId = Input::get('public_id') ?: Input::get('id');
}
@ -47,21 +40,10 @@ class EntityRequest extends Request
return null;
}
//Support Client Portal Scopes
$accountId = false;
if($this->user()->account_id)
$accountId = $this->user()->account_id;
elseif(Input::get('account_id'))
$accountId = Input::get('account_id');
elseif($contact = Contact::getContactIfLoggedIn())
$accountId = $contact->account->id;
if (method_exists($class, 'trashed')) {
$this->entity = $class::scope($publicId, $accountId)->withTrashed()->firstOrFail();
$this->entity = $class::scope($publicId)->withTrashed()->firstOrFail();
} else {
$this->entity = $class::scope($publicId, $accountId)->firstOrFail();
$this->entity = $class::scope($publicId)->firstOrFail();
}
return $this->entity;
@ -77,10 +59,11 @@ class EntityRequest extends Request
if ($this->entity()) {
if ($this->user()->can('view', $this->entity())) {
HistoryUtils::trackViewed($this->entity());
return true;
}
} else {
return $this->user()->can('createEntity', $this->entityType);
return $this->user()->can('create', $this->entityType);
}
}

View file

@ -9,73 +9,59 @@ class InvoiceRequest extends EntityRequest
{
protected $entityType = ENTITY_INVOICE;
/**
/**
* Determine if the user is authorized to make this request.
*
* @return bool
*/
public function authorize()
{
$invoice = parent::entity();
$entity = $invoice ? $invoice->subEntityType() : ENTITY_INVOICE;
switch($entity)
{
case ENTITY_INVOICE:
$crossCloneEntity = ENTITY_QUOTE;
break;
case ENTITY_QUOTE:
$crossCloneEntity = ENTITY_INVOICE;
break;
case ENTITY_RECURRING_INVOICE:
$crossCloneEntity = ENTITY_RECURRING_QUOTE;
break;
case ENTITY_RECURRING_QUOTE:
$crossCloneEntity = ENTITY_RECURRING_INVOICE;
break;
}
if ($invoice && $invoice->isQuote())
$standardOrRecurringInvoice = ENTITY_QUOTE;
elseif($invoice && $invoice->is_recurring)
$standardOrRecurringInvoice = ENTITY_RECURRING_INVOICE;
else
$standardOrRecurringInvoice = ENTITY_INVOICE;
if(request()->is('invoices/create*') && $this->user()->can('createEntity', ENTITY_INVOICE))
if(request()->is('invoices/*/edit') && request()->isMethod('get') && $this->user()->can('edit', $invoice))
return true;
if(request()->is('recurring_invoices/create*') && $this->user()->can('createEntity', ENTITY_INVOICE))
if(request()->is('quotes/*/edit') && request()->isMethod('get') && $this->user()->can('edit', $invoice))
return true;
if(request()->is('quotes/create*') && $this->user()->can('createEntity', ENTITY_QUOTE))
if(request()->is('invoices/create*') && $this->user()->can('create', ENTITY_INVOICE))
return true;
if(request()->is('recurring_quotes/create*') && $this->user()->can('createEntity', ENTITY_QUOTE))
return true;
if(request()->is('invoices/create*') && !$this->user()->can('create', ENTITY_INVOICE))
return false;
if($invoice && $invoice->isType(INVOICE_TYPE_STANDARD) && request()->is('*invoices/*/edit') && request()->isMethod('put') && $this->user()->can('edit', $invoice))
return true;
if(request()->is('recurring_invoices/create') && !$this->user()->can('create', ENTITY_RECURRING_INVOICE))
return false;
if($invoice && $invoice->isType(INVOICE_TYPE_QUOTE) && request()->is('*quotes/*/edit') && request()->isMethod('put') && $this->user()->can('edit', $invoice))
return true;
if(request()->is('quotes/create*') && !$this->user()->can('create', ENTITY_QUOTE))
return false;
// allow cross clone quote to invoice
if($invoice && $invoice->isType(INVOICE_TYPE_QUOTE) && request()->is('*invoices/*/clone') && request()->isMethod('get') && $this->user()->can('view', $invoice, $crossCloneEntity))
return true;
if(request()->is('invoices/*/edit') && request()->isMethod('put') && !$this->user()->can('edit', $standardOrRecurringInvoice))
return false;
// allow cross clone invoice to quote
if($invoice && $invoice->isType(INVOICE_TYPE_STANDARD) && request()->is('*quotes/*/clone') && request()->isMethod('get') && $this->user()->can('view', $invoice, $crossCloneEntity))
return true;
if(request()->is('quotes/*/edit') && request()->isMethod('put') && !$this->user()->can('edit', ENTITY_QUOTE))
return false;
if($invoice && $invoice->isType(INVOICE_TYPE_STANDARD) && request()->is('*invoices/*') && request()->isMethod('get') && $this->user()->can('view', $invoice, $entity))
return true;
if(request()->is('invoices/*') && request()->isMethod('get') && !$this->user()->can('view', $standardOrRecurringInvoice))
return false;
if($invoice && $invoice->isType(INVOICE_TYPE_QUOTE) && request()->is('*quotes/*') && request()->isMethod('get') && $this->user()->can('view', $invoice, $entity))
return true;
if(request()->is('quotes/*') && request()->isMethod('get') && !$this->user()->can('view', ENTITY_QUOTE))
return false;
if ($invoice) {
HistoryUtils::trackViewed($invoice);
}
return false;
return true;
}
public function entity()
{
$invoice = parent::entity();

View file

@ -2,20 +2,18 @@
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
use App\Models\Ticket;
class CreateTicketTemplateRequest extends Request
class MigrationAuthRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*
* @return bool
*/
public function authorize()
{
return $this->user()->can('create', Ticket::class);
return true;
}
/**
@ -23,16 +21,11 @@ class CreateTicketTemplateRequest extends Request
*
* @return array
*/
public function rules()
{
$rules = [
'name' => 'required',
'description' => 'required',
return [
'email' => 'required|email',
'password' => 'required',
];
return $rules;
}
}

View file

@ -0,0 +1,30 @@
<?php
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
class MigrationCompaniesRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*
* @return bool
*/
public function authorize()
{
return true;
}
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules()
{
return [
'companies' => 'required',
];
}
}

View file

@ -0,0 +1,30 @@
<?php
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
class MigrationEndpointRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*
* @return bool
*/
public function authorize()
{
return true;
}
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules()
{
return [
'endpoint' => 'required|url',
];
}
}

View file

@ -2,9 +2,15 @@
namespace App\Http\Requests;
class UpdateClientPortalTicketRequest extends TicketRequest
{
use Illuminate\Foundation\Http\FormRequest;
class MigrationTypeRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*
* @return bool
*/
public function authorize()
{
return true;
@ -18,6 +24,7 @@ class UpdateClientPortalTicketRequest extends TicketRequest
public function rules()
{
return [
'option' => 'required|in:0,1',
];
}
}

View file

@ -31,9 +31,4 @@ class ProposalSnippetRequest extends EntityRequest
return $this->all();
}
public function authorize()
{
return $this->user()->can('view', ENTITY_PROPOSAL) || $this->user()->can('createEntity', ENTITY_PROPOSAL);
}
}

View file

@ -2,22 +2,7 @@
namespace App\Http\Requests;
/**
* Class ProposalTemplateRequest
* @package App\Http\Requests
*/
class ProposalTemplateRequest extends EntityRequest
{
/**
* @var string
*/
protected $entityType = ENTITY_PROPOSAL_TEMPLATE;
/**
* @return bool
*/
public function authorize()
{
return $this->user()->can('view', ENTITY_PROPOSAL) || $this->user()->can('createEntity', ENTITY_PROPOSAL);
}
}

View file

@ -31,10 +31,4 @@ class QuoteRequest extends EntityRequest
return $invoice;
}
public function authorize()
{
return $this->user()->can('createEntity', ENTITY_QUOTE);
}
}

View file

@ -41,10 +41,6 @@ class SaveClientPortalSettings extends Request
$input['client_view_css'] = HTMLUtils::sanitizeCSS($this->client_view_css);
}
if ($this->client_view_js && Utils::isSelfHost()) {
$input['client_view_js'] = HTMLUtils::sanitizeJS($this->client_view_js);
}
if (Utils::isNinja()) {
if ($this->custom_link == 'subdomain') {
$subdomain = substr(strtolower($input['subdomain']), 0, MAX_SUBDOMAIN_LENGTH);

View file

@ -1,51 +0,0 @@
<?php
namespace App\Http\Requests;
use App\Models\Ticket;
use Illuminate\Validation\Rule;
class SaveTicketSettings extends Request
{
/**
* Determine if the user is authorized to make this request.
*
* @return bool
*/
public function authorize()
{
return $this->user()->is_admin;
}
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules()
{
return [
'support_email_local_part' =>
Rule::unique('account_ticket_settings')->ignore($this->user()->account->account_ticket_settings->id),
];
}
public function sanitize()
{
$input = $this->all();
//ensure we
$maxTicketNumber = Ticket::scope()->withTrashed()->max('ticket_number');
if($input['ticket_number_start'] <= $maxTicketNumber){
$input['ticket_number_start'] = $maxTicketNumber+1;
$this->replace($input);
return $this->all();
}
return $input;
}
}

View file

@ -1,93 +0,0 @@
<?php
namespace App\Http\Requests;
use App\Models\TicketRelation;
class TicketAddEntityRequest extends EntityRequest
{
protected $entityType = ENTITY_TICKET;
public function authorize()
{
return $this->user()->can('edit', Ticket::class);
}
public function addEntity()
{
$entityType = request()->entity;
$linkEntity = request()->entity;
if(request()->entity == 'quote')
$entityType = 'invoice';
$className = '\App\Models\\'.ucfirst($entityType);
$entityModel = new $className();
$entityId = $entityModel::getPortalPrivateId(request()->entity_id, request()->account_id);
$tr = new TicketRelation();
$tr->entity = $linkEntity;
$tr->entity_id = $entityId;
$tr->ticket_id = request()->ticket_id;
$tr->save();
$str = self::buildEntityUrl($linkEntity, request()->entity_id, request()->account_id);
$str .= ' <i style="margin-left:5px;width:12px;cursor:pointer" onclick="removeRelation('.$tr->id.')" class="fa fa-minus-circle redlink" title="Remove item"/>';
$tr->entity_url = $str;
$tr->save();
return $tr;
}
private static function buildEntityUrl($entityType, $publicId, $accountId) : string
{
$linkEntity = $entityType;
if($entityType == 'quote')
$entityType = 'invoice';
$className = '\App\Models\\'.ucfirst($entityType);
$entityModel = new $className();
$entity = $entityModel::scope($publicId, $accountId)->first();
return link_to("{$linkEntity}s/{$publicId}/edit", self::setLinkDescription($linkEntity, $entity), ['class' => ''])->toHtml();
}
private static function setLinkDescription($entityType, $entity)
{
switch($entityType)
{
case 'quote':
return trans('texts.quote'). ' ' .$entity->invoice_number;
case 'invoice':
return trans('texts.invoice'). ' ' .$entity->invoice_number;
case 'task':
return trans('texts.task'). ' ' .$entity->description;
case 'payment':
return trans('texts.payment'). '('. trans('texts.invoice') . ' #'. $entity->invoice->invoice_number. ')';
case 'credit':
return trans('texts.credit'). ' (' .$entity->client->getDisplayName(). ' ' .$entity->amount.')';
case 'expense':
return strlen($entity->public_notes) ? trans('texts.expense'). ' ' .$entity->public_notes : trans('texts.expense'). ' ' .$entity->amount;
case 'project':
return $entity->name;
default:
return '';
}
}
}

View file

@ -1,28 +0,0 @@
<?php
namespace App\Http\Requests;
use App\Ninja\Repositories\TicketRepository;
use App\Ninja\Tickets\Inbound\InboundTicketFactory;
use App\Ninja\Tickets\Inbound\InboundTicketService;
use Illuminate\Support\Facades\Log;
class TicketInboundRequest extends Request
{
public function entity()
{
$inboundTicketService = new InboundTicketService(new InboundTicketFactory(request()->getContent()), new TicketRepository());
return $inboundTicketService->process();
}
public function rules()
{
return [];
}
public function authorize()
{
return true;
}
}

View file

@ -1,23 +0,0 @@
<?php
namespace App\Http\Requests;
class TicketMergeRequest extends EntityRequest
{
protected $entityType = ENTITY_TICKET;
public function entity()
{
return parent::entity();
}
public function rules()
{
return [
'updated_ticket_id' => 'required',
];
}
}

View file

@ -1,14 +0,0 @@
<?php
namespace App\Http\Requests;
class TicketRemoveEntityRequest extends EntityRequest
{
protected $entityType = ENTITY_TICKET;
public function authorize()
{
return $this->user()->can('edit', Ticket::class);
}
}

View file

@ -1,34 +0,0 @@
<?php
namespace App\Http\Requests;
class TicketRequest extends EntityRequest
{
protected $entityType = ENTITY_TICKET;
public function entity()
{
$ticket = parent::entity();
// eager load the documents
if ($ticket && method_exists($ticket, 'documents') && ! $ticket->relationLoaded('documents'))
$ticket->load('documents', 'relations');
return $ticket;
}
public function authorize()
{
if(request()->is('tickets/create*') && $this->user()->can('createEntity', ENTITY_TICKET))
return true;
elseif (request()->is('tickets/*/edit') && $this->user()->can('view', $this->entity()))
return true;
else
return false;
}
}

View file

@ -2,8 +2,6 @@
namespace App\Http\Requests;
use App\Models\Contact;
class UpdateDocumentRequest extends DocumentRequest
{
/**
@ -13,14 +11,7 @@ class UpdateDocumentRequest extends DocumentRequest
*/
public function authorize()
{
$contact = Contact::getContactIfLoggedIn();
if($contact && $contact->account->hasFeature(FEATURE_DOCUMENTS))
return true;
else
return $this->entity() && $this->user()->can('edit', $this->entity());
return $this->entity() && $this->user()->can('edit', $this->entity());
}
/**

View file

@ -1,64 +0,0 @@
<?php
namespace App\Http\Requests;
use App\Models\Client;
class UpdateTicketRequest extends TicketRequest
{
protected $autoload = [
ENTITY_CLIENT
];
/**
* Determine if the user is authorized to make this request.
*
* @return bool
*/
public function authorize()
{
return $this->entity() && $this->user()->can('edit', $this->entity());
}
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules()
{
$data = $this->all();
$rules = [
'client_public_id' => 'min:1|numeric',
];
if($data['is_internal'] && $data['is_internal'] == false)
$rules['client_public_id'] = 'min:1|numeric|required';
return $rules;
}
public function sanitize()
{
$data = $this->all();
if(isset($data['client_public_id']) && $data['client_public_id'] > 0 && !isset($data['contact_key'])){
$client = Client::scope($data['client_public_id'])->first();
$contact = $client->getPrimaryContact();
$data['contact_key'] = $contact->contact_key;
}
if(isset($data['parent_ticket_id']) && $data['parent_ticket_id'] > 0)
$data['parent_ticket_id'] = Ticket::getPrivateId($data['parent_ticket_id']);
if(isset($data['agent_id']) && $data['agent_id'] > 0)
$data['user_id'] = $data['agent_id'];
$this->replace($data);
return $this->all();
}
}

View file

@ -1,48 +0,0 @@
<?php
namespace App\Http\ViewComponents;
use Illuminate\Contracts\Support\Htmlable;
use Illuminate\Support\Facades\View;
use App\Ninja\Repositories\ClientRepository;
class SimpleSelectComponent implements Htmlable
{
protected $entityType;
protected $items;
protected $itemLabel;
protected $fieldLabel;
protected $module;
protected $secondaryItemLabel;
protected $selectId;
protected $defaultValue;
public function __construct($entityType, $items, $itemLabel, $fieldLabel, $defaultValue = null, $secondaryItemLabel = null, $module = null, $selectId = null) {
$this->entityType = $entityType;
$this->items = $items;
$this->itemLabel = $itemLabel;
$this->fieldLabel = $fieldLabel;
$this->defaultValue = $defaultValue;
$this->module = $module;
$this->secondaryItemLabel = $secondaryItemLabel;
if ($selectId) {
$this->selectId = $selectId;
} else {
$this->selectId = $fieldLabel . '_id';
}
}
public function toHtml()
{
return View::make('components.simple_select')->with([
'entityType' => $this->entityType,
'items' => $this->items,
'itemLabel' => $this->itemLabel,
'secondaryItemLabel' => $this->secondaryItemLabel,
'fieldLabel' => mtrans($this->module, $this->fieldLabel),
'selectId' => $this->selectId,
'defaultValue' => $this->defaultValue,
])->render();
}
}

View file

@ -105,7 +105,7 @@ class GenerateStatementData
$payments = Payment::with('invoice', 'payment_type')
->withArchived()
->whereClientId($this->client->id)
->excludeFailed()
//->excludeFailed()
->where('payment_date', '>=', $this->options['start_date'])
->where('payment_date', '<=', $this->options['end_date']);
@ -151,7 +151,7 @@ class GenerateStatementData
$item->product_key = $ageGroups['age_group_0'];
$item->notes = $ageGroups['age_group_30'];
$item->custom_value1 = $ageGroups['age_group_60'];
$item->custom_value1 = $ageGroups['age_group_90'];
$item->custom_value2 = $ageGroups['age_group_90'];
$item->cost = $ageGroups['age_group_120'];
$item->invoice_item_type_id = 4;
$data->push($item);

View file

@ -2,7 +2,9 @@
namespace App\Jobs;
use App\Models\Invoice;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use App\Models\User;
use App\Ninja\Mailers\UserMailer;
use Barracuda\ArchiveStream\Archive;
@ -13,13 +15,15 @@ use Barracuda\ArchiveStream\Archive;
//class DownloadInvoices extends Job implements ShouldQueue
class DownloadInvoices extends Job
{
//use InteractsWithQueue, SerializesModels;
/**
* @var User
*/
protected $user;
/**
* @var Invoice[]
* @var array
*/
protected $invoices;
@ -38,7 +42,7 @@ class DownloadInvoices extends Job
/**
* Execute the job.
*
* @param UserMailer $mailer
* @param ContactMailer $mailer
*/
public function handle(UserMailer $userMailer)
{

View file

@ -65,17 +65,7 @@ class ExportReportResults extends Job
foreach ($totals as $currencyId => $each) {
foreach ($each as $dimension => $val) {
$tmp = [];
$currency = Utils::getFromCache($currencyId, 'currencies');
if (!$currency) {
$name = $currencyId;
$account = $this->user->account->first();
$currencyId = $account->currency_id;
} else {
$name = $currency->name;
}
$tmp[] = $dimension ? $name . ' - ' . $dimension : $name;
$tmp[] = Utils::getFromCache($currencyId, 'currencies')->name . (($dimension) ? ' - ' . $dimension : '');
foreach ($val as $field => $value) {
if ($field == 'duration') {
$tmp[] = Utils::formatTime($value);

View file

@ -10,12 +10,11 @@ use App\Jobs\Job;
class RunReport extends Job
{
public function __construct($user, $reportType, $config, $account, $isExport = false)
public function __construct($user, $reportType, $config, $isExport = false)
{
$this->user = $user;
$this->reportType = $reportType;
$this->config = $config;
$this->account = $account;
$this->isExport = $isExport;
}
@ -47,14 +46,6 @@ class RunReport extends Job
$startDate = Carbon::now()->subMonth()->firstOfMonth()->toDateString();
$endDate = Carbon::now()->subMonth()->lastOfMonth()->toDateString();
break;
case 'this_quarter':
$startDate = Carbon::now()->firstOfQuarter()->toDateString();
$endDate = Carbon::now()->lastOfQuarter()->toDateString();
break;
case 'last_quarter':
$startDate = Carbon::now()->subMonth(3)->firstOfQuarter()->toDateString();
$endDate = Carbon::now()->subMonth(3)->lastOfQuarter()->toDateString();
break;
case 'this_year':
$startDate = Carbon::now()->firstOfYear()->toDateString();
$endDate = Carbon::now()->lastOfYear()->toDateString();
@ -72,7 +63,7 @@ class RunReport extends Job
$endDate = $config['end_date'];
}
$report = new $reportClass($startDate, $endDate, $isExport, $this->account, $config);
$report = new $reportClass($startDate, $endDate, $isExport, $config);
$report->run();
$params = [

View file

@ -1,51 +0,0 @@
<?php
namespace App\Jobs;
use App\Models\Ticket;
use App\Ninja\Tickets\Actions\TicketOverdue;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
/**
* Class SendOverdueTicketNotification.
*/
class SendOverdueTicketNotification extends Job implements ShouldQueue
{
use InteractsWithQueue, SerializesModels;
/**
* @var Ticket
*/
protected $ticket;
/**
* @var string
*/
protected $server;
/**
* Create a new job instance.
* @param Ticket $ticket
* @param mixed $type
*/
public function __construct(Ticket $ticket)
{
$this->ticket = $ticket;
$this->server = config('database.default');
}
/**
* Execute the job.
*
* @param TicketOverdue $ticketOverdue
*/
public function handle(TicketOverdue $ticketOverdue)
{
$ticketOverdue->fire($this->ticket);
}
}

View file

@ -1,76 +0,0 @@
<?php
namespace App\Jobs\Ticket;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Auth;
use App;
use App\Jobs\Job;
use App\Models\Ticket;
use Illuminate\Support\Facades\Log;
/**
* Class TicketAction.
*
* Queues the processing of ticket actions's
*/
class TicketAction extends Job implements ShouldQueue
{
use InteractsWithQueue, SerializesModels;
/**
* @var Ticket_attributes
*/
protected $deltaAttributes;
/**
* @var Ticket
*/
protected $originalTicket;
/**
* @var Ticket
*/
protected $updatedTicket;
/**
* @var mixed
*/
protected $server;
/**
* @var mixed
*/
protected $action;
/**
* TicketAction constructor.
* @param array $deltaAttributes
* @param array $originalTicket
* @param $updatedTicket
* @param $action
*/
public function __construct($deltaAttributes, $originalTicket, $updatedTicket, $action)
{
$this->deltaAttributes = $deltaAttributes;
$this->originalTicket = $originalTicket;
$this->updatedTicket = $updatedTicket;
$this->server = config('database.default');
$this->action = $action;
}
/**
* process action
*/
public function handle()
{
$ticketHandler = new App\Ninja\Tickets\Factory\TicketFactory($this->originalTicket, $this->deltaAttributes, $this->updatedTicket, $this->action);
$ticketHandler->process();
}
}

View file

@ -1,68 +0,0 @@
<?php
namespace App\Jobs\Ticket;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Auth;
use App;
use App\Models\Ticket;
use App\Ninja\Mailers\TicketMailer;
use App\Jobs\Job;
/**
* Class TicketSendNotificationEmail.
*/
class TicketSendNotificationEmail extends Job implements ShouldQueue
{
use InteractsWithQueue, SerializesModels;
/**
* @var Ticket
*/
protected $ticket;
/**
* @var array
*/
protected $ticketData;
/**
* @var mixed
*/
protected $server;
/**
* TicketSendNotificationEmail constructor.
* @param array $ticketData
* @param Ticket $ticket
*/
public function __construct(array $ticketData, Ticket $ticket)
{
$this->ticket = $ticket;
$this->ticketData = $ticketData;
$this->server = config('database.default');
}
/**
* @param TicketMailer $mailer
*/
public function handle(TicketMailer $mailer)
{
//harvest list of contacts to email;
$data['bccEmail'] = $this->ticket->getCCs();
$data['text'] = $this->ticketData['comment'];
$data['replyTo'] = 'ticket-123@support.invoiceninja.com';
//$toEmail = strtolower($this->ticket->contact->email); //todo
$toEmail = 'david@romulus.com.au';
$fromEmail = $this->ticket->getTicketFromEmail();
$fromName = $this->ticket->getTicketFromName();
$subject = $this->ticket->subject;
$view = 'ticket';
$mailer->sendTo($toEmail, $fromEmail, $fromName, $subject, $view, $data);
}
}

View file

@ -50,14 +50,6 @@ class HTMLUtils
return $purifier->purify($html);
}
public static function sanitizeJS($js) {
if(!stripos($js, '<script')) {
$js = "<script type=\"text/javascript\">{$js}</script>";
}
return $js;
}
public static function previousUrl($fallback)
{
$previous = url()->previous();

View file

@ -1,99 +0,0 @@
<?php
namespace App\Libraries;
class MoneyUtilsInvalidArgumentException extends \Exception {};
/**
* Class MoneyUtils
* @package App\Libraries
*/
class MoneyUtils
{
/**
* @var bool this is to prevent repeating initialization by loadCurrencies method
*/
protected static $initialized;
/**
* @var array holds currency code => exchange rate
*/
protected static $rates = [];
/**
* @var string holds base currency code
*/
protected static $baseCurency;
/**
* Loads currencies from cache, make $rates array and $baseCurrency
* @throws \Exception
*/
protected static function loadCurrencies()
{
if(self::$initialized) return;
$currencies = cache('currencies');
foreach ($currencies as $currency)
{
if(!self::$baseCurency && $currency->exchange_rate === 1.0000)
{
self::$baseCurency = $currency->code;
}
self::$rates[$currency->code] = $currency->exchange_rate;
}
self::$initialized = true;
}
/**
* Currency conversion
*
* @param $val value to convert
* @param $from currency to convert from
* @param $to currency to convert to
* @throws \Exception
* @throws MoneyUtilsInvalidArgumentException
*/
public static function convert($val, $from, $to)
{
return $val * self::getRate($from, $to);
}
/**
* Get exchange rate for `to` currency based on `from` currency
*
* @param $from
* @param $to
* @return float|int|mixed
* @throws \Exception
* @throws MoneyUtilsInvalidArgumentException
*/
public static function getRate($from, $to)
{
self::loadCurrencies();
if(!array_key_exists($from, self::$rates) || !array_key_exists($to, self::$rates))
{
throw new MoneyUtilsInvalidArgumentException('Invalid currency code $from or $to');
}
// if `from` currency is same as `base` currency, return the basic exchange rate for the `to` currency
if($from === self::$baseCurency)
{
return self::$rates[$to];
}
// if `to` currency is same as `base` currency, return the basic inverse of the `from` currency
if($to === self::$baseCurency)
{
return 1 / self::$rates[$from];
}
// Otherwise, return the `to` rate multiplied by the inverse of the `from` rate to get the
// relative exchange rate between the two currencies
return self::$rates[$to] * (1 / self::$rates[$from]);
}
}

View file

@ -18,12 +18,9 @@ use Session;
use stdClass;
use View;
use WePay;
use Nwidart\Modules\Facades\Module;
class Utils
{
protected static $cacheValues = [];
private static $weekdayNames = [
'Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday',
];
@ -365,14 +362,7 @@ class Utils
if (substr($field, 0, 1) == '-') {
$data[] = substr($field, 1);
} elseif ($module) {
if(strpos($field, '::') >= 1) {
$customField = explode('::', $field);
if(count($customField) == 2) {
$data[] = trans("texts.$customField[0]", [ 'VALUE' => $customField[1]], 'en');
}
} else {
$data[] = mtrans($module, $field);
}
$data[] = mtrans($module, $field);
} else {
$data[] = trans("texts.$field");
}
@ -557,9 +547,6 @@ class Utils
public static function getFromCache($id, $type)
{
if (!empty(static::$cacheValues[$type]) && !empty(static::$cacheValues[$type][$id])) {
return static::$cacheValues[$type][$id];
}
$cache = Cache::get($type);
if (! $cache) {
@ -572,11 +559,7 @@ class Utils
return $item->id == $id;
});
$res = $data->first();
if (!empty($res)) {
static::$cacheValues[$type][$id] = $res;
}
return $res;
return $data->first();
}
public static function formatNumber($value, $currencyId = false, $precision = 0)
@ -664,10 +647,6 @@ class Utils
return 'proposal_categories';
} elseif ($type === ENTITY_TASK_STATUS) {
return 'task_statuses';
} elseif ($type === ENTITY_TICKET_STATUS) {
return 'ticket_statuses';
} elseif ($type === ENTITY_TICKET_CATEGORY) {
return 'ticket_categories';
} else {
return $type . 's';
}
@ -761,10 +740,9 @@ class Utils
}
$timestamp = $dateTime->getTimestamp();
$timezone = Session::get(SESSION_TIMEZONE, DEFAULT_TIMEZONE);
$format = Session::get(SESSION_DATE_FORMAT, DEFAULT_DATE_FORMAT);
return self::timestampToString($timestamp, $timezone, $format);
return self::timestampToString($timestamp, false, $format);
}
public static function timestampToString($timestamp, $timezone, $format)
@ -799,22 +777,6 @@ class Utils
}
}
public static function toSqlDateTime($date, $formatResult = true)
{
if (! $date) {
return;
}
$format = Session::get(SESSION_DATE_FORMAT, DEFAULT_DATE_FORMAT);
$dateTime = DateTime::createFromFormat($format, $date);
if (! $dateTime) {
return $date;
} else {
return $formatResult ? $dateTime->format('Y-m-d H:i:s') : $dateTime;
}
}
public static function fromSqlDate($date, $formatResult = true)
{
if (! $date || $date == '0000-00-00') {
@ -874,8 +836,7 @@ class Utils
return '';
}
$variables = ['MONTH', 'QUARTER', 'YEAR', 'DATE_MONTH', 'DATE_YEAR'];
$yearOverlap = 0;
$variables = ['MONTH', 'QUARTER', 'YEAR'];
for ($i = 0; $i < count($variables); $i++) {
$variable = $variables[$i];
$regExp = '/:'.$variable.'[+-]?[\d]*/';
@ -897,9 +858,6 @@ class Utils
$offset = intval($minArray[1]) * -1;
}
$yearOverlap += self::getDateYearOverlap($variable, $offset);
if($variable === 'YEAR') $offset += $yearOverlap;
$locale = $client && $client->language_id ? $client->language->locale : null;
$val = self::getDatePart($variable, $offset, $locale);
$str = str_replace($match, $val, $str);
@ -918,54 +876,9 @@ class Utils
return self::getQuarter($offset);
} elseif ($part == 'YEAR') {
return self::getYear($offset);
} elseif ($part == 'DATE_MONTH') {
return self::getDateMonth($offset, $locale);
} elseif ($part == 'DATE_YEAR') {
return self::getDateYear($offset);
}
}
private static function getDateYearOverlap(string $part, int $offset): int
{
$offset = intval($offset);
switch ($part) {
case 'MONTH':
return self::getMonthYearOverlap($offset);
case 'QUARTER':
return self::getQuarterYearOverlap($offset);
}
return 0;
}
public static function getDateMonth($offset, $locale)
{
$timestamp = time();
$res = $timestamp + ($offset * 24 * 60 * 60);
$months = static::$months;
$month = intval(date('n', $res)) - 1;
$month = $month % 12;
if ($month < 0) {
$month += 12;
}
return trans('texts.' . $months[$month], [], $locale);
}
public static function getDateYear($offset)
{
$timestamp = time();
$res = $timestamp + ($offset * 24 * 60 * 60);
$year = intval(date('Y', $res));
return $year;
}
public static function getMonthOptions()
{
$months = [];
@ -1007,34 +920,6 @@ class Utils
return 'Q'.$quarter;
}
private static function getMonthYearOverlap(int $offset): int
{
$month = intval(date('n')) - 1;
$month += $offset;
if($month < 0){
$month += 1;
return (((abs($month) / 12 % 12) + 1) * -1);
}
return ($month / 12 % 12);
}
private static function getQuarterYearOverlap(int $offset): int
{
$month = intval(date('n')) - 1;
$quarter = floor(($month + 3) / 3);
$quarter += $offset - 1;
if($quarter < 0){
$quarter += 1;
return (((abs($quarter) / 4 % 4) + 1) * -1);
}
return ($quarter / 4 % 4);
}
private static function getYear($offset)
{
$year = intval(date('Y'));
@ -1375,7 +1260,6 @@ class Utils
'invoice_id',
'credit_id',
'invitation_id',
'ticket_id',
];
$fields1 = $entity1->getAttributes();
@ -1630,66 +1514,4 @@ class Utils
);
return strtr($s, $replace);
}
public static function hasModuleSettings() {
$module = Module::toCollection()->first(function($module) {
return View::exists($module->getLowerName() . '::settings');
});
return $module ? true : false;
}
public static function getModulesWithSettings() {
$modules = Module::toCollection()->filter(function($module) {
return View::exists($module->getLowerName() . '::settings');
});
return $modules;
}
/**
* @return array of file sizes, using a MAX of the php.ini variables upload_max_filesize and post_max_size
* and iterating down by / 2 until a min size of 100kB
*/
public function getMaxFileUploadSizes()
{
$maxUploadSize = $this->fileUploadMaxSize();
$selectArray = [];
while($maxUploadSize > 100) {
array_push($selectArray, [$maxUploadSize => $maxUploadSize]);
$maxUploadSize = $maxUploadSize / 2;
}
return array_reverse($selectArray);
}
/**
* @return Returns a file size limit in kilobytes based on the PHP upload_max_filesize and post_max_size
*/
public function fileUploadMaxSize() {
return min($this->parse_size(ini_get('post_max_size')), $this->parse_size(ini_get('upload_max_filesize')));
}
/**
* @param $size
* @return float in kilobytes to match laravel file size validator
*/
private function parse_size($size) {
$unit = preg_replace('/[^bkmgtpezy]/i', '', $size); // Remove the non-unit characters from the size.
$size = preg_replace('/[^0-9\.]/', '', $size); // Remove the non-numeric characters from the size.
if ($unit) {
// Find the position of the unit in the ordered string which is the power of magnitude to multiply a kilobyte by.
return round($size * pow(1024, stripos('bkmgtpezy', $unit[0])))/1024;
}
else {
return round($size)/1024;
}
}
}

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