Compare commits
295 commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
60fc35bfc8 | ||
|
|
b80f15ce96 | ||
|
|
2bc5f1e573 | ||
|
|
b9681a7af4 | ||
|
|
fecd35fc3e | ||
|
|
5eb380c1f9 | ||
|
|
f8edb57f5c | ||
|
|
452bd9a1b2 | ||
|
|
ca7592b4e3 | ||
|
|
5bb0229a3a | ||
|
|
a87b2235b2 | ||
|
|
84abe28c78 | ||
|
|
3c395ab933 | ||
|
|
c2511272ee | ||
|
|
c95abe1936 | ||
|
|
61be22f2d7 | ||
|
|
3733b2e882 | ||
|
|
b753c3192c | ||
|
|
50db50c305 | ||
|
|
1909878195 | ||
|
|
4e1eb7529a | ||
|
|
706888945a | ||
|
|
b88041540b | ||
|
|
b3f70e4767 | ||
|
|
9396e11036 | ||
|
|
130d8cb795 | ||
|
|
948a101ad8 | ||
|
|
659c81953c | ||
|
|
942cfa9b94 | ||
|
|
196fad46f6 | ||
|
|
7ed344a0d2 | ||
|
|
a104c3ece8 | ||
|
|
38b07bbfd2 | ||
|
|
0c5d2cdc4e | ||
|
|
a7901412a0 | ||
|
|
e61466cfd6 | ||
|
|
7c412b16e2 | ||
|
|
71aa4c9bcd | ||
|
|
6fd119bfd1 | ||
|
|
4629631dd0 | ||
|
|
e80672bb4c | ||
|
|
9b81f3250a | ||
|
|
ad77c7a38a | ||
|
|
136e64c054 | ||
|
|
e658dcd202 | ||
|
|
7bb7a8b7d5 | ||
|
|
1fa5044a0d | ||
|
|
148e4dd398 | ||
|
|
459f95bde3 | ||
|
|
5d20757ee5 | ||
|
|
7d8c86684c | ||
|
|
196f2d6a17 | ||
|
|
37495b7f2f | ||
|
|
199ed4c777 | ||
|
|
e0e04c27bf | ||
|
|
2a873ea586 | ||
|
|
268bf8b059 | ||
|
|
fc6344c24f | ||
|
|
e0754666cd | ||
|
|
1052f03398 | ||
|
|
ea5d2393b0 | ||
|
|
36b41c205c | ||
|
|
16f9cc41de | ||
|
|
55706ffbb8 | ||
|
|
9f9c61435d | ||
|
|
e85a802380 | ||
|
|
34adf53e5b | ||
|
|
2419859d23 | ||
|
|
8f3deee107 | ||
|
|
c0e7e7f3b2 | ||
|
|
8fcbd66bc2 | ||
|
|
4c37e44cf3 | ||
|
|
54deb818f4 | ||
|
|
9acfb40d42 | ||
|
|
cf189e548d | ||
|
|
f0507809cd | ||
|
|
03138e8598 | ||
|
|
540afd4d5a | ||
|
|
7edc1fb195 | ||
|
|
037655e0bb | ||
|
|
da564aa8ec | ||
|
|
e66e899346 | ||
|
|
d2491323a0 | ||
|
|
c20a8e5609 | ||
|
|
f28b90f42a | ||
|
|
eb56acf03a | ||
|
|
694d3ad0f0 | ||
|
|
9d2c0ea03b | ||
|
|
cd2cbadd40 | ||
|
|
a8c8392f8d | ||
|
|
b3784125c2 | ||
|
|
2ef88fb859 | ||
|
|
b6e925381c | ||
|
|
7f43913a3c | ||
|
|
762b06667c | ||
|
|
eb676a3b9d | ||
|
|
ecc5a00600 | ||
|
|
5f5a1a6740 | ||
|
|
bfd13719c9 | ||
|
|
84e344b61b | ||
|
|
86750892b5 | ||
|
|
1ed3c9df1f | ||
|
|
91e67eef75 | ||
|
|
6974bab120 | ||
|
|
fc3074ad51 | ||
|
|
a98ad5ba02 | ||
|
|
aadda80988 | ||
|
|
139e0a4b0e | ||
|
|
d45ae2c50b | ||
|
|
ddf376090a | ||
|
|
ab4a44ff18 | ||
|
|
58a09087db | ||
|
|
161161f628 | ||
|
|
0ffa9a971d | ||
|
|
536c9c4a82 | ||
|
|
869967ba6a | ||
|
|
1d1c650648 | ||
|
|
7c8d9f5c24 | ||
|
|
af7510a194 | ||
|
|
cb62e92af0 | ||
|
|
8aa90e1bb7 | ||
|
|
6bbcad249d | ||
|
|
9284d76481 | ||
|
|
3c82ba1343 | ||
|
|
419d9ea5eb | ||
|
|
19080af049 | ||
|
|
14ed415051 | ||
|
|
b71f32a3af | ||
|
|
5255997eba | ||
|
|
3e4bd48ed3 | ||
|
|
ddf2502df3 | ||
|
|
76153812dc | ||
|
|
dc62abd766 | ||
|
|
0fccf69e62 | ||
|
|
429f079cf5 | ||
|
|
c1f8f93c7f | ||
|
|
bdd73414c9 | ||
|
|
f3c6e1148c | ||
|
|
e106eda7e2 | ||
|
|
21c4721942 | ||
|
|
ae672a25b1 | ||
|
|
be39bf93d2 | ||
|
|
a6198ea6ec | ||
|
|
96d53f49c2 | ||
|
|
ac1e48455f | ||
|
|
7f2e8bfcd6 | ||
|
|
75d3ab3c3d | ||
|
|
0ce77608a4 | ||
|
|
d78e609081 | ||
|
|
5206bd4629 | ||
|
|
b0aba3359b | ||
|
|
d9afbe0c4b | ||
|
|
b98a569888 | ||
|
|
1ce3a023cd | ||
|
|
a996cd6bcf | ||
|
|
9357405f14 | ||
|
|
ded1acd66c | ||
|
|
ac6284a94a | ||
|
|
3e3ac4989c | ||
|
|
74710e9985 | ||
|
|
c69f9e652c | ||
|
|
45e65b1b52 | ||
|
|
f8c7ae1a73 | ||
|
|
67cb2b3215 | ||
|
|
de6919d37c | ||
|
|
4476c9eea2 | ||
|
|
a469870b2a | ||
|
|
2d70fa7ad8 | ||
|
|
fd39f98f74 | ||
|
|
068cda7392 | ||
|
|
1f8c4b922e | ||
|
|
7052919731 | ||
|
|
acc73d53dc | ||
|
|
f639ebae94 | ||
|
|
6263e839d8 | ||
|
|
dc0f47cd9e | ||
|
|
fe7035c6a6 | ||
|
|
50f2c80190 | ||
|
|
e7fe3c938f | ||
|
|
3335f78e52 | ||
|
|
2124417c31 | ||
|
|
0c6a8cee73 | ||
|
|
048ee7181e | ||
|
|
8b4976cacf | ||
|
|
1dada2035c | ||
|
|
cee4652b7c | ||
|
|
8e8414ab49 | ||
|
|
8ed210d618 | ||
|
|
51dd22d7b5 | ||
|
|
f134ee1b51 | ||
|
|
b97b47f7cc | ||
|
|
f4a4ccc3b1 | ||
|
|
3a13d99dff | ||
|
|
23bf67b69b | ||
|
|
d2fab1c747 | ||
|
|
800db04f1e | ||
|
|
b1c64bae6c | ||
|
|
54df8e5055 | ||
|
|
1119701683 | ||
|
|
4736d2bdab | ||
|
|
3bea02e22e | ||
|
|
4db7ef0fba | ||
|
|
f09f10753f | ||
|
|
b1689ca6de | ||
|
|
fcf846d530 | ||
|
|
904cff732c | ||
|
|
9a608bcc65 | ||
|
|
955307bdfe | ||
|
|
6eb2717d13 | ||
|
|
97019f22e9 | ||
|
|
aa97b0a9ac | ||
|
|
b854115389 | ||
|
|
14c140c520 | ||
|
|
ab739ae358 | ||
|
|
2e3d79a700 | ||
|
|
9df361a378 | ||
|
|
f9021b4102 | ||
|
|
925bcca617 | ||
|
|
694de222a7 | ||
|
|
69a563cac4 | ||
|
|
af13bde5f4 | ||
|
|
9d8291c0fe | ||
|
|
6f2c6fd905 | ||
|
|
6a9dae79c3 | ||
|
|
d9724c3c50 | ||
|
|
def674a2e4 | ||
|
|
d515126a5b | ||
|
|
097764f866 | ||
|
|
e14e27cac6 | ||
|
|
02890eee2a | ||
|
|
17b72a8d95 | ||
|
|
b238e18352 | ||
|
|
98ab5599e5 | ||
|
|
7f304028e6 | ||
|
|
90d132ad20 | ||
|
|
d7368851f5 | ||
|
|
f119a6f74c | ||
|
|
e8233f6cbd | ||
|
|
e6cb3dc5e7 | ||
|
|
46f0cd500f | ||
|
|
503178693f | ||
|
|
0c3c72aa8b | ||
|
|
19f3eb43eb | ||
|
|
31abf8b8b6 | ||
|
|
ec9f77b53d | ||
|
|
8d706b62d9 | ||
|
|
258b5c6453 | ||
|
|
00700765d9 | ||
|
|
733b597240 | ||
|
|
a5ae3aa6bc | ||
|
|
c666a867ab | ||
|
|
7164c52d4b | ||
|
|
cdb0c80acc | ||
|
|
3508cce983 | ||
|
|
96d9b162c9 | ||
|
|
fde4e553df | ||
|
|
afbd8df072 | ||
|
|
feb2f9ae82 | ||
|
|
a0e29467a3 | ||
|
|
52ac8e83d6 | ||
|
|
724381a75a | ||
|
|
af51e715a5 | ||
|
|
2d03b28a72 | ||
|
|
994899e17a | ||
|
|
0d515da62e | ||
|
|
87bbe4ceea | ||
|
|
cf24e11f14 | ||
|
|
cd4322c0c2 | ||
|
|
bee27dbb69 | ||
|
|
a01f879023 | ||
|
|
153e223010 | ||
|
|
f1c562be12 | ||
|
|
c765200599 | ||
|
|
572e307362 | ||
|
|
d443d3ac89 | ||
|
|
c0bad70c0d | ||
|
|
b249bf78c0 | ||
|
|
d7966689cf | ||
|
|
64b8c599a2 | ||
|
|
4418451b71 | ||
|
|
006bb1c6e4 | ||
|
|
bd8414f0d8 | ||
|
|
c991cde22d | ||
|
|
46d342fa63 | ||
|
|
aabb32b6ed | ||
|
|
fe81900402 | ||
|
|
cabb98a976 | ||
|
|
2174ce15d9 | ||
|
|
1da78d8f29 | ||
|
|
d71874a20a | ||
|
|
8eaa382604 | ||
|
|
febb465c71 | ||
|
|
0b501181ac | ||
|
|
fce09b036a | ||
|
|
ade15ec390 |
669 changed files with 104331 additions and 59523 deletions
5
.gitignore
vendored
5
.gitignore
vendored
|
|
@ -9,7 +9,8 @@
|
||||||
/public/packages
|
/public/packages
|
||||||
/public/vendor
|
/public/vendor
|
||||||
/resources/assets/bower
|
/resources/assets/bower
|
||||||
/storage
|
/storage/*.key
|
||||||
|
/storage/documents
|
||||||
/bootstrap/compiled.php
|
/bootstrap/compiled.php
|
||||||
/bootstrap/environment.php
|
/bootstrap/environment.php
|
||||||
/vendor
|
/vendor
|
||||||
|
|
@ -39,3 +40,5 @@ tests/_support/_generated/
|
||||||
/c3.php
|
/c3.php
|
||||||
|
|
||||||
_ide_helper.php
|
_ide_helper.php
|
||||||
|
storage/version.txt
|
||||||
|
storage/framework/.DS_Store
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,9 @@ group: deprecated-2017Q4
|
||||||
php:
|
php:
|
||||||
- 7.2
|
- 7.2
|
||||||
|
|
||||||
|
services:
|
||||||
|
- mysql
|
||||||
|
|
||||||
addons:
|
addons:
|
||||||
hosts:
|
hosts:
|
||||||
- www.ninja.test
|
- www.ninja.test
|
||||||
|
|
|
||||||
4
CODE_OF_CONDUCT.md
Normal file
4
CODE_OF_CONDUCT.md
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
# Invoice Ninja Code of Conduct
|
||||||
|
|
||||||
|
The development team has invested a tremendous amount of time and energy into this project. While we appreciate that bugs can be frustrating we ask that our community refrain from insults and snide remarks. We're happy to provide support to both our hosted and selfhosted communities but ask that feedback is always polite.
|
||||||
|
|
||||||
88
LICENSE
88
LICENSE
|
|
@ -1,47 +1,47 @@
|
||||||
Copyright (c) 2018 by Hillel Coren
|
Elastic License 2.0 (ELv2)
|
||||||
Invoice Ninja * https://www.invoiceninja.com
|
Elastic License
|
||||||
"CREATE. SEND. GET PAID"
|
|
||||||
|
|
||||||
All Rights Reserved
|
Acceptance
|
||||||
ATTRIBUTION ASSURANCE LICENSE (adapted from the original BSD license)
|
By using the software, you agree to all of the terms and conditions below.
|
||||||
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.
|
|
||||||
|
|
||||||
1. Redistributions of source code, in whole or part and with or without
|
Copyright License
|
||||||
modification (the "Code"), must prominently display this GPG-signed
|
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
|
||||||
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.
|
|
||||||
|
|
||||||
THIS FREE SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND
|
Limitations
|
||||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
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.
|
||||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
|
||||||
FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
|
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.
|
||||||
EVENT SHALL THE AUTHOR OR ANY CONTRIBUTOR BE LIABLE FOR
|
|
||||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
You may not alter, remove, or obscure any licensing, copyright, or other notices of the licensor in the software. Any use of the licensor’s trademarks is subject to applicable law.
|
||||||
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
|
||||||
EFFECTS OF UNAUTHORIZED OR MALICIOUS NETWORK ACCESS;
|
Patents
|
||||||
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
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.
|
||||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
|
|
||||||
AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
Notices
|
||||||
LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
You must ensure that anyone who gets a copy of any part of the software from you also gets a copy of these terms.
|
||||||
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
|
|
||||||
IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
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/
|
||||||
45
README.md
45
README.md
|
|
@ -4,14 +4,18 @@
|
||||||
|
|
||||||
# Invoice Ninja
|
# Invoice Ninja
|
||||||
|
|
||||||
[](https://travis-ci.org/invoiceninja/invoiceninja)
|
[](https://travis-ci.org/invoiceninja/invoiceninja)
|
||||||
[](https://invoice-ninja.readthedocs.io/en/latest/?badge=latest)
|
[](https://invoice-ninja.readthedocs.io/en/latest/?badge=latest)
|
||||||
|
|
||||||
## [Hosted](https://www.invoiceninja.com) | [Self-Hosted](https://www.invoiceninja.org)
|
## [Hosted](https://www.invoiceninja.com) | [Self-Hosted](https://www.invoiceninja.org)
|
||||||
|
|
||||||
### We're on Slack, join us at [slack.invoiceninja.com](http://slack.invoiceninja.com)
|
### We're on Slack, join us at [slack.invoiceninja.com](http://slack.invoiceninja.com) or if you like [StackOverflow](https://stackoverflow.com/tags/invoice-ninja/)
|
||||||
|
|
||||||
All Pro and Enterprise features from the hosted app are included in the open-source code. We offer a $20 per year white-label license to remove our branding for personal use.
|
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.
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
The self-host zip includes all third party libraries whereas downloading the code from GitHub requires using Composer to install the dependencies.
|
The self-host zip includes all third party libraries whereas downloading the code from GitHub requires using Composer to install the dependencies.
|
||||||
|
|
||||||
|
|
@ -19,22 +23,14 @@ The self-host zip includes all third party libraries whereas downloading the cod
|
||||||
* [Videos](https://www.youtube.com/channel/UCXAHcBvhW05PDtWYIq7WDFA/videos)
|
* [Videos](https://www.youtube.com/channel/UCXAHcBvhW05PDtWYIq7WDFA/videos)
|
||||||
* [User Guide](https://invoice-ninja.readthedocs.io/en/latest/)
|
* [User Guide](https://invoice-ninja.readthedocs.io/en/latest/)
|
||||||
* [Support Forum](https://www.invoiceninja.com/forums/forum/support/)
|
* [Support Forum](https://www.invoiceninja.com/forums/forum/support/)
|
||||||
* [Roadmap](https://trello.com/b/63BbiVVe/)
|
|
||||||
|
|
||||||
## Affiliates Programs
|
## Referral Program
|
||||||
* Referral Program (we pay you)
|
* Earn 50% of Pro & Enterprise Plans up to 4 years - [Learn more](https://www.invoiceninja.com/referral-program/)
|
||||||
* $100 per sign up paid over 3 years - [Learn more](https://www.invoiceninja.com/referral-program/)
|
|
||||||
* White-Label Reseller (you pay us)
|
|
||||||
* Hosted: $500 annually and either 10% of revenue or $1/user/month
|
|
||||||
* Self-Hosted: Contact us for volume license pricing
|
|
||||||
|
|
||||||
## Mobile Apps
|
## Mobile App
|
||||||
* Current: [github.com/invoiceninja/flutter-mobile](https://github.com/invoiceninja/flutter-mobile)
|
* [iPhone](https://itunes.apple.com/us/app/invoice-ninja/id1435514417?ls=1&mt=8)
|
||||||
* [iPhone](https://itunes.apple.com/us/app/invoice-ninja/id1435514417?ls=1&mt=8)
|
* [Android](https://play.google.com/store/apps/details?id=com.invoiceninja.flutter)
|
||||||
* [Android](https://play.google.com/store/apps/details?id=com.invoiceninja.flutter)
|
* [Source Code](https://github.com/invoiceninja/flutter-mobile)
|
||||||
* Legacy
|
|
||||||
* [iPhone](https://itunes.apple.com/us/app/invoice-ninja/id1220337560?ls=1&mt=8)
|
|
||||||
* [Android](https://play.google.com/store/apps/details?id=com.invoiceninja.invoiceninja)
|
|
||||||
|
|
||||||
## Installation Options
|
## Installation Options
|
||||||
* [Ansible](https://github.com/invoiceninja/ansible-installer)
|
* [Ansible](https://github.com/invoiceninja/ansible-installer)
|
||||||
|
|
@ -56,6 +52,8 @@ The self-host zip includes all third party libraries whereas downloading the cod
|
||||||
## Third Party Modules
|
## Third Party Modules
|
||||||
* [Event Scheduler](https://github.com/cytech/Scheduler-InvoiceNinja)
|
* [Event Scheduler](https://github.com/cytech/Scheduler-InvoiceNinja)
|
||||||
* [Manufacturer Module](https://github.com/dicarlosystems/manufacturer-invoiceninja)
|
* [Manufacturer Module](https://github.com/dicarlosystems/manufacturer-invoiceninja)
|
||||||
|
* [Point of Sale](https://github.com/dicarlosystems/pointofsale-invoiceninja)
|
||||||
|
* [Invoice Design Import/Export](https://github.com/feyst/invoicedesignexport)
|
||||||
|
|
||||||
> Feel free to email us for help if you're working on a module, we're happy to provide developer support.
|
> Feel free to email us for help if you're working on a module, we're happy to provide developer support.
|
||||||
|
|
||||||
|
|
@ -65,10 +63,10 @@ The self-host zip includes all third party libraries whereas downloading the cod
|
||||||
* [Shopping Cart](https://github.com/Scifabric/invoiceninjashoppingcart)
|
* [Shopping Cart](https://github.com/Scifabric/invoiceninjashoppingcart)
|
||||||
|
|
||||||
## Third Party Developers
|
## Third Party Developers
|
||||||
* [Some Techie](https://www.sometechie.com/customize-invoice-ninja/)
|
* [Bold Compass](https://boldcompass.com/customize-invoice-ninja/)
|
||||||
|
|
||||||
## Contributing
|
## Contributing
|
||||||
All contributors are welcome!
|
All contributors are welcome!
|
||||||
For information on how contribute to Invoice Ninja, please see our [contributing guide](CONTRIBUTING.md).
|
For information on how contribute to Invoice Ninja, please see our [contributing guide](CONTRIBUTING.md).
|
||||||
|
|
||||||
## Credits
|
## Credits
|
||||||
|
|
@ -80,13 +78,16 @@ For information on how contribute to Invoice Ninja, please see our [contributing
|
||||||
* [Troels Liebe Bentsen](https://github.com/tlbdk)
|
* [Troels Liebe Bentsen](https://github.com/tlbdk)
|
||||||
* [Jeramy Simpson](https://github.com/JeramyMywork) - [MyWork](https://www.mywork.com.au)
|
* [Jeramy Simpson](https://github.com/JeramyMywork) - [MyWork](https://www.mywork.com.au)
|
||||||
* [Sigitas Limontas](https://lt.linkedin.com/in/sigitaslimontas)
|
* [Sigitas Limontas](https://lt.linkedin.com/in/sigitaslimontas)
|
||||||
* [Joshua Dwire](https://github.com/joshuadwire) - [Some Techie](https://www.sometechie.com)
|
* [Joshua Dwire](https://github.com/joshuadwire) - [Bold Compass](https://boldcompass.com/)
|
||||||
* [Holger Lösken](https://github.com/codedge) - [codedge](http://codedge.de)
|
* [Holger Lösken](https://github.com/codedge) - [codedge](http://codedge.de)
|
||||||
* [Samuel Laulhau](https://github.com/lalop) - [Lalop](http://lalop.co/)
|
* [Samuel Laulhau](https://github.com/lalop) - [Lalop](http://lalop.co/)
|
||||||
* [Alexander Zamponi](https://github.com/alexz707)
|
* [Alexander Zamponi](https://github.com/alexz707)
|
||||||
* [Matthieu Calie](https://github.com/Matth--)
|
* [Matthieu Calie](https://github.com/Matth--)
|
||||||
* [Suhas Sunil Gaikwad](https://github.com/Suhas-Gaikwad) - (Security)
|
* [Christopher Di Carlo](https://github.com/dicarlosystems) - [Di Carlo Systems Inc.](https://www.dicarlosystems.ca)
|
||||||
|
* [Kristian Feldsam](https://github.com/feldsam) - [FeldHost™](https://www.feldhost.net)
|
||||||
|
* [Suhas Sunil Gaikwad](https://github.com/Suhas-Gaikwad)
|
||||||
|
* [Mike Skaggs](https://github.com/titan-fail)
|
||||||
|
|
||||||
## License
|
## License
|
||||||
Invoice Ninja is released under the Attribution Assurance License.
|
Invoice Ninja is released under the Attribution Assurance License.
|
||||||
See [LICENSE](LICENSE) for details.
|
See [LICENSE](LICENSE) for details.
|
||||||
|
|
|
||||||
|
|
@ -57,7 +57,7 @@ class ChargeRenewalInvoices extends Command
|
||||||
$this->paymentService = $paymentService;
|
$this->paymentService = $paymentService;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function fire()
|
public function handle()
|
||||||
{
|
{
|
||||||
$this->info(date('r').' ChargeRenewalInvoices...');
|
$this->info(date('r').' ChargeRenewalInvoices...');
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -67,7 +67,7 @@ class CheckData extends Command
|
||||||
protected $log = '';
|
protected $log = '';
|
||||||
protected $isValid = true;
|
protected $isValid = true;
|
||||||
|
|
||||||
public function fire()
|
public function handle()
|
||||||
{
|
{
|
||||||
$this->logMessage(date('Y-m-d h:i:s') . ' Running CheckData...');
|
$this->logMessage(date('Y-m-d h:i:s') . ' Running CheckData...');
|
||||||
|
|
||||||
|
|
@ -91,7 +91,7 @@ class CheckData extends Command
|
||||||
|
|
||||||
if (! $this->option('client_id')) {
|
if (! $this->option('client_id')) {
|
||||||
$this->checkOAuth();
|
$this->checkOAuth();
|
||||||
$this->checkInvitations();
|
//$this->checkInvitations();
|
||||||
$this->checkAccountData();
|
$this->checkAccountData();
|
||||||
$this->checkLookupData();
|
$this->checkLookupData();
|
||||||
$this->checkFailedJobs();
|
$this->checkFailedJobs();
|
||||||
|
|
@ -427,7 +427,7 @@ class CheckData extends Command
|
||||||
$queueDB = config('queue.connections.database.connection');
|
$queueDB = config('queue.connections.database.connection');
|
||||||
$count = DB::connection($queueDB)->table('failed_jobs')->count();
|
$count = DB::connection($queueDB)->table('failed_jobs')->count();
|
||||||
|
|
||||||
if ($count > 0) {
|
if ($count > 25) {
|
||||||
$this->isValid = false;
|
$this->isValid = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -676,6 +676,8 @@ class CheckData extends Command
|
||||||
|
|
||||||
foreach ($clients as $client) {
|
foreach ($clients as $client) {
|
||||||
$this->logMessage("=== Company: {$client->company_id} Account:{$client->account_id} Client:{$client->id} Balance:{$client->balance} Actual Balance:{$client->actual_balance} ===");
|
$this->logMessage("=== Company: {$client->company_id} Account:{$client->account_id} Client:{$client->id} Balance:{$client->balance} Actual Balance:{$client->actual_balance} ===");
|
||||||
|
|
||||||
|
/*
|
||||||
$foundProblem = false;
|
$foundProblem = false;
|
||||||
$lastBalance = 0;
|
$lastBalance = 0;
|
||||||
$lastAdjustment = 0;
|
$lastAdjustment = 0;
|
||||||
|
|
@ -838,6 +840,7 @@ class CheckData extends Command
|
||||||
->where('id', $client->id)
|
->where('id', $client->id)
|
||||||
->update($data);
|
->update($data);
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -37,7 +37,7 @@ class CreateLuisData extends Command
|
||||||
/**
|
/**
|
||||||
* @return bool
|
* @return bool
|
||||||
*/
|
*/
|
||||||
public function fire()
|
public function handle()
|
||||||
{
|
{
|
||||||
$this->fakerField = $this->argument('faker_field');
|
$this->fakerField = $this->argument('faker_field');
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,11 +2,16 @@
|
||||||
|
|
||||||
namespace App\Console\Commands;
|
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\AccountRepository;
|
||||||
use App\Ninja\Repositories\ClientRepository;
|
use App\Ninja\Repositories\ClientRepository;
|
||||||
use App\Ninja\Repositories\ExpenseRepository;
|
use App\Ninja\Repositories\ExpenseRepository;
|
||||||
use App\Ninja\Repositories\InvoiceRepository;
|
use App\Ninja\Repositories\InvoiceRepository;
|
||||||
use App\Ninja\Repositories\PaymentRepository;
|
use App\Ninja\Repositories\PaymentRepository;
|
||||||
|
use App\Ninja\Repositories\TicketRepository;
|
||||||
use App\Ninja\Repositories\VendorRepository;
|
use App\Ninja\Repositories\VendorRepository;
|
||||||
use App\Ninja\Repositories\TaskRepository;
|
use App\Ninja\Repositories\TaskRepository;
|
||||||
use App\Ninja\Repositories\ProjectRepository;
|
use App\Ninja\Repositories\ProjectRepository;
|
||||||
|
|
@ -15,6 +20,7 @@ use App\Models\TaxRate;
|
||||||
use App\Models\Project;
|
use App\Models\Project;
|
||||||
use App\Models\ExpenseCategory;
|
use App\Models\ExpenseCategory;
|
||||||
use Auth;
|
use Auth;
|
||||||
|
use Carbon\Carbon;
|
||||||
use Faker\Factory;
|
use Faker\Factory;
|
||||||
use Illuminate\Console\Command;
|
use Illuminate\Console\Command;
|
||||||
use Utils;
|
use Utils;
|
||||||
|
|
@ -48,8 +54,12 @@ class CreateTestData extends Command
|
||||||
* @param ExpenseRepository $expenseRepo
|
* @param ExpenseRepository $expenseRepo
|
||||||
* @param TaskRepository $taskRepo
|
* @param TaskRepository $taskRepo
|
||||||
* @param AccountRepository $accountRepo
|
* @param AccountRepository $accountRepo
|
||||||
|
* @param TicketRepository $ticketRepo
|
||||||
|
* @param ProjectRepository $projectRepo
|
||||||
*/
|
*/
|
||||||
|
|
||||||
public function __construct(
|
public function __construct(
|
||||||
|
TicketRepository $ticketRepo,
|
||||||
ClientRepository $clientRepo,
|
ClientRepository $clientRepo,
|
||||||
InvoiceRepository $invoiceRepo,
|
InvoiceRepository $invoiceRepo,
|
||||||
PaymentRepository $paymentRepo,
|
PaymentRepository $paymentRepo,
|
||||||
|
|
@ -71,12 +81,13 @@ class CreateTestData extends Command
|
||||||
$this->taskRepo = $taskRepo;
|
$this->taskRepo = $taskRepo;
|
||||||
$this->projectRepo = $projectRepo;
|
$this->projectRepo = $projectRepo;
|
||||||
$this->accountRepo = $accountRepo;
|
$this->accountRepo = $accountRepo;
|
||||||
|
$this->ticketRepo = $ticketRepo;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return bool
|
* @return bool
|
||||||
*/
|
*/
|
||||||
public function fire()
|
public function handle()
|
||||||
{
|
{
|
||||||
if (Utils::isNinjaProd()) {
|
if (Utils::isNinjaProd()) {
|
||||||
$this->info('Unable to run in production');
|
$this->info('Unable to run in production');
|
||||||
|
|
@ -103,6 +114,8 @@ class CreateTestData extends Command
|
||||||
Auth::loginUsingId(1);
|
Auth::loginUsingId(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//$this->createTicketStubs();
|
||||||
|
//$this->createTicketTemplates();
|
||||||
$this->createClients();
|
$this->createClients();
|
||||||
$this->createVendors();
|
$this->createVendors();
|
||||||
$this->createOtherObjects();
|
$this->createOtherObjects();
|
||||||
|
|
@ -134,6 +147,84 @@ class CreateTestData extends Command
|
||||||
$this->createInvoices($client);
|
$this->createInvoices($client);
|
||||||
$this->createInvoices($client, true);
|
$this->createInvoices($client, true);
|
||||||
$this->createTasks($client);
|
$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}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -362,6 +362,7 @@ class InitLookup extends Command
|
||||||
DB::statement('truncate lookup_invitations');
|
DB::statement('truncate lookup_invitations');
|
||||||
DB::statement('truncate lookup_proposal_invitations');
|
DB::statement('truncate lookup_proposal_invitations');
|
||||||
DB::statement('truncate lookup_account_tokens');
|
DB::statement('truncate lookup_account_tokens');
|
||||||
|
DB::statement('truncate lookup_ticket_invitations');
|
||||||
DB::statement('SET FOREIGN_KEY_CHECKS = 1');
|
DB::statement('SET FOREIGN_KEY_CHECKS = 1');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
113
app/Console/Commands/MakeModuleSettings.php
Normal file
113
app/Console/Commands/MakeModuleSettings.php
Normal file
|
|
@ -0,0 +1,113 @@
|
||||||
|
<?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';
|
||||||
|
}
|
||||||
|
}
|
||||||
121
app/Console/Commands/MobileLocalization.php
Normal file
121
app/Console/Commands/MobileLocalization.php
Normal file
|
|
@ -0,0 +1,121 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Console\Commands;
|
||||||
|
|
||||||
|
use Illuminate\Console\Command;
|
||||||
|
use App\Models\DbServer;
|
||||||
|
use App\Models\User;
|
||||||
|
use App\Models\Company;
|
||||||
|
use App\Libraries\CurlUtils;
|
||||||
|
|
||||||
|
class MobileLocalization extends Command
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* The name and signature of the console command.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $signature = 'ninja:mobile-localization {--type=}';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The console command description.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $description = 'Generate mobile localization resources';
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new command instance.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
parent::__construct();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Execute the console command.
|
||||||
|
*
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
public function handle()
|
||||||
|
{
|
||||||
|
$type = strtolower($this->option('type'));
|
||||||
|
|
||||||
|
switch ($type) {
|
||||||
|
case 'laravel':
|
||||||
|
$this->laravelResources();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
$this->flutterResources();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private function laravelResources()
|
||||||
|
{
|
||||||
|
$resources = $this->getResources();
|
||||||
|
|
||||||
|
foreach ($resources as $key => $val) {
|
||||||
|
$transKey = "texts.{$key}";
|
||||||
|
if (trans($transKey) == $transKey) {
|
||||||
|
echo "'$key' => '$val',\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private function flutterResources()
|
||||||
|
{
|
||||||
|
$languages = cache('languages');
|
||||||
|
$resources = $this->getResources();
|
||||||
|
|
||||||
|
foreach ($languages as $language) {
|
||||||
|
if ($language->locale == 'en') {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
echo "'{$language->locale}': {\n";
|
||||||
|
|
||||||
|
foreach ($resources as $key => $val) {
|
||||||
|
$text = trim(addslashes(trans("texts.{$key}", [], $language->locale)));
|
||||||
|
if (substr($text, 0, 6) == 'texts.') {
|
||||||
|
$text = $resources->$key;
|
||||||
|
}
|
||||||
|
|
||||||
|
$text = str_replace(array('<b>', '</b>'), '', $text);
|
||||||
|
$text = str_replace(array('<i>', '</i>'), '', $text);
|
||||||
|
$text = str_replace(array('<strong>', '</strong>'), '', $text);
|
||||||
|
|
||||||
|
echo "'$key': '$text',\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
echo "},\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private function getResources()
|
||||||
|
{
|
||||||
|
$url = 'https://raw.githubusercontent.com/invoiceninja/flutter-client/develop/lib/utils/i18n.dart';
|
||||||
|
$data = CurlUtils::get($url);
|
||||||
|
|
||||||
|
$start = strpos($data, 'do not remove comment') + 25;
|
||||||
|
$end = strpos($data, '},', $start);
|
||||||
|
$data = substr($data, $start, $end - $start - 5);
|
||||||
|
|
||||||
|
$data = str_replace("\n", "", $data);
|
||||||
|
$data = str_replace("\"", "\'", $data);
|
||||||
|
$data = str_replace("'", "\"", $data);
|
||||||
|
|
||||||
|
return json_decode('{' . rtrim($data, ',') . '}');
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function getOptions()
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
['type', null, InputOption::VALUE_OPTIONAL, 'Type', null],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -21,7 +21,7 @@ class PruneData extends Command
|
||||||
*/
|
*/
|
||||||
protected $description = 'Delete inactive accounts';
|
protected $description = 'Delete inactive accounts';
|
||||||
|
|
||||||
public function fire()
|
public function handle()
|
||||||
{
|
{
|
||||||
$this->info(date('r').' Running PruneData...');
|
$this->info(date('r').' Running PruneData...');
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,7 @@ class RemoveOrphanedDocuments extends Command
|
||||||
*/
|
*/
|
||||||
protected $description = 'Removes old documents not associated with an expense or invoice';
|
protected $description = 'Removes old documents not associated with an expense or invoice';
|
||||||
|
|
||||||
public function fire()
|
public function handle()
|
||||||
{
|
{
|
||||||
$this->info(date('r').' Running RemoveOrphanedDocuments...');
|
$this->info(date('r').' Running RemoveOrphanedDocuments...');
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,7 @@ class ResetData extends Command
|
||||||
*/
|
*/
|
||||||
protected $description = 'Reset data';
|
protected $description = 'Reset data';
|
||||||
|
|
||||||
public function fire()
|
public function handle()
|
||||||
{
|
{
|
||||||
$this->info(date('r') . ' Running ResetData...');
|
$this->info(date('r') . ' Running ResetData...');
|
||||||
|
|
||||||
|
|
|
||||||
79
app/Console/Commands/SendOverdueTickets.php
Normal file
79
app/Console/Commands/SendOverdueTickets.php
Normal file
|
|
@ -0,0 +1,79 @@
|
||||||
|
<?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],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -48,7 +48,7 @@ class SendRecurringInvoices extends Command
|
||||||
$this->recurringExpenseRepo = $recurringExpenseRepo;
|
$this->recurringExpenseRepo = $recurringExpenseRepo;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function fire()
|
public function handle()
|
||||||
{
|
{
|
||||||
$this->info(date('r') . ' Running SendRecurringInvoices...');
|
$this->info(date('r') . ' Running SendRecurringInvoices...');
|
||||||
|
|
||||||
|
|
@ -81,8 +81,8 @@ class SendRecurringInvoices extends Command
|
||||||
$invoices = Invoice::with('account.timezone', 'invoice_items', 'client', 'user')
|
$invoices = Invoice::with('account.timezone', 'invoice_items', 'client', 'user')
|
||||||
->whereRaw('is_deleted IS FALSE AND deleted_at IS NULL AND is_recurring IS TRUE AND is_public IS TRUE AND frequency_id > 0 AND start_date <= ? AND (end_date IS NULL OR end_date >= ?)', [$today, $today])
|
->whereRaw('is_deleted IS FALSE AND deleted_at IS NULL AND is_recurring IS TRUE AND is_public IS TRUE AND frequency_id > 0 AND start_date <= ? AND (end_date IS NULL OR end_date >= ?)', [$today, $today])
|
||||||
->orderBy('id', 'asc')
|
->orderBy('id', 'asc')
|
||||||
->get();
|
->cursor();
|
||||||
$this->info(date('r ') . $invoices->count() . ' recurring invoice(s) found');
|
$this->info(date('r ') . ' Recurring invoice(s) found');
|
||||||
|
|
||||||
foreach ($invoices as $recurInvoice) {
|
foreach ($invoices as $recurInvoice) {
|
||||||
$shouldSendToday = $recurInvoice->shouldSendToday();
|
$shouldSendToday = $recurInvoice->shouldSendToday();
|
||||||
|
|
|
||||||
|
|
@ -70,7 +70,7 @@ class SendReminders extends Command
|
||||||
$this->userMailer = $userMailer;
|
$this->userMailer = $userMailer;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function fire()
|
public function handle()
|
||||||
{
|
{
|
||||||
$this->info(date('r') . ' Running SendReminders...');
|
$this->info(date('r') . ' Running SendReminders...');
|
||||||
|
|
||||||
|
|
@ -141,8 +141,14 @@ class SendReminders extends Command
|
||||||
$account->loadLocalizationSettings($invoice->client); // support trans to add fee line item
|
$account->loadLocalizationSettings($invoice->client); // support trans to add fee line item
|
||||||
$number = preg_replace('/[^0-9]/', '', $reminder);
|
$number = preg_replace('/[^0-9]/', '', $reminder);
|
||||||
|
|
||||||
$amount = $account->account_email_settings->{"late_fee{$number}_amount"};
|
if ($invoice->isQuote()) {
|
||||||
$percent = $account->account_email_settings->{"late_fee{$number}_percent"};
|
$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"};
|
||||||
|
}
|
||||||
|
|
||||||
$this->invoiceRepo->setLateFee($invoice, $amount, $percent);
|
$this->invoiceRepo->setLateFee($invoice, $amount, $percent);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -184,6 +190,18 @@ class SendReminders extends Command
|
||||||
$this->info(date('r') . ' Send email: ' . $invoice->id);
|
$this->info(date('r') . ' Send email: ' . $invoice->id);
|
||||||
dispatch(new SendInvoiceEmail($invoice, $invoice->user_id, 'reminder4'));
|
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'));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -211,8 +229,8 @@ class SendReminders extends Command
|
||||||
// send email as user
|
// send email as user
|
||||||
auth()->onceUsingId($user->id);
|
auth()->onceUsingId($user->id);
|
||||||
|
|
||||||
$report = dispatch(new RunReport($scheduledReport->user, $reportType, $config, true));
|
$report = dispatch_now(new RunReport($scheduledReport->user, $reportType, $config, $account, true));
|
||||||
$file = dispatch(new ExportReportResults($scheduledReport->user, $config['export_format'], $reportType, $report->exportParams));
|
$file = dispatch_now(new ExportReportResults($scheduledReport->user, $config['export_format'], $reportType, $report->exportParams));
|
||||||
|
|
||||||
if ($file) {
|
if ($file) {
|
||||||
try {
|
try {
|
||||||
|
|
@ -240,13 +258,24 @@ class SendReminders extends Command
|
||||||
if (config('ninja.exchange_rates_enabled')) {
|
if (config('ninja.exchange_rates_enabled')) {
|
||||||
$this->info(date('r') . ' Loading latest exchange rates...');
|
$this->info(date('r') . ' Loading latest exchange rates...');
|
||||||
|
|
||||||
$response = CurlUtils::get(config('ninja.exchange_rates_url'));
|
$url = config('ninja.exchange_rates_url');
|
||||||
|
$apiKey = config('ninja.exchange_rates_api_key');
|
||||||
|
$url = str_replace('{apiKey}', $apiKey, $url);
|
||||||
|
|
||||||
|
$response = CurlUtils::get($url);
|
||||||
$data = json_decode($response);
|
$data = json_decode($response);
|
||||||
|
|
||||||
if ($data && property_exists($data, 'rates')) {
|
if ($data && property_exists($data, 'rates') && property_exists($data, 'base')) {
|
||||||
Currency::whereCode(config('ninja.exchange_rates_base'))->update(['exchange_rate' => 1]);
|
$base = config('ninja.exchange_rates_base');
|
||||||
|
|
||||||
|
// should calculate to different base
|
||||||
|
$recalculate = ($data->base != $base);
|
||||||
|
|
||||||
foreach ($data->rates as $code => $rate) {
|
foreach ($data->rates as $code => $rate) {
|
||||||
|
if($recalculate) {
|
||||||
|
$rate = 1 / $data->rates->{$base} * $rate;
|
||||||
|
}
|
||||||
|
|
||||||
Currency::whereCode($code)->update(['exchange_rate' => $rate]);
|
Currency::whereCode($code)->update(['exchange_rate' => $rate]);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|
|
||||||
|
|
@ -48,7 +48,7 @@ class SendRenewalInvoices extends Command
|
||||||
$this->accountRepo = $repo;
|
$this->accountRepo = $repo;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function fire()
|
public function handle()
|
||||||
{
|
{
|
||||||
$this->info(date('r').' Running SendRenewalInvoices...');
|
$this->info(date('r').' Running SendRenewalInvoices...');
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -37,7 +37,7 @@ class TestOFX extends Command
|
||||||
$this->bankAccountService = $bankAccountService;
|
$this->bankAccountService = $bankAccountService;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function fire()
|
public function handle()
|
||||||
{
|
{
|
||||||
$this->info(date('r').' Running TestOFX...');
|
$this->info(date('r').' Running TestOFX...');
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -27,7 +27,7 @@ class UpdateKey extends Command
|
||||||
*/
|
*/
|
||||||
protected $description = 'Update application key';
|
protected $description = 'Update application key';
|
||||||
|
|
||||||
public function fire()
|
public function handle()
|
||||||
{
|
{
|
||||||
$this->info(date('r') . ' Running UpdateKey...');
|
$this->info(date('r') . ' Running UpdateKey...');
|
||||||
|
|
||||||
|
|
|
||||||
2
app/Console/Commands/stubs/command.stub
Executable file → Normal file
2
app/Console/Commands/stubs/command.stub
Executable file → Normal file
|
|
@ -37,7 +37,7 @@ class $CLASS$ extends Command
|
||||||
*
|
*
|
||||||
* @return mixed
|
* @return mixed
|
||||||
*/
|
*/
|
||||||
public function fire()
|
public function handle()
|
||||||
{
|
{
|
||||||
//
|
//
|
||||||
}
|
}
|
||||||
|
|
|
||||||
0
app/Console/Commands/stubs/composer.stub
Executable file → Normal file
0
app/Console/Commands/stubs/composer.stub
Executable file → Normal file
0
app/Console/Commands/stubs/controller-plain.stub
Executable file → Normal file
0
app/Console/Commands/stubs/controller-plain.stub
Executable file → Normal file
0
app/Console/Commands/stubs/controller.stub
Executable file → Normal file
0
app/Console/Commands/stubs/controller.stub
Executable file → Normal file
0
app/Console/Commands/stubs/event.stub
Executable file → Normal file
0
app/Console/Commands/stubs/event.stub
Executable file → Normal file
0
app/Console/Commands/stubs/job.stub
Executable file → Normal file
0
app/Console/Commands/stubs/job.stub
Executable file → Normal file
0
app/Console/Commands/stubs/json.stub
Executable file → Normal file
0
app/Console/Commands/stubs/json.stub
Executable file → Normal file
0
app/Console/Commands/stubs/listener.stub
Executable file → Normal file
0
app/Console/Commands/stubs/listener.stub
Executable file → Normal file
0
app/Console/Commands/stubs/mail.stub
Executable file → Normal file
0
app/Console/Commands/stubs/mail.stub
Executable file → Normal file
0
app/Console/Commands/stubs/middleware.stub
Executable file → Normal file
0
app/Console/Commands/stubs/middleware.stub
Executable file → Normal file
0
app/Console/Commands/stubs/migration/add.stub
Executable file → Normal file
0
app/Console/Commands/stubs/migration/add.stub
Executable file → Normal file
0
app/Console/Commands/stubs/migration/create.stub
Executable file → Normal file
0
app/Console/Commands/stubs/migration/create.stub
Executable file → Normal file
0
app/Console/Commands/stubs/migration/delete.stub
Executable file → Normal file
0
app/Console/Commands/stubs/migration/delete.stub
Executable file → Normal file
0
app/Console/Commands/stubs/migration/drop.stub
Executable file → Normal file
0
app/Console/Commands/stubs/migration/drop.stub
Executable file → Normal file
0
app/Console/Commands/stubs/migration/plain.stub
Executable file → Normal file
0
app/Console/Commands/stubs/migration/plain.stub
Executable file → Normal file
0
app/Console/Commands/stubs/model.stub
Executable file → Normal file
0
app/Console/Commands/stubs/model.stub
Executable file → Normal file
8
app/Console/Commands/stubs/module-settings-routes.stub
Normal file
8
app/Console/Commands/stubs/module-settings-routes.stub
Normal file
|
|
@ -0,0 +1,8 @@
|
||||||
|
|
||||||
|
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');
|
||||||
|
});
|
||||||
31
app/Console/Commands/stubs/module-settings-view.stub
Normal file
31
app/Console/Commands/stubs/module-settings-view.stub
Normal file
|
|
@ -0,0 +1,31 @@
|
||||||
|
@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
|
||||||
0
app/Console/Commands/stubs/notification.stub
Executable file → Normal file
0
app/Console/Commands/stubs/notification.stub
Executable file → Normal file
0
app/Console/Commands/stubs/provider.stub
Executable file → Normal file
0
app/Console/Commands/stubs/provider.stub
Executable file → Normal file
0
app/Console/Commands/stubs/request.stub
Executable file → Normal file
0
app/Console/Commands/stubs/request.stub
Executable file → Normal file
0
app/Console/Commands/stubs/route-provider.stub
Executable file → Normal file
0
app/Console/Commands/stubs/route-provider.stub
Executable file → Normal file
0
app/Console/Commands/stubs/routes.stub
Executable file → Normal file
0
app/Console/Commands/stubs/routes.stub
Executable file → Normal file
0
app/Console/Commands/stubs/scaffold/config.stub
Executable file → Normal file
0
app/Console/Commands/stubs/scaffold/config.stub
Executable file → Normal file
0
app/Console/Commands/stubs/scaffold/provider.stub
Executable file → Normal file
0
app/Console/Commands/stubs/scaffold/provider.stub
Executable file → Normal file
0
app/Console/Commands/stubs/seeder.stub
Executable file → Normal file
0
app/Console/Commands/stubs/seeder.stub
Executable file → Normal file
0
app/Console/Commands/stubs/start.stub
Executable file → Normal file
0
app/Console/Commands/stubs/start.stub
Executable file → Normal file
0
app/Console/Commands/stubs/views.stub
Executable file → Normal file
0
app/Console/Commands/stubs/views.stub
Executable file → Normal file
0
app/Console/Commands/stubs/views/master.stub
Executable file → Normal file
0
app/Console/Commands/stubs/views/master.stub
Executable file → Normal file
|
|
@ -30,6 +30,9 @@ class Kernel extends ConsoleKernel
|
||||||
'App\Console\Commands\InitLookup',
|
'App\Console\Commands\InitLookup',
|
||||||
'App\Console\Commands\CalculatePayouts',
|
'App\Console\Commands\CalculatePayouts',
|
||||||
'App\Console\Commands\UpdateKey',
|
'App\Console\Commands\UpdateKey',
|
||||||
|
'App\Console\Commands\MobileLocalization',
|
||||||
|
'App\Console\Commands\SendOverdueTickets',
|
||||||
|
'App\Console\Commands\MakeModuleSettings',
|
||||||
];
|
];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -44,13 +47,13 @@ class Kernel extends ConsoleKernel
|
||||||
$logFile = storage_path() . '/logs/cron.log';
|
$logFile = storage_path() . '/logs/cron.log';
|
||||||
|
|
||||||
$schedule
|
$schedule
|
||||||
->command('ninja:send-invoices --force')
|
->command('ninja:send-invoices')
|
||||||
->sendOutputTo($logFile)
|
->sendOutputTo($logFile)
|
||||||
->withoutOverlapping()
|
->withoutOverlapping()
|
||||||
->hourly();
|
->hourly();
|
||||||
|
|
||||||
$schedule
|
$schedule
|
||||||
->command('ninja:send-reminders --force')
|
->command('ninja:send-reminders')
|
||||||
->sendOutputTo($logFile)
|
->sendOutputTo($logFile)
|
||||||
->daily();
|
->daily();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,7 @@ if (! defined('APP_NAME')) {
|
||||||
define('ENTITY_INVOICE_ITEM', 'invoice_item');
|
define('ENTITY_INVOICE_ITEM', 'invoice_item');
|
||||||
define('ENTITY_INVITATION', 'invitation');
|
define('ENTITY_INVITATION', 'invitation');
|
||||||
define('ENTITY_RECURRING_INVOICE', 'recurring_invoice');
|
define('ENTITY_RECURRING_INVOICE', 'recurring_invoice');
|
||||||
|
define('ENTITY_RECURRING_QUOTE', 'recurring_quote');
|
||||||
define('ENTITY_PAYMENT', 'payment');
|
define('ENTITY_PAYMENT', 'payment');
|
||||||
define('ENTITY_CREDIT', 'credit');
|
define('ENTITY_CREDIT', 'credit');
|
||||||
define('ENTITY_QUOTE', 'quote');
|
define('ENTITY_QUOTE', 'quote');
|
||||||
|
|
@ -47,6 +48,13 @@ if (! defined('APP_NAME')) {
|
||||||
define('ENTITY_PROPOSAL_SNIPPET', 'proposal_snippet');
|
define('ENTITY_PROPOSAL_SNIPPET', 'proposal_snippet');
|
||||||
define('ENTITY_PROPOSAL_CATEGORY', 'proposal_category');
|
define('ENTITY_PROPOSAL_CATEGORY', 'proposal_category');
|
||||||
define('ENTITY_PROPOSAL_INVITATION', 'proposal_invitation');
|
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 = [
|
$permissionEntities = [
|
||||||
ENTITY_CLIENT,
|
ENTITY_CLIENT,
|
||||||
|
|
@ -59,10 +67,11 @@ if (! defined('APP_NAME')) {
|
||||||
ENTITY_PROJECT,
|
ENTITY_PROJECT,
|
||||||
ENTITY_PROPOSAL,
|
ENTITY_PROPOSAL,
|
||||||
ENTITY_QUOTE,
|
ENTITY_QUOTE,
|
||||||
|
'dashboard',
|
||||||
'reports',
|
'reports',
|
||||||
|
ENTITY_TICKET,
|
||||||
ENTITY_TASK,
|
ENTITY_TASK,
|
||||||
ENTITY_VENDOR,
|
ENTITY_VENDOR,
|
||||||
ENTITY_RECURRING_INVOICE,
|
|
||||||
];
|
];
|
||||||
|
|
||||||
define('PERMISSION_ENTITIES', json_encode($permissionEntities));
|
define('PERMISSION_ENTITIES', json_encode($permissionEntities));
|
||||||
|
|
@ -95,6 +104,7 @@ if (! defined('APP_NAME')) {
|
||||||
define('ACCOUNT_MAP', 'import_map');
|
define('ACCOUNT_MAP', 'import_map');
|
||||||
define('ACCOUNT_EXPORT', 'export');
|
define('ACCOUNT_EXPORT', 'export');
|
||||||
define('ACCOUNT_TAX_RATES', 'tax_rates');
|
define('ACCOUNT_TAX_RATES', 'tax_rates');
|
||||||
|
define('ACCOUNT_TICKETS', 'tickets');
|
||||||
define('ACCOUNT_PRODUCTS', 'products');
|
define('ACCOUNT_PRODUCTS', 'products');
|
||||||
define('ACCOUNT_ADVANCED_SETTINGS', 'advanced_settings');
|
define('ACCOUNT_ADVANCED_SETTINGS', 'advanced_settings');
|
||||||
define('ACCOUNT_INVOICE_SETTINGS', 'invoice_settings');
|
define('ACCOUNT_INVOICE_SETTINGS', 'invoice_settings');
|
||||||
|
|
@ -162,6 +172,16 @@ if (! defined('APP_NAME')) {
|
||||||
define('ACTIVITY_TYPE_DELETE_TASK', 45);
|
define('ACTIVITY_TYPE_DELETE_TASK', 45);
|
||||||
define('ACTIVITY_TYPE_RESTORE_TASK', 46);
|
define('ACTIVITY_TYPE_RESTORE_TASK', 46);
|
||||||
define('ACTIVITY_TYPE_UPDATE_EXPENSE', 47);
|
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('DEFAULT_INVOICE_NUMBER', '0001');
|
||||||
define('RECENTLY_VIEWED_LIMIT', 20);
|
define('RECENTLY_VIEWED_LIMIT', 20);
|
||||||
|
|
@ -202,9 +222,9 @@ if (! defined('APP_NAME')) {
|
||||||
define('IMPORT_PANCAKE', 'Pancake');
|
define('IMPORT_PANCAKE', 'Pancake');
|
||||||
|
|
||||||
define('MAX_NUM_CLIENTS', 100);
|
define('MAX_NUM_CLIENTS', 100);
|
||||||
define('MAX_NUM_CLIENTS_PRO', 20000);
|
define('MAX_NUM_CLIENTS_PRO', 40000);
|
||||||
define('MAX_NUM_CLIENTS_LEGACY', 500);
|
define('MAX_NUM_CLIENTS_LEGACY', 500);
|
||||||
define('MAX_INVOICE_AMOUNT', 1000000000);
|
define('MAX_INVOICE_AMOUNT', 10000000000);
|
||||||
define('LEGACY_CUTOFF', 57800);
|
define('LEGACY_CUTOFF', 57800);
|
||||||
define('ERROR_DELAY', 3);
|
define('ERROR_DELAY', 3);
|
||||||
|
|
||||||
|
|
@ -361,7 +381,7 @@ if (! defined('APP_NAME')) {
|
||||||
define('NINJA_APP_URL', env('NINJA_APP_URL', 'https://app.invoiceninja.com'));
|
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_DOCS_URL', env('NINJA_DOCS_URL', 'https://invoice-ninja.readthedocs.io/en/latest'));
|
||||||
define('NINJA_DATE', '2000-01-01');
|
define('NINJA_DATE', '2000-01-01');
|
||||||
define('NINJA_VERSION', '4.5.5' . env('NINJA_VERSION_SUFFIX'));
|
define('NINJA_VERSION', '4.5.37' . env('NINJA_VERSION_SUFFIX'));
|
||||||
define('NINJA_TERMS_VERSION', '1.0.1');
|
define('NINJA_TERMS_VERSION', '1.0.1');
|
||||||
|
|
||||||
define('SOCIAL_LINK_FACEBOOK', env('SOCIAL_LINK_FACEBOOK', 'https://www.facebook.com/invoiceninja'));
|
define('SOCIAL_LINK_FACEBOOK', env('SOCIAL_LINK_FACEBOOK', 'https://www.facebook.com/invoiceninja'));
|
||||||
|
|
@ -371,9 +391,9 @@ if (! defined('APP_NAME')) {
|
||||||
define('NINJA_FORUM_URL', env('NINJA_FORUM_URL', 'https://www.invoiceninja.com/forums/forum/support/'));
|
define('NINJA_FORUM_URL', env('NINJA_FORUM_URL', 'https://www.invoiceninja.com/forums/forum/support/'));
|
||||||
define('NINJA_CONTACT_URL', env('NINJA_CONTACT_URL', 'https://www.invoiceninja.com/contact/'));
|
define('NINJA_CONTACT_URL', env('NINJA_CONTACT_URL', 'https://www.invoiceninja.com/contact/'));
|
||||||
define('NINJA_FROM_EMAIL', env('NINJA_FROM_EMAIL', 'maildelivery@invoiceninja.com'));
|
define('NINJA_FROM_EMAIL', env('NINJA_FROM_EMAIL', 'maildelivery@invoiceninja.com'));
|
||||||
define('NINJA_IOS_APP_URL', 'https://itunes.apple.com/WebObjects/MZStore.woa/wa/viewSoftware?id=1220337560&mt=8');
|
define('NINJA_IOS_APP_URL', 'https://itunes.apple.com/us/app/invoice-ninja/id1435514417?ls=1&mt=8');
|
||||||
define('NINJA_ANDROID_APP_URL', 'https://play.google.com/store/apps/details?id=com.invoiceninja.invoiceninja');
|
define('NINJA_ANDROID_APP_URL', 'https://play.google.com/store/apps/details?id=com.invoiceninja.flutter');
|
||||||
define('RELEASES_URL', env('RELEASES_URL', 'https://trello.com/b/63BbiVVe/invoice-ninja'));
|
define('RELEASES_URL', env('RELEASES_URL', 'https://github.com/invoiceninja/invoiceninja/releases'));
|
||||||
define('ZAPIER_URL', env('ZAPIER_URL', 'https://zapier.com/zapbook/invoice-ninja'));
|
define('ZAPIER_URL', env('ZAPIER_URL', 'https://zapier.com/zapbook/invoice-ninja'));
|
||||||
define('OUTDATE_BROWSER_URL', env('OUTDATE_BROWSER_URL', 'http://browsehappy.com/'));
|
define('OUTDATE_BROWSER_URL', env('OUTDATE_BROWSER_URL', 'http://browsehappy.com/'));
|
||||||
define('PDFMAKE_DOCS', env('PDFMAKE_DOCS', 'http://pdfmake.org/playground.html'));
|
define('PDFMAKE_DOCS', env('PDFMAKE_DOCS', 'http://pdfmake.org/playground.html'));
|
||||||
|
|
@ -414,12 +434,12 @@ if (! defined('APP_NAME')) {
|
||||||
define('INVOICE_DESIGNS_AFFILIATE_KEY', 'T3RS74');
|
define('INVOICE_DESIGNS_AFFILIATE_KEY', 'T3RS74');
|
||||||
define('SELF_HOST_AFFILIATE_KEY', '8S69AD');
|
define('SELF_HOST_AFFILIATE_KEY', '8S69AD');
|
||||||
|
|
||||||
define('PLAN_PRICE_PRO_MONTHLY', env('PLAN_PRICE_PRO_MONTHLY', 8));
|
define('PLAN_PRICE_PRO_MONTHLY', env('PLAN_PRICE_PRO_MONTHLY', 10));
|
||||||
define('PLAN_PRICE_ENTERPRISE_MONTHLY_2', env('PLAN_PRICE_ENTERPRISE_MONTHLY_2', 12));
|
define('PLAN_PRICE_ENTERPRISE_MONTHLY_2', env('PLAN_PRICE_ENTERPRISE_MONTHLY_2', 14));
|
||||||
define('PLAN_PRICE_ENTERPRISE_MONTHLY_5', env('PLAN_PRICE_ENTERPRISE_MONTHLY_5', 18));
|
define('PLAN_PRICE_ENTERPRISE_MONTHLY_5', env('PLAN_PRICE_ENTERPRISE_MONTHLY_5', 26));
|
||||||
define('PLAN_PRICE_ENTERPRISE_MONTHLY_10', env('PLAN_PRICE_ENTERPRISE_MONTHLY_10', 24));
|
define('PLAN_PRICE_ENTERPRISE_MONTHLY_10', env('PLAN_PRICE_ENTERPRISE_MONTHLY_10', 36));
|
||||||
define('PLAN_PRICE_ENTERPRISE_MONTHLY_20', env('PLAN_PRICE_ENTERPRISE_MONTHLY_20', 36));
|
define('PLAN_PRICE_ENTERPRISE_MONTHLY_20', env('PLAN_PRICE_ENTERPRISE_MONTHLY_20', 44));
|
||||||
define('WHITE_LABEL_PRICE', env('WHITE_LABEL_PRICE', 20));
|
define('WHITE_LABEL_PRICE', env('WHITE_LABEL_PRICE', 30));
|
||||||
define('INVOICE_DESIGNS_PRICE', env('INVOICE_DESIGNS_PRICE', 10));
|
define('INVOICE_DESIGNS_PRICE', env('INVOICE_DESIGNS_PRICE', 10));
|
||||||
|
|
||||||
define('USER_TYPE_SELF_HOST', 'SELF_HOST');
|
define('USER_TYPE_SELF_HOST', 'SELF_HOST');
|
||||||
|
|
@ -493,6 +513,10 @@ if (! defined('APP_NAME')) {
|
||||||
define('TEMPLATE_REMINDER2', 'reminder2');
|
define('TEMPLATE_REMINDER2', 'reminder2');
|
||||||
define('TEMPLATE_REMINDER3', 'reminder3');
|
define('TEMPLATE_REMINDER3', 'reminder3');
|
||||||
define('TEMPLATE_REMINDER4', 'reminder4');
|
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_DASHBOARD', 'dashboard');
|
||||||
define('CUSTOM_MESSAGE_UNPAID_INVOICE', 'unpaid_invoice');
|
define('CUSTOM_MESSAGE_UNPAID_INVOICE', 'unpaid_invoice');
|
||||||
|
|
@ -574,6 +598,7 @@ if (! defined('APP_NAME')) {
|
||||||
define('FEATURE_MORE_INVOICE_DESIGNS', 'more_invoice_designs');
|
define('FEATURE_MORE_INVOICE_DESIGNS', 'more_invoice_designs');
|
||||||
define('FEATURE_QUOTES', 'quotes');
|
define('FEATURE_QUOTES', 'quotes');
|
||||||
define('FEATURE_TASKS', 'tasks');
|
define('FEATURE_TASKS', 'tasks');
|
||||||
|
define('FEATURE_TICKETS', 'tickets');
|
||||||
define('FEATURE_EXPENSES', 'expenses');
|
define('FEATURE_EXPENSES', 'expenses');
|
||||||
define('FEATURE_REPORTS', 'reports');
|
define('FEATURE_REPORTS', 'reports');
|
||||||
define('FEATURE_BUY_NOW_BUTTONS', 'buy_now_buttons');
|
define('FEATURE_BUY_NOW_BUTTONS', 'buy_now_buttons');
|
||||||
|
|
@ -663,6 +688,45 @@ if (! defined('APP_NAME')) {
|
||||||
// Fix for mPDF: https://github.com/kartik-v/yii2-mpdf/issues/9
|
// Fix for mPDF: https://github.com/kartik-v/yii2-mpdf/issues/9
|
||||||
define('_MPDF_TTFONTDATAPATH', storage_path('framework/cache/'));
|
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 = [])
|
function uctrans($text, $data = [])
|
||||||
{
|
{
|
||||||
$locale = Session::get(SESSION_LOCALE);
|
$locale = Session::get(SESSION_LOCALE);
|
||||||
|
|
@ -695,7 +759,7 @@ if (! defined('APP_NAME')) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// include modules in translations
|
// include modules in translations
|
||||||
function mtrans($entityType, $text = false)
|
function mtrans($entityType, $text = false, $replace = [])
|
||||||
{
|
{
|
||||||
if (! $text) {
|
if (! $text) {
|
||||||
$text = $entityType;
|
$text = $entityType;
|
||||||
|
|
@ -704,7 +768,7 @@ if (! defined('APP_NAME')) {
|
||||||
// check if this has been translated in a module language file
|
// check if this has been translated in a module language file
|
||||||
if (! Utils::isNinjaProd() && $module = Module::find($entityType)) {
|
if (! Utils::isNinjaProd() && $module = Module::find($entityType)) {
|
||||||
$key = "{$module->getLowerName()}::texts.{$text}";
|
$key = "{$module->getLowerName()}::texts.{$text}";
|
||||||
$value = trans($key);
|
$value = trans($key, $replace);
|
||||||
if ($key != $value) {
|
if ($key != $value) {
|
||||||
return $value;
|
return $value;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -30,4 +30,26 @@ class Domain
|
||||||
{
|
{
|
||||||
return 'maildelivery@' . static::getDomainFromId($id);
|
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');
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
29
app/Events/TicketUserViewed.php
Normal file
29
app/Events/TicketUserViewed.php
Normal file
|
|
@ -0,0 +1,29 @@
|
||||||
|
<?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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -5,6 +5,7 @@ namespace App\Http\Controllers;
|
||||||
use App\Events\UserSignedUp;
|
use App\Events\UserSignedUp;
|
||||||
use App\Http\Requests\RegisterRequest;
|
use App\Http\Requests\RegisterRequest;
|
||||||
use App\Http\Requests\UpdateAccountRequest;
|
use App\Http\Requests\UpdateAccountRequest;
|
||||||
|
use App\Models\Company;
|
||||||
use App\Models\Account;
|
use App\Models\Account;
|
||||||
use App\Models\User;
|
use App\Models\User;
|
||||||
use App\Ninja\OAuth\OAuth;
|
use App\Ninja\OAuth\OAuth;
|
||||||
|
|
@ -14,6 +15,7 @@ use App\Ninja\Transformers\UserAccountTransformer;
|
||||||
use App\Services\AuthService;
|
use App\Services\AuthService;
|
||||||
use Auth;
|
use Auth;
|
||||||
use Cache;
|
use Cache;
|
||||||
|
use Carbon;
|
||||||
use Exception;
|
use Exception;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
use Illuminate\Support\Facades\Log;
|
use Illuminate\Support\Facades\Log;
|
||||||
|
|
@ -105,9 +107,9 @@ class AccountApiController extends BaseAPIController
|
||||||
$user = Auth::user();
|
$user = Auth::user();
|
||||||
$account = $user->account;
|
$account = $user->account;
|
||||||
|
|
||||||
if ($createToken) {
|
if ($createToken)
|
||||||
$this->accountRepo->createTokens($user, $request->token_name);
|
$this->accountRepo->createTokens($user, $request->token_name);
|
||||||
}
|
|
||||||
|
|
||||||
$users = $this->accountRepo->findUsers($user, 'account.account_tokens');
|
$users = $this->accountRepo->findUsers($user, 'account.account_tokens');
|
||||||
$transformer = new UserAccountTransformer($account, $request->serializer, $request->token_name);
|
$transformer = new UserAccountTransformer($account, $request->serializer, $request->token_name);
|
||||||
|
|
@ -261,6 +263,17 @@ class AccountApiController extends BaseAPIController
|
||||||
$oAuth = new OAuth();
|
$oAuth = new OAuth();
|
||||||
$user = $oAuth->getProvider($provider)->getTokenResponse($token);
|
$user = $oAuth->getProvider($provider)->getTokenResponse($token);
|
||||||
|
|
||||||
|
/*
|
||||||
|
if ($user->google_2fa_secret && strpos($request->token_name, 'invoice-ninja-') !== false) {
|
||||||
|
$secret = \Crypt::decrypt($user->google_2fa_secret);
|
||||||
|
if (! $request->one_time_password) {
|
||||||
|
return $this->errorResponse(['message' => 'OTP_REQUIRED'], 401);
|
||||||
|
} elseif (! \Google2FA::verifyKey($secret, $request->one_time_password)) {
|
||||||
|
return $this->errorResponse(['message' => 'Invalid one time password'], 401);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
if ($user) {
|
if ($user) {
|
||||||
Auth::login($user);
|
Auth::login($user);
|
||||||
return $this->processLogin($request);
|
return $this->processLogin($request);
|
||||||
|
|
@ -276,4 +289,49 @@ class AccountApiController extends BaseAPIController
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function upgrade(Request $request)
|
||||||
|
{
|
||||||
|
$user = Auth::user();
|
||||||
|
$account = $user->account;
|
||||||
|
$company = $account->company;
|
||||||
|
$orderId = $request->order_id;
|
||||||
|
$timestamp = $request->timestamp;
|
||||||
|
$productId = $request->product_id;
|
||||||
|
|
||||||
|
if ($company->app_store_order_id) {
|
||||||
|
return '{"message":"error"}';
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($productId == 'v1_pro_yearly') {
|
||||||
|
$company->plan = PLAN_PRO;
|
||||||
|
$company->num_users = 1;
|
||||||
|
$company->plan_price = PLAN_PRICE_PRO_MONTHLY * 10;
|
||||||
|
} else if ($productId == 'v1_enterprise_2_yearly') {
|
||||||
|
$company->plan = PLAN_ENTERPRISE;
|
||||||
|
$company->num_users = 2;
|
||||||
|
$company->plan_price = PLAN_PRICE_ENTERPRISE_MONTHLY_2 * 10;
|
||||||
|
} else if ($productId == 'v1_enterprise_5_yearly') {
|
||||||
|
$company->plan = PLAN_ENTERPRISE;
|
||||||
|
$company->num_users = 5;
|
||||||
|
$company->plan_price = PLAN_PRICE_ENTERPRISE_MONTHLY_5 * 10;
|
||||||
|
} else if ($productId == 'v1_enterprise_10_yearly') {
|
||||||
|
$company->plan = PLAN_ENTERPRISE;
|
||||||
|
$company->num_users = 10;
|
||||||
|
$company->plan_price = PLAN_PRICE_ENTERPRISE_MONTHLY_10 * 10;
|
||||||
|
} else if ($productId == 'v1_enterprise_20_yearly') {
|
||||||
|
$company->plan = PLAN_ENTERPRISE;
|
||||||
|
$company->num_users = 20;
|
||||||
|
$company->plan_price = PLAN_PRICE_ENTERPRISE_MONTHLY_20 * 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
$company->app_store_order_id = $orderId;
|
||||||
|
$company->plan_term = PLAN_TERM_YEARLY;
|
||||||
|
$company->plan_started = $company->plan_started ?: date('Y-m-d');
|
||||||
|
$company->plan_paid = date('Y-m-d');
|
||||||
|
$company->plan_expires = Carbon::now()->addYear()->format('Y-m-d');
|
||||||
|
$company->trial_plan = null;
|
||||||
|
$company->save();
|
||||||
|
|
||||||
|
return '{"message":"success"}';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,9 +8,11 @@ use App\Events\UserSettingsChanged;
|
||||||
use App\Events\UserSignedUp;
|
use App\Events\UserSignedUp;
|
||||||
use App\Http\Requests\SaveClientPortalSettings;
|
use App\Http\Requests\SaveClientPortalSettings;
|
||||||
use App\Http\Requests\SaveEmailSettings;
|
use App\Http\Requests\SaveEmailSettings;
|
||||||
|
use App\Http\Requests\SaveTicketSettings;
|
||||||
use App\Http\Requests\UpdateAccountRequest;
|
use App\Http\Requests\UpdateAccountRequest;
|
||||||
use App\Models\Account;
|
use App\Models\Account;
|
||||||
use App\Models\AccountGateway;
|
use App\Models\AccountGateway;
|
||||||
|
use App\Models\AccountTicketSettings;
|
||||||
use App\Models\Affiliate;
|
use App\Models\Affiliate;
|
||||||
use App\Models\Document;
|
use App\Models\Document;
|
||||||
use App\Models\Gateway;
|
use App\Models\Gateway;
|
||||||
|
|
@ -21,6 +23,7 @@ use App\Models\License;
|
||||||
use App\Models\PaymentTerm;
|
use App\Models\PaymentTerm;
|
||||||
use App\Models\Product;
|
use App\Models\Product;
|
||||||
use App\Models\TaxRate;
|
use App\Models\TaxRate;
|
||||||
|
use App\Models\TicketTemplate;
|
||||||
use App\Models\User;
|
use App\Models\User;
|
||||||
use App\Models\AccountEmailSettings;
|
use App\Models\AccountEmailSettings;
|
||||||
use App\Ninja\Mailers\ContactMailer;
|
use App\Ninja\Mailers\ContactMailer;
|
||||||
|
|
@ -30,6 +33,7 @@ use App\Ninja\Repositories\ReferralRepository;
|
||||||
use App\Services\AuthService;
|
use App\Services\AuthService;
|
||||||
use App\Services\PaymentService;
|
use App\Services\PaymentService;
|
||||||
use App\Services\TemplateService;
|
use App\Services\TemplateService;
|
||||||
|
use Monolog\Handler\Curl\Util;
|
||||||
use Nwidart\Modules\Facades\Module;
|
use Nwidart\Modules\Facades\Module;
|
||||||
use Auth;
|
use Auth;
|
||||||
use Cache;
|
use Cache;
|
||||||
|
|
@ -289,7 +293,9 @@ class AccountController extends BaseController
|
||||||
} elseif ($section == ACCOUNT_INVOICE_SETTINGS) {
|
} elseif ($section == ACCOUNT_INVOICE_SETTINGS) {
|
||||||
return self::showInvoiceSettings();
|
return self::showInvoiceSettings();
|
||||||
} elseif ($section == ACCOUNT_IMPORT_EXPORT) {
|
} 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) {
|
} elseif ($section == ACCOUNT_MANAGEMENT) {
|
||||||
return self::showAccountManagement();
|
return self::showAccountManagement();
|
||||||
} elseif ($section == ACCOUNT_INVOICE_DESIGN || $section == ACCOUNT_CUSTOMIZE_DESIGN) {
|
} elseif ($section == ACCOUNT_INVOICE_DESIGN || $section == ACCOUNT_CUSTOMIZE_DESIGN) {
|
||||||
|
|
@ -302,6 +308,8 @@ class AccountController extends BaseController
|
||||||
return self::showProducts();
|
return self::showProducts();
|
||||||
} elseif ($section === ACCOUNT_TAX_RATES) {
|
} elseif ($section === ACCOUNT_TAX_RATES) {
|
||||||
return self::showTaxRates();
|
return self::showTaxRates();
|
||||||
|
} elseif ($section === ACCOUNT_TICKETS) {
|
||||||
|
return self::showTickets();
|
||||||
} elseif ($section === ACCOUNT_PAYMENT_TERMS) {
|
} elseif ($section === ACCOUNT_PAYMENT_TERMS) {
|
||||||
return self::showPaymentTerms();
|
return self::showPaymentTerms();
|
||||||
} elseif ($section === ACCOUNT_SYSTEM_SETTINGS) {
|
} elseif ($section === ACCOUNT_SYSTEM_SETTINGS) {
|
||||||
|
|
@ -510,11 +518,29 @@ class AccountController extends BaseController
|
||||||
$data = [
|
$data = [
|
||||||
'account' => Auth::user()->account,
|
'account' => Auth::user()->account,
|
||||||
'title' => trans('texts.product_library'),
|
'title' => trans('texts.product_library'),
|
||||||
|
|
||||||
];
|
];
|
||||||
|
|
||||||
return View::make('accounts.products', $data);
|
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
|
* @return \Illuminate\Contracts\View\View
|
||||||
*/
|
*/
|
||||||
|
|
@ -633,6 +659,7 @@ class AccountController extends BaseController
|
||||||
$data['invoiceFonts'] = Cache::get('fonts');
|
$data['invoiceFonts'] = Cache::get('fonts');
|
||||||
$data['section'] = $section;
|
$data['section'] = $section;
|
||||||
$data['pageSizes'] = array_combine(InvoiceDesign::$pageSizes, InvoiceDesign::$pageSizes);
|
$data['pageSizes'] = array_combine(InvoiceDesign::$pageSizes, InvoiceDesign::$pageSizes);
|
||||||
|
$data['showModuleSettings'] = Utils::hasModuleSettings();
|
||||||
|
|
||||||
$design = false;
|
$design = false;
|
||||||
foreach ($data['invoiceDesigns'] as $item) {
|
foreach ($data['invoiceDesigns'] as $item) {
|
||||||
|
|
@ -694,8 +721,14 @@ class AccountController extends BaseController
|
||||||
'account' => $account,
|
'account' => $account,
|
||||||
'products' => Product::scope()->orderBy('product_key')->get(),
|
'products' => Product::scope()->orderBy('product_key')->get(),
|
||||||
'gateway_types' => $options,
|
'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);
|
return View::make('accounts.client_portal', $data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -719,6 +752,7 @@ class AccountController extends BaseController
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
$data['title'] = trans('texts.email_templates');
|
$data['title'] = trans('texts.email_templates');
|
||||||
|
$data['showModuleSettings'] = Utils::hasModuleSettings();
|
||||||
|
|
||||||
return View::make('accounts.templates_and_reminders', $data);
|
return View::make('accounts.templates_and_reminders', $data);
|
||||||
}
|
}
|
||||||
|
|
@ -786,6 +820,7 @@ class AccountController extends BaseController
|
||||||
$user->save();
|
$user->save();
|
||||||
|
|
||||||
$account->live_preview = Input::get('live_preview') ? true : false;
|
$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
|
// Automatically disable live preview when using a large font
|
||||||
$fonts = Cache::get('fonts')->filter(function ($font) use ($account) {
|
$fonts = Cache::get('fonts')->filter(function ($font) use ($account) {
|
||||||
|
|
@ -856,6 +891,7 @@ class AccountController extends BaseController
|
||||||
|
|
||||||
$account->fill($request->all());
|
$account->fill($request->all());
|
||||||
$account->client_view_css = $request->client_view_css;
|
$account->client_view_css = $request->client_view_css;
|
||||||
|
$account->client_view_js = $request->client_view_js;
|
||||||
$account->subdomain = $request->subdomain;
|
$account->subdomain = $request->subdomain;
|
||||||
$account->iframe_url = $request->iframe_url;
|
$account->iframe_url = $request->iframe_url;
|
||||||
$account->is_custom_domain = $request->is_custom_domain;
|
$account->is_custom_domain = $request->is_custom_domain;
|
||||||
|
|
@ -905,21 +941,29 @@ class AccountController extends BaseController
|
||||||
$account->account_email_settings->$bodyField = ($body == $account->getDefaultEmailTemplate($type) ? null : $body);
|
$account->account_email_settings->$bodyField = ($body == $account->getDefaultEmailTemplate($type) ? null : $body);
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach ([TEMPLATE_REMINDER1, TEMPLATE_REMINDER2, TEMPLATE_REMINDER3] as $type) {
|
foreach ([TEMPLATE_REMINDER1, TEMPLATE_REMINDER2, TEMPLATE_REMINDER3, TEMPLATE_QUOTE_REMINDER1, TEMPLATE_QUOTE_REMINDER2, TEMPLATE_QUOTE_REMINDER3] as $type) {
|
||||||
$enableField = "enable_{$type}";
|
$enableField = "enable_{$type}";
|
||||||
$account->$enableField = Input::get($enableField) ? true : false;
|
$account->account_email_settings->$enableField = Input::get($enableField) ? true : false;
|
||||||
$account->{"num_days_{$type}"} = Input::get("num_days_{$type}");
|
$account->account_email_settings->{"num_days_{$type}"} = Input::get("num_days_{$type}");
|
||||||
$account->{"field_{$type}"} = Input::get("field_{$type}");
|
$account->account_email_settings->{"field_{$type}"} = Input::get("field_{$type}");
|
||||||
$account->{"direction_{$type}"} = Input::get("field_{$type}") == REMINDER_FIELD_INVOICE_DATE ? REMINDER_DIRECTION_AFTER : Input::get("direction_{$type}");
|
$account->account_email_settings->{"direction_{$type}"} = Input::get("field_{$type}") == REMINDER_FIELD_INVOICE_DATE ? REMINDER_DIRECTION_AFTER : Input::get("direction_{$type}");
|
||||||
|
|
||||||
$number = preg_replace('/[^0-9]/', '', $type);
|
$number = preg_replace('/[^0-9]/', '', $type);
|
||||||
$account->account_email_settings->{"late_fee{$number}_amount"} = Input::get("late_fee{$number}_amount");
|
if (strpos($type, 'quote') !== false) {
|
||||||
$account->account_email_settings->{"late_fee{$number}_percent"} = Input::get("late_fee{$number}_percent");
|
$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->enable_reminder4 = Input::get('enable_reminder4') ? true : false;
|
$account->account_email_settings->enable_reminder4 = Input::get('enable_reminder4') ? true : false;
|
||||||
$account->account_email_settings->frequency_id_reminder4 = Input::get('frequency_id_reminder4');
|
$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->save();
|
||||||
$account->account_email_settings->save();
|
$account->account_email_settings->save();
|
||||||
|
|
||||||
|
|
@ -929,6 +973,29 @@ class AccountController extends BaseController
|
||||||
return Redirect::to('settings/'.ACCOUNT_TEMPLATES_AND_REMINDERS);
|
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
|
* @return \Illuminate\Http\RedirectResponse
|
||||||
*/
|
*/
|
||||||
|
|
@ -950,6 +1017,7 @@ class AccountController extends BaseController
|
||||||
{
|
{
|
||||||
$account = Auth::user()->account;
|
$account = Auth::user()->account;
|
||||||
|
|
||||||
|
$account->show_product_notes = Input::get('show_product_notes') ? true : false;
|
||||||
$account->fill_products = Input::get('fill_products') ? true : false;
|
$account->fill_products = Input::get('fill_products') ? true : false;
|
||||||
$account->update_products = Input::get('update_products') ? true : false;
|
$account->update_products = Input::get('update_products') ? true : false;
|
||||||
$account->convert_products = Input::get('convert_products') ? true : false;
|
$account->convert_products = Input::get('convert_products') ? true : false;
|
||||||
|
|
@ -998,6 +1066,8 @@ class AccountController extends BaseController
|
||||||
$account->quote_terms = Input::get('quote_terms');
|
$account->quote_terms = Input::get('quote_terms');
|
||||||
$account->auto_convert_quote = Input::get('auto_convert_quote');
|
$account->auto_convert_quote = Input::get('auto_convert_quote');
|
||||||
$account->auto_archive_quote = Input::get('auto_archive_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_archive_invoice = Input::get('auto_archive_invoice');
|
||||||
$account->auto_email_invoice = Input::get('auto_email_invoice');
|
$account->auto_email_invoice = Input::get('auto_email_invoice');
|
||||||
$account->recurring_invoice_number_prefix = Input::get('recurring_invoice_number_prefix');
|
$account->recurring_invoice_number_prefix = Input::get('recurring_invoice_number_prefix');
|
||||||
|
|
@ -1010,6 +1080,7 @@ class AccountController extends BaseController
|
||||||
$account->credit_number_pattern = trim(Input::get('credit_number_pattern'));
|
$account->credit_number_pattern = trim(Input::get('credit_number_pattern'));
|
||||||
$account->reset_counter_frequency_id = Input::get('reset_counter_frequency_id');
|
$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->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')) {
|
if (Input::has('recurring_hour')) {
|
||||||
$account->recurring_hour = Input::get('recurring_hour');
|
$account->recurring_hour = Input::get('recurring_hour');
|
||||||
|
|
@ -1201,6 +1272,7 @@ class AccountController extends BaseController
|
||||||
*/
|
*/
|
||||||
public function saveUserDetails()
|
public function saveUserDetails()
|
||||||
{
|
{
|
||||||
|
|
||||||
/** @var \App\Models\User $user */
|
/** @var \App\Models\User $user */
|
||||||
$user = Auth::user();
|
$user = Auth::user();
|
||||||
$email = trim(strtolower(Input::get('email')));
|
$email = trim(strtolower(Input::get('email')));
|
||||||
|
|
@ -1230,6 +1302,7 @@ class AccountController extends BaseController
|
||||||
$user->email = $email;
|
$user->email = $email;
|
||||||
$user->phone = trim(Input::get('phone'));
|
$user->phone = trim(Input::get('phone'));
|
||||||
$user->dark_mode = Input::get('dark_mode');
|
$user->dark_mode = Input::get('dark_mode');
|
||||||
|
$user->signature = Input::get('signature');
|
||||||
|
|
||||||
if (! Auth::user()->is_admin) {
|
if (! Auth::user()->is_admin) {
|
||||||
$user->notify_sent = Input::get('notify_sent');
|
$user->notify_sent = Input::get('notify_sent');
|
||||||
|
|
@ -1249,6 +1322,8 @@ class AccountController extends BaseController
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$this->saveUserAvatar(Input::file('avatar'), $user);
|
||||||
|
|
||||||
$user->save();
|
$user->save();
|
||||||
|
|
||||||
event(new UserSettingsChanged());
|
event(new UserSettingsChanged());
|
||||||
|
|
@ -1258,6 +1333,89 @@ 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
|
* @return \Illuminate\Http\RedirectResponse
|
||||||
*/
|
*/
|
||||||
|
|
@ -1326,6 +1484,28 @@ class AccountController extends BaseController
|
||||||
return Redirect::to('settings/'.ACCOUNT_COMPANY_DETAILS);
|
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
|
* @return string
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
|
|
@ -44,6 +44,10 @@ class AppController extends BaseController
|
||||||
return Redirect::to('/');
|
return Redirect::to('/');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (file_exists(base_path() . '/.env')) {
|
||||||
|
exit('Error: app is already configured, backup then delete the .env file to re-run the setup');
|
||||||
|
}
|
||||||
|
|
||||||
return View::make('setup');
|
return View::make('setup');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -131,7 +135,6 @@ class AppController extends BaseController
|
||||||
|
|
||||||
Cache::flush();
|
Cache::flush();
|
||||||
Artisan::call('db:seed', ['--force' => true, '--class' => 'UpdateSeeder']);
|
Artisan::call('db:seed', ['--force' => true, '--class' => 'UpdateSeeder']);
|
||||||
Artisan::call('optimize', ['--force' => true]);
|
|
||||||
|
|
||||||
if (! Account::count()) {
|
if (! Account::count()) {
|
||||||
$firstName = trim(Input::get('first_name'));
|
$firstName = trim(Input::get('first_name'));
|
||||||
|
|
@ -269,7 +272,6 @@ class AppController extends BaseController
|
||||||
if (Industry::count() == 0) {
|
if (Industry::count() == 0) {
|
||||||
Artisan::call('db:seed', ['--force' => true]);
|
Artisan::call('db:seed', ['--force' => true]);
|
||||||
}
|
}
|
||||||
Artisan::call('optimize', ['--force' => true]);
|
|
||||||
} catch (Exception $e) {
|
} catch (Exception $e) {
|
||||||
Utils::logError($e);
|
Utils::logError($e);
|
||||||
|
|
||||||
|
|
@ -307,7 +309,6 @@ class AppController extends BaseController
|
||||||
Artisan::call('route:clear');
|
Artisan::call('route:clear');
|
||||||
Artisan::call('view:clear');
|
Artisan::call('view:clear');
|
||||||
Artisan::call('config:clear');
|
Artisan::call('config:clear');
|
||||||
Artisan::call('optimize', ['--force' => true]);
|
|
||||||
Auth::logout();
|
Auth::logout();
|
||||||
Cache::flush();
|
Cache::flush();
|
||||||
Session::flush();
|
Session::flush();
|
||||||
|
|
|
||||||
|
|
@ -42,7 +42,7 @@ class AuthController extends Controller
|
||||||
*/
|
*/
|
||||||
public function oauthLogin($provider, Request $request)
|
public function oauthLogin($provider, Request $request)
|
||||||
{
|
{
|
||||||
return $this->authService->execute($provider, $request->has('code'));
|
return $this->authService->execute($provider, $request->filled('code'));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,7 @@ class CalendarController extends BaseController
|
||||||
public function loadEvents()
|
public function loadEvents()
|
||||||
{
|
{
|
||||||
if (auth()->user()->account->hasFeature(FEATURE_REPORTS)) {
|
if (auth()->user()->account->hasFeature(FEATURE_REPORTS)) {
|
||||||
$events = dispatch(new GenerateCalendarEvents());
|
$events = dispatch_now(new GenerateCalendarEvents());
|
||||||
} else {
|
} else {
|
||||||
$events = [];
|
$events = [];
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -43,7 +43,7 @@ class ClientApiController extends BaseAPIController
|
||||||
public function index()
|
public function index()
|
||||||
{
|
{
|
||||||
$clients = Client::scope()
|
$clients = Client::scope()
|
||||||
->orderBy('created_at', 'desc')
|
->orderBy('updated_at', 'desc')
|
||||||
->withTrashed();
|
->withTrashed();
|
||||||
|
|
||||||
if ($email = Input::get('email')) {
|
if ($email = Input::get('email')) {
|
||||||
|
|
|
||||||
|
|
@ -175,11 +175,9 @@ class LoginController extends Controller
|
||||||
*/
|
*/
|
||||||
public function getLogoutWrapper(Request $request)
|
public function getLogoutWrapper(Request $request)
|
||||||
{
|
{
|
||||||
$contactKey = session('contact_key');
|
|
||||||
|
|
||||||
self::logout($request);
|
self::logout($request);
|
||||||
|
|
||||||
return redirect('/client/dashboard/' . $contactKey);
|
return redirect('/client/login?account_key=' . $request->account_key);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -106,6 +106,9 @@ class ClientController extends BaseController
|
||||||
if ($user->can('create', ENTITY_RECURRING_INVOICE)) {
|
if ($user->can('create', ENTITY_RECURRING_INVOICE)) {
|
||||||
$actionLinks[] = ['label' => trans('texts.new_recurring_invoice'), 'url' => URL::to('/recurring_invoices/create/'.$client->public_id)];
|
$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)) {
|
if (! empty($actionLinks)) {
|
||||||
$actionLinks[] = \DropdownButton::DIVIDER;
|
$actionLinks[] = \DropdownButton::DIVIDER;
|
||||||
|
|
@ -133,6 +136,7 @@ class ClientController extends BaseController
|
||||||
'credit' => $client->getTotalCredit(),
|
'credit' => $client->getTotalCredit(),
|
||||||
'title' => trans('texts.view_client'),
|
'title' => trans('texts.view_client'),
|
||||||
'hasRecurringInvoices' => $account->isModuleEnabled(ENTITY_RECURRING_INVOICE) && Invoice::scope()->recurring()->withArchived()->whereClientId($client->id)->count() > 0,
|
'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,
|
'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,
|
'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,
|
'hasExpenses' => $account->isModuleEnabled(ENTITY_EXPENSE) && Expense::scope()->withArchived()->whereClientId($client->id)->count() > 0,
|
||||||
|
|
@ -260,7 +264,7 @@ class ClientController extends BaseController
|
||||||
}
|
}
|
||||||
|
|
||||||
if (request()->json) {
|
if (request()->json) {
|
||||||
return dispatch(new GenerateStatementData($client, request()->all()));
|
return dispatch_now(new GenerateStatementData($client, request()->all()));
|
||||||
}
|
}
|
||||||
|
|
||||||
$data = [
|
$data = [
|
||||||
|
|
@ -276,14 +280,14 @@ class ClientController extends BaseController
|
||||||
|
|
||||||
public function getEmailHistory()
|
public function getEmailHistory()
|
||||||
{
|
{
|
||||||
$history = dispatch(new LoadPostmarkHistory(request()->email));
|
$history = dispatch_now(new LoadPostmarkHistory(request()->email));
|
||||||
|
|
||||||
return response()->json($history);
|
return response()->json($history);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function reactivateEmail()
|
public function reactivateEmail()
|
||||||
{
|
{
|
||||||
$result = dispatch(new ReactivatePostmarkEmail(request()->bounce_id));
|
$result = dispatch_now(new ReactivatePostmarkEmail(request()->bounce_id));
|
||||||
|
|
||||||
return response()->json($result);
|
return response()->json($result);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -136,21 +136,24 @@ class ClientPortalController extends BaseController
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($wepayGateway = $account->getGatewayConfig(GATEWAY_WEPAY)) {
|
if (! Input::has('phantomjs')) {
|
||||||
$data['enableWePayACH'] = $wepayGateway->getAchEnabled();
|
if ($wepayGateway = $account->getGatewayConfig(GATEWAY_WEPAY)) {
|
||||||
}
|
$data['enableWePayACH'] = $wepayGateway->getAchEnabled();
|
||||||
if ($stripeGateway = $account->getGatewayConfig(GATEWAY_STRIPE)) {
|
}
|
||||||
//$data['enableStripeSources'] = $stripeGateway->getAlipayEnabled();
|
if ($stripeGateway = $account->getGatewayConfig(GATEWAY_STRIPE)) {
|
||||||
$data['enableStripeSources'] = true;
|
//$data['enableStripeSources'] = $stripeGateway->getAlipayEnabled();
|
||||||
|
$data['enableStripeSources'] = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$showApprove = $invoice->quote_invoice_id ? false : true;
|
$showApprove = ($invoice->isQuote() && $account->require_approve_quote) ? true: false;
|
||||||
if ($invoice->invoice_status_id >= INVOICE_STATUS_APPROVED) {
|
if ($invoice->invoice_status_id >= INVOICE_STATUS_APPROVED) {
|
||||||
$showApprove = false;
|
$showApprove = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
$data += [
|
$data += [
|
||||||
'account' => $account,
|
'account' => $account,
|
||||||
|
'approveRequired' => $account->require_approve_quote,
|
||||||
'showApprove' => $showApprove,
|
'showApprove' => $showApprove,
|
||||||
'showBreadcrumbs' => false,
|
'showBreadcrumbs' => false,
|
||||||
'invoice' => $invoice->hidePrivateFields(),
|
'invoice' => $invoice->hidePrivateFields(),
|
||||||
|
|
@ -321,7 +324,7 @@ class ClientPortalController extends BaseController
|
||||||
->make();
|
->make();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function recurringInvoiceIndex()
|
public function recurringInvoiceIndex($quotes = false)
|
||||||
{
|
{
|
||||||
if (! $contact = $this->getContact()) {
|
if (! $contact = $this->getContact()) {
|
||||||
return $this->returnError();
|
return $this->returnError();
|
||||||
|
|
@ -341,12 +344,19 @@ class ClientPortalController extends BaseController
|
||||||
$columns[] = 'auto_bill';
|
$columns[] = 'auto_bill';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$title = trans('texts.recurring_invoices');
|
||||||
|
$entityType = ENTITY_RECURRING_INVOICE;
|
||||||
|
if ($quotes) {
|
||||||
|
$title = trans('texts.recurring_quotes');
|
||||||
|
$entityType = ENTITY_RECURRING_QUOTE;
|
||||||
|
}
|
||||||
|
|
||||||
$data = [
|
$data = [
|
||||||
'color' => $color,
|
'color' => $color,
|
||||||
'account' => $account,
|
'account' => $account,
|
||||||
'client' => $client,
|
'client' => $client,
|
||||||
'title' => trans('texts.recurring_invoices'),
|
'title' => $title,
|
||||||
'entityType' => ENTITY_RECURRING_INVOICE,
|
'entityType' => $entityType,
|
||||||
'columns' => Utils::trans($columns),
|
'columns' => Utils::trans($columns),
|
||||||
'sortColumn' => 1,
|
'sortColumn' => 1,
|
||||||
];
|
];
|
||||||
|
|
@ -354,6 +364,11 @@ class ClientPortalController extends BaseController
|
||||||
return response()->view('public_list', $data);
|
return response()->view('public_list', $data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function recurringQuoteIndex()
|
||||||
|
{
|
||||||
|
return self::recurringInvoiceIndex(true);
|
||||||
|
}
|
||||||
|
|
||||||
public function invoiceIndex()
|
public function invoiceIndex()
|
||||||
{
|
{
|
||||||
if (! $contact = $this->getContact()) {
|
if (! $contact = $this->getContact()) {
|
||||||
|
|
@ -399,6 +414,15 @@ class ClientPortalController extends BaseController
|
||||||
return $this->invoiceRepo->getClientRecurringDatatable($contact->id);
|
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()
|
public function paymentIndex()
|
||||||
{
|
{
|
||||||
if (! $contact = $this->getContact()) {
|
if (! $contact = $this->getContact()) {
|
||||||
|
|
@ -500,6 +524,7 @@ class ClientPortalController extends BaseController
|
||||||
$data = [
|
$data = [
|
||||||
'color' => $color,
|
'color' => $color,
|
||||||
'account' => $account,
|
'account' => $account,
|
||||||
|
'client' => $contact->client,
|
||||||
'title' => trans('texts.quotes'),
|
'title' => trans('texts.quotes'),
|
||||||
'entityType' => ENTITY_QUOTE,
|
'entityType' => ENTITY_QUOTE,
|
||||||
'columns' => Utils::trans(['quote_number', 'quote_date', 'quote_total', 'due_date', 'status']),
|
'columns' => Utils::trans(['quote_number', 'quote_date', 'quote_total', 'due_date', 'status']),
|
||||||
|
|
@ -627,7 +652,7 @@ class ClientPortalController extends BaseController
|
||||||
return $this->documentRepo->getClientDatatable($contact->id, ENTITY_DOCUMENT, Input::get('sSearch'));
|
return $this->documentRepo->getClientDatatable($contact->id, ENTITY_DOCUMENT, Input::get('sSearch'));
|
||||||
}
|
}
|
||||||
|
|
||||||
private function returnError($error = false)
|
public function returnError($error = false)
|
||||||
{
|
{
|
||||||
if (request()->phantomjs) {
|
if (request()->phantomjs) {
|
||||||
abort(404);
|
abort(404);
|
||||||
|
|
@ -640,7 +665,7 @@ class ClientPortalController extends BaseController
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
private function getContact()
|
public function getContact()
|
||||||
{
|
{
|
||||||
$contactKey = session('contact_key');
|
$contactKey = session('contact_key');
|
||||||
|
|
||||||
|
|
@ -1033,7 +1058,7 @@ class ClientPortalController extends BaseController
|
||||||
}
|
}
|
||||||
|
|
||||||
if (request()->json) {
|
if (request()->json) {
|
||||||
return dispatch(new GenerateStatementData($client, request()->all(), $contact));
|
return dispatch_now(new GenerateStatementData($client, request()->all(), $contact));
|
||||||
}
|
}
|
||||||
|
|
||||||
$data = [
|
$data = [
|
||||||
|
|
@ -1047,4 +1072,6 @@ class ClientPortalController extends BaseController
|
||||||
return view('clients.statement', $data);
|
return view('clients.statement', $data);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -54,7 +54,7 @@ class ClientPortalProposalController extends BaseController
|
||||||
|
|
||||||
$proposal = $invitation->proposal;
|
$proposal = $invitation->proposal;
|
||||||
|
|
||||||
$pdf = dispatch(new ConvertProposalToPdf($proposal));
|
$pdf = dispatch_now(new ConvertProposalToPdf($proposal));
|
||||||
|
|
||||||
$this->downloadResponse($proposal->getFilename(), $pdf);
|
$this->downloadResponse($proposal->getFilename(), $pdf);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
202
app/Http/Controllers/ClientPortalTicketController.php
Normal file
202
app/Http/Controllers/ClientPortalTicketController.php
Normal file
|
|
@ -0,0 +1,202 @@
|
||||||
|
<?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
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -46,7 +46,7 @@ class CreditApiController extends BaseAPIController
|
||||||
$credits = Credit::scope()
|
$credits = Credit::scope()
|
||||||
->withTrashed()
|
->withTrashed()
|
||||||
->with(['client'])
|
->with(['client'])
|
||||||
->orderBy('created_at', 'desc');
|
->orderBy('updated_at', 'desc');
|
||||||
|
|
||||||
return $this->listResponse($credits);
|
return $this->listResponse($credits);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,9 +3,11 @@
|
||||||
namespace App\Http\Controllers;
|
namespace App\Http\Controllers;
|
||||||
|
|
||||||
use App\Models\Client;
|
use App\Models\Client;
|
||||||
|
use App\Models\Currency;
|
||||||
use App\Models\Expense;
|
use App\Models\Expense;
|
||||||
use App\Ninja\Repositories\DashboardRepository;
|
use App\Ninja\Repositories\DashboardRepository;
|
||||||
use Auth;
|
use Auth;
|
||||||
|
use App\Libraries\MoneyUtils;
|
||||||
use Utils;
|
use Utils;
|
||||||
use View;
|
use View;
|
||||||
|
|
||||||
|
|
@ -34,7 +36,7 @@ class DashboardController extends BaseController
|
||||||
$metrics = $dashboardRepo->totals($accountId, $userId, $viewAll);
|
$metrics = $dashboardRepo->totals($accountId, $userId, $viewAll);
|
||||||
$paidToDate = $dashboardRepo->paidToDate($account, $userId, $viewAll);
|
$paidToDate = $dashboardRepo->paidToDate($account, $userId, $viewAll);
|
||||||
$averageInvoice = $dashboardRepo->averages($account, $userId, $viewAll);
|
$averageInvoice = $dashboardRepo->averages($account, $userId, $viewAll);
|
||||||
$balances = $dashboardRepo->balances($accountId, $userId, $viewAll);
|
$balances = $dashboardRepo->balances($account, $userId, $viewAll);
|
||||||
$activities = $dashboardRepo->activities($accountId, $userId, $viewAll);
|
$activities = $dashboardRepo->activities($accountId, $userId, $viewAll);
|
||||||
$pastDue = $dashboardRepo->pastDue($accountId, $userId, $viewAll);
|
$pastDue = $dashboardRepo->pastDue($accountId, $userId, $viewAll);
|
||||||
$upcoming = $dashboardRepo->upcoming($accountId, $userId, $viewAll);
|
$upcoming = $dashboardRepo->upcoming($accountId, $userId, $viewAll);
|
||||||
|
|
@ -42,6 +44,60 @@ class DashboardController extends BaseController
|
||||||
$expenses = $dashboardRepo->expenses($account, $userId, $viewAll);
|
$expenses = $dashboardRepo->expenses($account, $userId, $viewAll);
|
||||||
$tasks = $dashboardRepo->tasks($accountId, $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;
|
$showBlueVinePromo = false;
|
||||||
if ($user->is_admin && env('BLUEVINE_PARTNER_UNIQUE_ID')) {
|
if ($user->is_admin && env('BLUEVINE_PARTNER_UNIQUE_ID')) {
|
||||||
$showBlueVinePromo = ! $account->company->bluevine_status
|
$showBlueVinePromo = ! $account->company->bluevine_status
|
||||||
|
|
@ -67,10 +123,14 @@ class DashboardController extends BaseController
|
||||||
'account' => $user->account,
|
'account' => $user->account,
|
||||||
'user' => $user,
|
'user' => $user,
|
||||||
'paidToDate' => $paidToDate,
|
'paidToDate' => $paidToDate,
|
||||||
|
'paidToDateTotal' => $paidToDateTotal,
|
||||||
'balances' => $balances,
|
'balances' => $balances,
|
||||||
|
'balancesTotals' => $balancesTotals,
|
||||||
'averageInvoice' => $averageInvoice,
|
'averageInvoice' => $averageInvoice,
|
||||||
|
'averageInvoiceTotal' => $averageInvoiceTotal,
|
||||||
'invoicesSent' => $metrics ? $metrics->invoices_sent : 0,
|
'invoicesSent' => $metrics ? $metrics->invoices_sent : 0,
|
||||||
'activeClients' => $metrics ? $metrics->active_clients : 0,
|
'activeClients' => $metrics ? $metrics->active_clients : 0,
|
||||||
|
'invoiceExchangeRateMissing' => $account->getInvoiceExchangeRateCustomFieldIndex() ? false : true,
|
||||||
'activities' => $activities,
|
'activities' => $activities,
|
||||||
'pastDue' => $pastDue,
|
'pastDue' => $pastDue,
|
||||||
'upcoming' => $upcoming,
|
'upcoming' => $upcoming,
|
||||||
|
|
@ -80,6 +140,7 @@ class DashboardController extends BaseController
|
||||||
'showBreadcrumbs' => false,
|
'showBreadcrumbs' => false,
|
||||||
'currencies' => $this->getCurrencyCodes(),
|
'currencies' => $this->getCurrencyCodes(),
|
||||||
'expenses' => $expenses,
|
'expenses' => $expenses,
|
||||||
|
'expensesTotals' => $expensesTotals,
|
||||||
'tasks' => $tasks,
|
'tasks' => $tasks,
|
||||||
'showBlueVinePromo' => $showBlueVinePromo,
|
'showBlueVinePromo' => $showBlueVinePromo,
|
||||||
'showWhiteLabelExpired' => $showWhiteLabelExpired,
|
'showWhiteLabelExpired' => $showWhiteLabelExpired,
|
||||||
|
|
|
||||||
|
|
@ -151,10 +151,10 @@ class DocumentAPIController extends BaseAPIController
|
||||||
* )
|
* )
|
||||||
*/
|
*/
|
||||||
public function destroy(UpdateDocumentRequest $request)
|
public function destroy(UpdateDocumentRequest $request)
|
||||||
{
|
{
|
||||||
$entity = $request->entity();
|
$entity = $request->entity();
|
||||||
|
|
||||||
$this->documentRepo->delete($entity);
|
$entity->delete();
|
||||||
|
|
||||||
return $this->itemResponse($entity);
|
return $this->itemResponse($entity);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ namespace App\Http\Controllers;
|
||||||
use App\Http\Requests\CreateDocumentRequest;
|
use App\Http\Requests\CreateDocumentRequest;
|
||||||
use App\Http\Requests\DocumentRequest;
|
use App\Http\Requests\DocumentRequest;
|
||||||
use App\Http\Requests\UpdateDocumentRequest;
|
use App\Http\Requests\UpdateDocumentRequest;
|
||||||
|
use App\Models\Contact;
|
||||||
use App\Models\Document;
|
use App\Models\Document;
|
||||||
use App\Ninja\Repositories\DocumentRepository;
|
use App\Ninja\Repositories\DocumentRepository;
|
||||||
use Redirect;
|
use Redirect;
|
||||||
|
|
@ -56,6 +57,7 @@ class DocumentController extends BaseController
|
||||||
|
|
||||||
public function getPreview(DocumentRequest $request)
|
public function getPreview(DocumentRequest $request)
|
||||||
{
|
{
|
||||||
|
|
||||||
$document = $request->entity();
|
$document = $request->entity();
|
||||||
|
|
||||||
if (empty($document->preview)) {
|
if (empty($document->preview)) {
|
||||||
|
|
@ -118,6 +120,7 @@ class DocumentController extends BaseController
|
||||||
'code' => 200,
|
'code' => 200,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
return Response::json($response, 200);
|
return Response::json($response, 200);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -47,7 +47,7 @@ class ExpenseApiController extends BaseAPIController
|
||||||
$expenses = Expense::scope()
|
$expenses = Expense::scope()
|
||||||
->withTrashed()
|
->withTrashed()
|
||||||
->with('client', 'invoice', 'vendor', 'expense_category')
|
->with('client', 'invoice', 'vendor', 'expense_category')
|
||||||
->orderBy('created_at', 'desc');
|
->orderBy('updated_at', 'desc');
|
||||||
|
|
||||||
return $this->listResponse($expenses);
|
return $this->listResponse($expenses);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -54,14 +54,14 @@ class InvoiceApiController extends BaseAPIController
|
||||||
* response="default",
|
* response="default",
|
||||||
* description="an ""unexpected"" error"
|
* description="an ""unexpected"" error"
|
||||||
* )
|
* )
|
||||||
* )
|
* );
|
||||||
*/
|
*/
|
||||||
public function index()
|
public function index()
|
||||||
{
|
{
|
||||||
$invoices = Invoice::scope()
|
$invoices = Invoice::scope()
|
||||||
->withTrashed()
|
->withTrashed()
|
||||||
->with('invoice_items', 'client')
|
->with('invoice_items', 'client')
|
||||||
->orderBy('created_at', 'desc');
|
->orderBy('updated_at', 'desc');
|
||||||
|
|
||||||
// Filter by invoice number
|
// Filter by invoice number
|
||||||
if ($invoiceNumber = Input::get('invoice_number')) {
|
if ($invoiceNumber = Input::get('invoice_number')) {
|
||||||
|
|
@ -73,11 +73,11 @@ class InvoiceApiController extends BaseAPIController
|
||||||
$invoices->where('invoice_status_id', '>=', $statusId);
|
$invoices->where('invoice_status_id', '>=', $statusId);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (request()->has('is_recurring')) {
|
if (request()->filled('is_recurring')) {
|
||||||
$invoices->where('is_recurring', '=', request()->is_recurring);
|
$invoices->where('is_recurring', '=', request()->is_recurring);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (request()->has('invoice_type_id')) {
|
if (request()->filled('invoice_type_id')) {
|
||||||
$invoices->where('invoice_type_id', '=', request()->invoice_type_id);
|
$invoices->where('invoice_type_id', '=', request()->invoice_type_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -104,7 +104,7 @@ class InvoiceApiController extends BaseAPIController
|
||||||
* response="default",
|
* response="default",
|
||||||
* description="an ""unexpected"" error"
|
* description="an ""unexpected"" error"
|
||||||
* )
|
* )
|
||||||
* )
|
* );
|
||||||
*/
|
*/
|
||||||
public function show(InvoiceRequest $request)
|
public function show(InvoiceRequest $request)
|
||||||
{
|
{
|
||||||
|
|
@ -130,7 +130,7 @@ class InvoiceApiController extends BaseAPIController
|
||||||
* response="default",
|
* response="default",
|
||||||
* description="an ""unexpected"" error"
|
* description="an ""unexpected"" error"
|
||||||
* )
|
* )
|
||||||
* )
|
* );
|
||||||
*/
|
*/
|
||||||
public function store(CreateInvoiceAPIRequest $request)
|
public function store(CreateInvoiceAPIRequest $request)
|
||||||
{
|
{
|
||||||
|
|
@ -455,7 +455,7 @@ class InvoiceApiController extends BaseAPIController
|
||||||
* response="default",
|
* response="default",
|
||||||
* description="an ""unexpected"" error"
|
* description="an ""unexpected"" error"
|
||||||
* )
|
* )
|
||||||
* )
|
* );
|
||||||
*/
|
*/
|
||||||
public function destroy(UpdateInvoiceAPIRequest $request)
|
public function destroy(UpdateInvoiceAPIRequest $request)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -81,6 +81,14 @@ class InvoiceController extends BaseController
|
||||||
return $this->recurringInvoiceService->getDatatable($accountId, $clientPublicId, ENTITY_RECURRING_INVOICE, $search);
|
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)
|
public function edit(InvoiceRequest $request, $publicId, $clone = false)
|
||||||
{
|
{
|
||||||
$account = Auth::user()->account;
|
$account = Auth::user()->account;
|
||||||
|
|
@ -101,7 +109,6 @@ class InvoiceController extends BaseController
|
||||||
$entityType = $clone == INVOICE_TYPE_STANDARD ? ENTITY_INVOICE : ENTITY_QUOTE;
|
$entityType = $clone == INVOICE_TYPE_STANDARD ? ENTITY_INVOICE : ENTITY_QUOTE;
|
||||||
$invoice->id = $invoice->public_id = null;
|
$invoice->id = $invoice->public_id = null;
|
||||||
$invoice->is_public = false;
|
$invoice->is_public = false;
|
||||||
$invoice->is_recurring = $invoice->is_recurring && $clone == INVOICE_TYPE_STANDARD;
|
|
||||||
$invoice->invoice_type_id = $clone;
|
$invoice->invoice_type_id = $clone;
|
||||||
$invoice->invoice_number = $account->getNextNumber($invoice);
|
$invoice->invoice_number = $account->getNextNumber($invoice);
|
||||||
$invoice->due_date = null;
|
$invoice->due_date = null;
|
||||||
|
|
@ -138,7 +145,11 @@ class InvoiceController extends BaseController
|
||||||
'invoice_settings' => Auth::user()->hasFeature(FEATURE_INVOICE_SETTINGS),
|
'invoice_settings' => Auth::user()->hasFeature(FEATURE_INVOICE_SETTINGS),
|
||||||
];
|
];
|
||||||
|
|
||||||
$lastSent = ($invoice->is_recurring && $invoice->last_sent_date) ? $invoice->recurring_invoices->last() : null;
|
$lastSent = null;
|
||||||
|
if($invoice->is_recurring && $invoice->last_sent_date)
|
||||||
|
{
|
||||||
|
$lastSent = ($invoice->subEntityType() == ENTITY_RECURRING_INVOICE) ? $invoice->recurring_invoices->last() : $invoice->recurring_quotes->last();
|
||||||
|
}
|
||||||
|
|
||||||
if (! Auth::user()->hasPermission('view_client')) {
|
if (! Auth::user()->hasPermission('view_client')) {
|
||||||
$clients = $clients->where('clients.user_id', '=', Auth::user()->id);
|
$clients = $clients->where('clients.user_id', '=', Auth::user()->id);
|
||||||
|
|
@ -192,14 +203,17 @@ class InvoiceController extends BaseController
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (Auth::user()->registered && ! Auth::user()->confirmed) {
|
||||||
|
session()->flash('warning', trans('texts.confirmation_required', ['link' => link_to('/resend_confirmation', trans('texts.click_here'))]));
|
||||||
|
}
|
||||||
|
|
||||||
return View::make('invoices.edit', $data);
|
return View::make('invoices.edit', $data);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function create(InvoiceRequest $request, $clientPublicId = 0, $isRecurring = false)
|
public function create(InvoiceRequest $request, $clientPublicId = 0, $entityType = ENTITY_INVOICE)
|
||||||
{
|
{
|
||||||
$account = Auth::user()->account;
|
$account = Auth::user()->account;
|
||||||
|
|
||||||
$entityType = $isRecurring ? ENTITY_RECURRING_INVOICE : ENTITY_INVOICE;
|
|
||||||
$clientId = null;
|
$clientId = null;
|
||||||
|
|
||||||
if ($request->client_id) {
|
if ($request->client_id) {
|
||||||
|
|
@ -230,7 +244,12 @@ class InvoiceController extends BaseController
|
||||||
|
|
||||||
public function createRecurring(InvoiceRequest $request, $clientPublicId = 0)
|
public function createRecurring(InvoiceRequest $request, $clientPublicId = 0)
|
||||||
{
|
{
|
||||||
return self::create($request, $clientPublicId, true);
|
return self::create($request, $clientPublicId, ENTITY_RECURRING_INVOICE);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function createRecurringQuote(InvoiceRequest $request, $clientPublicId = 0)
|
||||||
|
{
|
||||||
|
return self::create($request, $clientPublicId, ENTITY_RECURRING_QUOTE);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static function getViewModel($invoice)
|
private static function getViewModel($invoice)
|
||||||
|
|
@ -302,7 +321,7 @@ class InvoiceController extends BaseController
|
||||||
|
|
||||||
// Check for any taxes which have been deleted
|
// Check for any taxes which have been deleted
|
||||||
$taxRateOptions = $account->present()->taxRateOptions;
|
$taxRateOptions = $account->present()->taxRateOptions;
|
||||||
if ($invoice->exists) {
|
if ($invoice->exists && !$invoice->deleted_at) {
|
||||||
foreach ($invoice->getTaxes() as $key => $rate) {
|
foreach ($invoice->getTaxes() as $key => $rate) {
|
||||||
$key = '0 ' . $key; // mark it as a standard exclusive rate option
|
$key = '0 ' . $key; // mark it as a standard exclusive rate option
|
||||||
if (isset($taxRateOptions[$key])) {
|
if (isset($taxRateOptions[$key])) {
|
||||||
|
|
@ -510,6 +529,10 @@ class InvoiceController extends BaseController
|
||||||
$entityType = ENTITY_RECURRING_INVOICE;
|
$entityType = ENTITY_RECURRING_INVOICE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (strpos(\Request::server('HTTP_REFERER'), 'recurring_quotes')) {
|
||||||
|
$entityType = ENTITY_RECURRING_QUOTE;
|
||||||
|
}
|
||||||
|
|
||||||
return $this->returnBulk($entityType, $action, $ids);
|
return $this->returnBulk($entityType, $action, $ids);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
611
app/Http/Controllers/Migration/StepsController.php
Normal file
611
app/Http/Controllers/Migration/StepsController.php
Normal file
|
|
@ -0,0 +1,611 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
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\Libraries\Utils;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
use Illuminate\Support\Facades\Auth;
|
||||||
|
use Illuminate\Support\Facades\Storage;
|
||||||
|
use App\Http\Controllers\BaseController;
|
||||||
|
|
||||||
|
class StepsController extends BaseController
|
||||||
|
{
|
||||||
|
private $account;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View
|
||||||
|
*/
|
||||||
|
public function start()
|
||||||
|
{
|
||||||
|
return view('migration.start');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function import()
|
||||||
|
{
|
||||||
|
return view('migration.import');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View
|
||||||
|
*/
|
||||||
|
public function download()
|
||||||
|
{
|
||||||
|
return view('migration.download');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle data downloading for the migration.
|
||||||
|
*
|
||||||
|
* @return \Illuminate\Http\JsonResponse
|
||||||
|
*/
|
||||||
|
public function handleDownload()
|
||||||
|
{
|
||||||
|
$this->account = Auth::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 = [
|
||||||
|
'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(),
|
||||||
|
];
|
||||||
|
|
||||||
|
$file = storage_path("{$fileName}.zip");
|
||||||
|
|
||||||
|
$zip = new \ZipArchive();
|
||||||
|
$zip->open($file, \ZipArchive::CREATE | \ZipArchive::OVERWRITE);
|
||||||
|
$zip->addFromString('migration.json', json_encode($data));
|
||||||
|
$zip->close();
|
||||||
|
|
||||||
|
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,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
return $transformed;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -11,6 +11,7 @@ use App\Models\Payment;
|
||||||
use App\Models\PaymentMethod;
|
use App\Models\PaymentMethod;
|
||||||
use App\Models\Product;
|
use App\Models\Product;
|
||||||
use App\Ninja\Mailers\UserMailer;
|
use App\Ninja\Mailers\UserMailer;
|
||||||
|
use App\Ninja\PaymentDrivers\PaymentActionRequiredException;
|
||||||
use App\Ninja\Repositories\ClientRepository;
|
use App\Ninja\Repositories\ClientRepository;
|
||||||
use App\Ninja\Repositories\InvoiceRepository;
|
use App\Ninja\Repositories\InvoiceRepository;
|
||||||
use App\Services\InvoiceService;
|
use App\Services\InvoiceService;
|
||||||
|
|
@ -124,11 +125,18 @@ class OnlinePaymentController extends BaseController
|
||||||
*
|
*
|
||||||
* @return \Illuminate\Http\RedirectResponse
|
* @return \Illuminate\Http\RedirectResponse
|
||||||
*/
|
*/
|
||||||
public function doPayment(CreateOnlinePaymentRequest $request, $invitationKey, $gatewayTypeAlias = false)
|
public function doPayment(
|
||||||
|
CreateOnlinePaymentRequest $request,
|
||||||
|
$invitationKey,
|
||||||
|
$gatewayTypeAlias = false,
|
||||||
|
$sourceId = false
|
||||||
|
)
|
||||||
{
|
{
|
||||||
$invitation = $request->invitation;
|
$invitation = $request->invitation;
|
||||||
|
|
||||||
if ($gatewayTypeAlias) {
|
if ($gatewayTypeAlias == GATEWAY_TYPE_TOKEN) {
|
||||||
|
$gatewayTypeId = $gatewayTypeAlias;
|
||||||
|
} elseif ($gatewayTypeAlias) {
|
||||||
$gatewayTypeId = GatewayType::getIdFromAlias($gatewayTypeAlias);
|
$gatewayTypeId = GatewayType::getIdFromAlias($gatewayTypeAlias);
|
||||||
} else {
|
} else {
|
||||||
$gatewayTypeId = Session::get($invitation->id . 'gateway_type');
|
$gatewayTypeId = Session::get($invitation->id . 'gateway_type');
|
||||||
|
|
@ -141,7 +149,16 @@ class OnlinePaymentController extends BaseController
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$paymentDriver->completeOnsitePurchase($request->all());
|
// Load the payment method to charge.
|
||||||
|
// Currently only hit for saved cards that still require 3D secure verification.
|
||||||
|
$paymentMethod = null;
|
||||||
|
if ($sourceId) {
|
||||||
|
$paymentMethod = PaymentMethod::clientId($invitation->invoice->client_id)
|
||||||
|
->wherePublicId($sourceId)
|
||||||
|
->firstOrFail();
|
||||||
|
}
|
||||||
|
|
||||||
|
$paymentDriver->completeOnsitePurchase($request->all(), $paymentMethod);
|
||||||
|
|
||||||
if (request()->capture) {
|
if (request()->capture) {
|
||||||
return redirect('/client/dashboard')->withMessage(trans('texts.updated_payment_details'));
|
return redirect('/client/dashboard')->withMessage(trans('texts.updated_payment_details'));
|
||||||
|
|
@ -152,6 +169,8 @@ class OnlinePaymentController extends BaseController
|
||||||
}
|
}
|
||||||
|
|
||||||
return $this->completePurchase($invitation);
|
return $this->completePurchase($invitation);
|
||||||
|
} catch (PaymentActionRequiredException $exception) {
|
||||||
|
return $paymentDriver->startStepTwo($exception->getData());
|
||||||
} catch (Exception $exception) {
|
} catch (Exception $exception) {
|
||||||
return $this->error($paymentDriver, $exception, true);
|
return $this->error($paymentDriver, $exception, true);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -51,7 +51,7 @@ class PaymentApiController extends BaseAPIController
|
||||||
$payments = Payment::scope()
|
$payments = Payment::scope()
|
||||||
->withTrashed()
|
->withTrashed()
|
||||||
->with(['invoice'])
|
->with(['invoice'])
|
||||||
->orderBy('created_at', 'desc');
|
->orderBy('updated_at', 'desc');
|
||||||
|
|
||||||
return $this->listResponse($payments);
|
return $this->listResponse($payments);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -218,7 +218,7 @@ class PaymentController extends BaseController
|
||||||
Session::flash('message', trans($credit ? 'texts.created_payment_and_credit' : 'texts.created_payment'));
|
Session::flash('message', trans($credit ? 'texts.created_payment_and_credit' : 'texts.created_payment'));
|
||||||
}
|
}
|
||||||
|
|
||||||
return redirect()->to($payment->client->getRoute() . '#payments');
|
return url($payment->client->getRoute());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -56,7 +56,7 @@ class ProductApiController extends BaseAPIController
|
||||||
{
|
{
|
||||||
$products = Product::scope()
|
$products = Product::scope()
|
||||||
->withTrashed()
|
->withTrashed()
|
||||||
->orderBy('created_at', 'desc');
|
->orderBy('updated_at', 'desc');
|
||||||
|
|
||||||
return $this->listResponse($products);
|
return $this->listResponse($products);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -74,7 +74,7 @@ class ProjectApiController extends BaseAPIController
|
||||||
{
|
{
|
||||||
$projects = Project::scope()
|
$projects = Project::scope()
|
||||||
->withTrashed()
|
->withTrashed()
|
||||||
->orderBy('created_at', 'desc');
|
->orderBy('updated_at', 'desc');
|
||||||
|
|
||||||
return $this->listResponse($projects);
|
return $this->listResponse($projects);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -54,7 +54,7 @@ class ProjectController extends BaseController
|
||||||
{
|
{
|
||||||
$account = auth()->user()->account;
|
$account = auth()->user()->account;
|
||||||
$project = $request->entity();
|
$project = $request->entity();
|
||||||
$chartData = dispatch(new GenerateProjectChartData($project));
|
$chartData = dispatch_now(new GenerateProjectChartData($project));
|
||||||
|
|
||||||
$data = [
|
$data = [
|
||||||
'account' => auth()->user()->account,
|
'account' => auth()->user()->account,
|
||||||
|
|
|
||||||
|
|
@ -168,7 +168,7 @@ class ProposalController extends BaseController
|
||||||
{
|
{
|
||||||
$proposal = $request->entity();
|
$proposal = $request->entity();
|
||||||
|
|
||||||
$pdf = dispatch(new ConvertProposalToPdf($proposal));
|
$pdf = dispatch_now(new ConvertProposalToPdf($proposal));
|
||||||
|
|
||||||
$this->downloadResponse($proposal->getFilename(), $pdf);
|
$this->downloadResponse($proposal->getFilename(), $pdf);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -35,7 +35,7 @@ class QuoteApiController extends InvoiceApiController
|
||||||
->withTrashed()
|
->withTrashed()
|
||||||
->quotes()
|
->quotes()
|
||||||
->with('invoice_items', 'client')
|
->with('invoice_items', 'client')
|
||||||
->orderBy('created_at', 'desc');
|
->orderBy('updated_at', 'desc');
|
||||||
|
|
||||||
return $this->listResponse($invoices);
|
return $this->listResponse($invoices);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,7 @@ use App\Ninja\Mailers\ContactMailer as Mailer;
|
||||||
use App\Ninja\Repositories\ClientRepository;
|
use App\Ninja\Repositories\ClientRepository;
|
||||||
use App\Ninja\Repositories\InvoiceRepository;
|
use App\Ninja\Repositories\InvoiceRepository;
|
||||||
use App\Services\InvoiceService;
|
use App\Services\InvoiceService;
|
||||||
|
use App\Services\RecurringInvoiceService;
|
||||||
use Auth;
|
use Auth;
|
||||||
use Cache;
|
use Cache;
|
||||||
use Input;
|
use Input;
|
||||||
|
|
@ -33,7 +34,7 @@ class QuoteController extends BaseController
|
||||||
protected $invoiceService;
|
protected $invoiceService;
|
||||||
protected $entityType = ENTITY_INVOICE;
|
protected $entityType = ENTITY_INVOICE;
|
||||||
|
|
||||||
public function __construct(Mailer $mailer, InvoiceRepository $invoiceRepo, ClientRepository $clientRepo, InvoiceService $invoiceService)
|
public function __construct(Mailer $mailer, InvoiceRepository $invoiceRepo, ClientRepository $clientRepo, InvoiceService $invoiceService, RecurringInvoiceService $recurringInvoiceService)
|
||||||
{
|
{
|
||||||
// parent::__construct();
|
// parent::__construct();
|
||||||
|
|
||||||
|
|
@ -41,6 +42,7 @@ class QuoteController extends BaseController
|
||||||
$this->invoiceRepo = $invoiceRepo;
|
$this->invoiceRepo = $invoiceRepo;
|
||||||
$this->clientRepo = $clientRepo;
|
$this->clientRepo = $clientRepo;
|
||||||
$this->invoiceService = $invoiceService;
|
$this->invoiceService = $invoiceService;
|
||||||
|
$this->recurringInvoiceService = $recurringInvoiceService;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function index()
|
public function index()
|
||||||
|
|
@ -65,6 +67,14 @@ class QuoteController extends BaseController
|
||||||
return $this->invoiceService->getDatatable($accountId, $clientPublicId, ENTITY_QUOTE, $search);
|
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)
|
public function create(QuoteRequest $request, $clientPublicId = 0)
|
||||||
{
|
{
|
||||||
if (! Utils::hasFeature(FEATURE_QUOTES)) {
|
if (! Utils::hasFeature(FEATURE_QUOTES)) {
|
||||||
|
|
@ -157,7 +167,7 @@ class QuoteController extends BaseController
|
||||||
|
|
||||||
if ($invoice->due_date) {
|
if ($invoice->due_date) {
|
||||||
$carbonDueDate = \Carbon::parse($invoice->due_date);
|
$carbonDueDate = \Carbon::parse($invoice->due_date);
|
||||||
if (! $carbonDueDate->isToday() && ! $carbonDueDate->isFuture()) {
|
if (! $account->allow_approve_expired_quote && ! $carbonDueDate->isToday() && ! $carbonDueDate->isFuture()) {
|
||||||
return redirect("view/{$invitationKey}")->withError(trans('texts.quote_has_expired'));
|
return redirect("view/{$invitationKey}")->withError(trans('texts.quote_has_expired'));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
41
app/Http/Controllers/RecurringQuoteController.php
Normal file
41
app/Http/Controllers/RecurringQuoteController.php
Normal file
|
|
@ -0,0 +1,41 @@
|
||||||
|
<?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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -61,6 +61,8 @@ class ReportController extends BaseController
|
||||||
$action = Input::get('action');
|
$action = Input::get('action');
|
||||||
$format = Input::get('format');
|
$format = Input::get('format');
|
||||||
|
|
||||||
|
$account = Auth::user()->account;
|
||||||
|
|
||||||
if (Input::get('report_type')) {
|
if (Input::get('report_type')) {
|
||||||
$reportType = Input::get('report_type');
|
$reportType = Input::get('report_type');
|
||||||
$dateField = Input::get('date_field');
|
$dateField = Input::get('date_field');
|
||||||
|
|
@ -69,7 +71,7 @@ class ReportController extends BaseController
|
||||||
} else {
|
} else {
|
||||||
$reportType = ENTITY_INVOICE;
|
$reportType = ENTITY_INVOICE;
|
||||||
$dateField = FILTER_INVOICE_DATE;
|
$dateField = FILTER_INVOICE_DATE;
|
||||||
$startDate = Utils::today(false)->modify('-3 month');
|
$startDate = Utils::today(false)->modify('-1 month');
|
||||||
$endDate = Utils::today(false);
|
$endDate = Utils::today(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -85,6 +87,7 @@ class ReportController extends BaseController
|
||||||
'product',
|
'product',
|
||||||
'profit_and_loss',
|
'profit_and_loss',
|
||||||
'task',
|
'task',
|
||||||
|
'task_details',
|
||||||
'tax_rate',
|
'tax_rate',
|
||||||
'quote',
|
'quote',
|
||||||
];
|
];
|
||||||
|
|
@ -95,10 +98,10 @@ class ReportController extends BaseController
|
||||||
'reportTypes' => array_combine($reportTypes, Utils::trans($reportTypes)),
|
'reportTypes' => array_combine($reportTypes, Utils::trans($reportTypes)),
|
||||||
'reportType' => $reportType,
|
'reportType' => $reportType,
|
||||||
'title' => trans('texts.charts_and_reports'),
|
'title' => trans('texts.charts_and_reports'),
|
||||||
'account' => Auth::user()->account,
|
'account' => $account,
|
||||||
];
|
];
|
||||||
|
|
||||||
if (Auth::user()->account->hasFeature(FEATURE_REPORTS)) {
|
if ($account->hasFeature(FEATURE_REPORTS)) {
|
||||||
$isExport = $action == 'export';
|
$isExport = $action == 'export';
|
||||||
$config = [
|
$config = [
|
||||||
'date_field' => $dateField,
|
'date_field' => $dateField,
|
||||||
|
|
@ -111,11 +114,12 @@ class ReportController extends BaseController
|
||||||
'start_date' => $params['startDate'],
|
'start_date' => $params['startDate'],
|
||||||
'end_date' => $params['endDate'],
|
'end_date' => $params['endDate'],
|
||||||
];
|
];
|
||||||
$report = dispatch(new RunReport(auth()->user(), $reportType, $config, $isExport));
|
|
||||||
|
$report = dispatch_now(new RunReport(auth()->user(), $reportType, $config, $account, $isExport));
|
||||||
$params = array_merge($params, $report->exportParams);
|
$params = array_merge($params, $report->exportParams);
|
||||||
switch ($action) {
|
switch ($action) {
|
||||||
case 'export':
|
case 'export':
|
||||||
return dispatch(new ExportReportResults(auth()->user(), $format, $reportType, $params))->export($format);
|
return dispatch_now(new ExportReportResults(auth()->user(), $format, $reportType, $params))->export($format);
|
||||||
break;
|
break;
|
||||||
case 'schedule':
|
case 'schedule':
|
||||||
self::schedule($params, $config);
|
self::schedule($params, $config);
|
||||||
|
|
@ -190,7 +194,7 @@ class ReportController extends BaseController
|
||||||
|
|
||||||
public function loadEmailReport($startDate, $endDate)
|
public function loadEmailReport($startDate, $endDate)
|
||||||
{
|
{
|
||||||
$data = dispatch(new LoadPostmarkStats($startDate, $endDate));
|
$data = dispatch_now(new LoadPostmarkStats($startDate, $endDate));
|
||||||
|
|
||||||
return response()->json($data);
|
return response()->json($data);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -45,8 +45,8 @@ class TaskApiController extends BaseAPIController
|
||||||
{
|
{
|
||||||
$tasks = Task::scope()
|
$tasks = Task::scope()
|
||||||
->withTrashed()
|
->withTrashed()
|
||||||
->with('client', 'invoice', 'project')
|
->with('client', 'invoice', 'project', 'task_status', 'user')
|
||||||
->orderBy('created_at', 'desc');
|
->orderBy('updated_at', 'desc');
|
||||||
|
|
||||||
return $this->listResponse($tasks);
|
return $this->listResponse($tasks);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
47
app/Http/Controllers/TaskController.php
Normal file → Executable file
47
app/Http/Controllers/TaskController.php
Normal file → Executable file
|
|
@ -7,6 +7,7 @@ use App\Http\Requests\TaskRequest;
|
||||||
use App\Http\Requests\UpdateTaskRequest;
|
use App\Http\Requests\UpdateTaskRequest;
|
||||||
use App\Models\Client;
|
use App\Models\Client;
|
||||||
use App\Models\Project;
|
use App\Models\Project;
|
||||||
|
use App\Models\Product;
|
||||||
use App\Models\Task;
|
use App\Models\Task;
|
||||||
use App\Models\TaskStatus;
|
use App\Models\TaskStatus;
|
||||||
use App\Ninja\Datatables\TaskDatatable;
|
use App\Ninja\Datatables\TaskDatatable;
|
||||||
|
|
@ -128,6 +129,7 @@ class TaskController extends BaseController
|
||||||
'task' => null,
|
'task' => null,
|
||||||
'clientPublicId' => Input::old('client') ? Input::old('client') : ($request->client_id ?: 0),
|
'clientPublicId' => Input::old('client') ? Input::old('client') : ($request->client_id ?: 0),
|
||||||
'projectPublicId' => Input::old('project_id') ? Input::old('project_id') : ($request->project_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',
|
'method' => 'POST',
|
||||||
'url' => 'tasks',
|
'url' => 'tasks',
|
||||||
'title' => trans('texts.new_task'),
|
'title' => trans('texts.new_task'),
|
||||||
|
|
@ -140,6 +142,12 @@ class TaskController extends BaseController
|
||||||
return View::make('tasks.edit', $data);
|
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.
|
* Show the form for editing the specified resource.
|
||||||
*
|
*
|
||||||
|
|
@ -147,7 +155,7 @@ class TaskController extends BaseController
|
||||||
*
|
*
|
||||||
* @return \Illuminate\Contracts\View\View
|
* @return \Illuminate\Contracts\View\View
|
||||||
*/
|
*/
|
||||||
public function edit(TaskRequest $request)
|
public function edit(TaskRequest $request, $publicId, $clone = false)
|
||||||
{
|
{
|
||||||
$this->checkTimezone();
|
$this->checkTimezone();
|
||||||
$task = $request->entity();
|
$task = $request->entity();
|
||||||
|
|
@ -172,19 +180,36 @@ class TaskController extends BaseController
|
||||||
|
|
||||||
$actions[] = DropdownButton::DIVIDER;
|
$actions[] = DropdownButton::DIVIDER;
|
||||||
if (! $task->trashed()) {
|
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:submitAction("archive")', 'label' => trans('texts.archive_task')];
|
||||||
$actions[] = ['url' => 'javascript:onDeleteClick()', 'label' => trans('texts.delete_task')];
|
$actions[] = ['url' => 'javascript:onDeleteClick()', 'label' => trans('texts.delete_task')];
|
||||||
} else {
|
} else {
|
||||||
$actions[] = ['url' => 'javascript:submitAction("restore")', 'label' => trans('texts.restore_task')];
|
$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 = [
|
$data = [
|
||||||
'task' => $task,
|
'task' => $task,
|
||||||
'entity' => $task,
|
'entity' => $task,
|
||||||
'clientPublicId' => $task->client ? $task->client->public_id : 0,
|
'clientPublicId' => $task->client ? $task->client->public_id : 0,
|
||||||
'projectPublicId' => $task->project ? $task->project->public_id : 0,
|
'projectPublicId' => $task->project ? $task->project->public_id : 0,
|
||||||
'method' => 'PUT',
|
'productPublicId' => $task->product ? $task->product->public_id : 0,
|
||||||
'url' => 'tasks/'.$task->public_id,
|
'method' => $method,
|
||||||
|
'url' => $url,
|
||||||
'title' => trans('texts.edit_task'),
|
'title' => trans('texts.edit_task'),
|
||||||
'actions' => $actions,
|
'actions' => $actions,
|
||||||
'timezone' => Auth::user()->account->timezone ? Auth::user()->account->timezone->name : DEFAULT_TIMEZONE,
|
'timezone' => Auth::user()->account->timezone ? Auth::user()->account->timezone->name : DEFAULT_TIMEZONE,
|
||||||
|
|
@ -219,6 +244,7 @@ class TaskController extends BaseController
|
||||||
'clients' => Client::scope()->withActiveOrSelected($task ? $task->client_id : false)->with('contacts')->orderBy('name')->get(),
|
'clients' => Client::scope()->withActiveOrSelected($task ? $task->client_id : false)->with('contacts')->orderBy('name')->get(),
|
||||||
'account' => Auth::user()->account,
|
'account' => Auth::user()->account,
|
||||||
'projects' => Project::scope()->withActiveOrSelected($task ? $task->project_id : false)->with('client.contacts')->orderBy('name')->get(),
|
'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(),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -229,8 +255,13 @@ class TaskController extends BaseController
|
||||||
*/
|
*/
|
||||||
private function save($request, $publicId = null)
|
private function save($request, $publicId = null)
|
||||||
{
|
{
|
||||||
|
|
||||||
$action = Input::get('action');
|
$action = Input::get('action');
|
||||||
|
|
||||||
|
if ( in_array($action, ['clone'])) {
|
||||||
|
return redirect()->to(sprintf('tasks/%s/clone', $publicId));
|
||||||
|
}
|
||||||
|
|
||||||
if (in_array($action, ['archive', 'delete', 'restore'])) {
|
if (in_array($action, ['archive', 'delete', 'restore'])) {
|
||||||
return self::bulk();
|
return self::bulk();
|
||||||
}
|
}
|
||||||
|
|
@ -303,12 +334,20 @@ class TaskController extends BaseController
|
||||||
|
|
||||||
$account = Auth::user()->account;
|
$account = Auth::user()->account;
|
||||||
$showProject = $lastProjectId != $task->project_id;
|
$showProject = $lastProjectId != $task->project_id;
|
||||||
$data[] = [
|
$item_data = [
|
||||||
'publicId' => $task->public_id,
|
'publicId' => $task->public_id,
|
||||||
'description' => $task->present()->invoiceDescription($account, $showProject),
|
'description' => $task->present()->invoiceDescription($account, $showProject),
|
||||||
'duration' => $task->getHours(),
|
'duration' => $task->getHours(),
|
||||||
'cost' => $task->getRate(),
|
'cost' => $task->getRate(),
|
||||||
|
'productKey' => null,
|
||||||
];
|
];
|
||||||
|
|
||||||
|
if (!empty($task->product_id)) {
|
||||||
|
$item_data['productKey'] = $task->product->product_key;
|
||||||
|
}
|
||||||
|
|
||||||
|
$data[] = $item_data;
|
||||||
|
|
||||||
$lastProjectId = $task->project_id;
|
$lastProjectId = $task->project_id;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -125,7 +125,7 @@ class TaskKanbanController extends BaseController
|
||||||
$origSortOrder = $status->sort_order;
|
$origSortOrder = $status->sort_order;
|
||||||
$newSortOrder = request('sort_order');
|
$newSortOrder = request('sort_order');
|
||||||
|
|
||||||
if (request()->has('sort_order') && $newSortOrder != $origSortOrder) {
|
if (request()->filled('sort_order') && $newSortOrder != $origSortOrder) {
|
||||||
TaskStatus::scope()
|
TaskStatus::scope()
|
||||||
->where('sort_order', '>', $origSortOrder)
|
->where('sort_order', '>', $origSortOrder)
|
||||||
->decrement('sort_order');
|
->decrement('sort_order');
|
||||||
|
|
|
||||||
|
|
@ -185,7 +185,7 @@ class TaxRateApiController extends BaseAPIController
|
||||||
{
|
{
|
||||||
$entity = $request->entity();
|
$entity = $request->entity();
|
||||||
|
|
||||||
$this->taxRateRepo->delete($entity);
|
$entity->delete();
|
||||||
|
|
||||||
return $this->itemResponse($entity);
|
return $this->itemResponse($entity);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
375
app/Http/Controllers/TicketController.php
Normal file
375
app/Http/Controllers/TicketController.php
Normal file
|
|
@ -0,0 +1,375 @@
|
||||||
|
<?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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
200
app/Http/Controllers/TicketTemplateController.php
Normal file
200
app/Http/Controllers/TicketTemplateController.php
Normal file
|
|
@ -0,0 +1,200 @@
|
||||||
|
<?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');
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -53,7 +53,7 @@ class VendorApiController extends BaseAPIController
|
||||||
{
|
{
|
||||||
$vendors = Vendor::scope()
|
$vendors = Vendor::scope()
|
||||||
->withTrashed()
|
->withTrashed()
|
||||||
->orderBy('created_at', 'desc');
|
->orderBy('updated_at', 'desc');
|
||||||
|
|
||||||
return $this->listResponse($vendors);
|
return $this->listResponse($vendors);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -63,5 +63,6 @@ class Kernel extends HttpKernel
|
||||||
'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class,
|
'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class,
|
||||||
'lookup' => \App\Http\Middleware\DatabaseLookup::class,
|
'lookup' => \App\Http\Middleware\DatabaseLookup::class,
|
||||||
'permissions.required' => \App\Http\Middleware\PermissionsRequired::class,
|
'permissions.required' => \App\Http\Middleware\PermissionsRequired::class,
|
||||||
|
'ticket' => \App\Http\Middleware\InboundTicketCheck::class,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -71,7 +71,9 @@ class ApiCheck
|
||||||
return $next($request);
|
return $next($request);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (! Utils::hasFeature(FEATURE_API) && ! $hasApiSecret) {
|
$isMobileApp = strpos(array_get($_SERVER, 'HTTP_USER_AGENT'), '(dart:io)') !== false;
|
||||||
|
|
||||||
|
if (! Utils::hasFeature(FEATURE_API) && ! $hasApiSecret && ! $isMobileApp) {
|
||||||
$error['error'] = ['message' => 'API requires pro plan'];
|
$error['error'] = ['message' => 'API requires pro plan'];
|
||||||
|
|
||||||
return Response::json($error, 403, $headers);
|
return Response::json($error, 403, $headers);
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ use App\Models\Account;
|
||||||
use App\Models\Contact;
|
use App\Models\Contact;
|
||||||
use App\Models\Invitation;
|
use App\Models\Invitation;
|
||||||
use App\Models\ProposalInvitation;
|
use App\Models\ProposalInvitation;
|
||||||
|
use App\Models\TicketInvitation;
|
||||||
use Auth;
|
use Auth;
|
||||||
use Utils;
|
use Utils;
|
||||||
use Closure;
|
use Closure;
|
||||||
|
|
@ -28,14 +29,22 @@ class Authenticate
|
||||||
public function handle($request, Closure $next, $guard = 'user')
|
public function handle($request, Closure $next, $guard = 'user')
|
||||||
{
|
{
|
||||||
$authenticated = Auth::guard($guard)->check();
|
$authenticated = Auth::guard($guard)->check();
|
||||||
$invitationKey = $request->invitation_key ?: $request->proposal_invitation_key;
|
|
||||||
|
$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;
|
||||||
|
|
||||||
if ($guard == 'client') {
|
if ($guard == 'client') {
|
||||||
if (! empty($request->invitation_key) || ! empty($request->proposal_invitation_key)) {
|
if (! empty($request->invitation_key) || ! empty($request->proposal_invitation_key) || ! empty($request->ticket_invitation_key)) {
|
||||||
$contact_key = session('contact_key');
|
$contact_key = session('contact_key');
|
||||||
if ($contact_key) {
|
if ($contact_key) {
|
||||||
$contact = $this->getContact($contact_key);
|
$contact = $this->getContact($contact_key);
|
||||||
$invitation = $this->getInvitation($invitationKey, ! empty($request->proposal_invitation_key));
|
$invitation = $this->getInvitation($invitationKey, ! empty($request->proposal_invitation_key), ! empty($request->ticket_invitation_key));
|
||||||
|
|
||||||
if (! $invitation) {
|
if (! $invitation) {
|
||||||
return response()->view('error', [
|
return response()->view('error', [
|
||||||
|
|
@ -63,7 +72,7 @@ class Authenticate
|
||||||
$contact = false;
|
$contact = false;
|
||||||
if ($contact_key) {
|
if ($contact_key) {
|
||||||
$contact = $this->getContact($contact_key);
|
$contact = $this->getContact($contact_key);
|
||||||
} elseif ($invitation = $this->getInvitation($invitationKey, ! empty($request->proposal_invitation_key))) {
|
} elseif ($invitationKey && $invitation = $this->getInvitation($invitationKey, ! empty($request->proposal_invitation_key), ! empty($request->ticket_invitation_key))) {
|
||||||
$contact = $invitation->contact;
|
$contact = $invitation->contact;
|
||||||
Session::put('contact_key', $contact->contact_key);
|
Session::put('contact_key', $contact->contact_key);
|
||||||
}
|
}
|
||||||
|
|
@ -127,7 +136,7 @@ class Authenticate
|
||||||
*
|
*
|
||||||
* @return \Illuminate\Database\Eloquent\Model|null|static
|
* @return \Illuminate\Database\Eloquent\Model|null|static
|
||||||
*/
|
*/
|
||||||
protected function getInvitation($key, $isProposal = false)
|
protected function getInvitation($key, $isProposal = false, $isTicket = false)
|
||||||
{
|
{
|
||||||
if (! $key) {
|
if (! $key) {
|
||||||
return false;
|
return false;
|
||||||
|
|
@ -139,6 +148,8 @@ class Authenticate
|
||||||
|
|
||||||
if ($isProposal) {
|
if ($isProposal) {
|
||||||
$invitation = ProposalInvitation::withTrashed()->where('invitation_key', '=', $key)->first();
|
$invitation = ProposalInvitation::withTrashed()->where('invitation_key', '=', $key)->first();
|
||||||
|
} elseif ($isTicket) {
|
||||||
|
$invitation = TicketInvitation::withTrashed()->where('invitation_key', '=', $key)->first();
|
||||||
} else {
|
} else {
|
||||||
$invitation = Invitation::withTrashed()->where('invitation_key', '=', $key)->first();
|
$invitation = Invitation::withTrashed()->where('invitation_key', '=', $key)->first();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,22 +0,0 @@
|
||||||
<?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');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
namespace App\Http\Middleware;
|
namespace App\Http\Middleware;
|
||||||
|
|
||||||
|
use App\Models\LookupTicketInvitation;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
use Closure;
|
use Closure;
|
||||||
use App\Models\LookupAccount;
|
use App\Models\LookupAccount;
|
||||||
|
|
@ -46,10 +47,14 @@ class DatabaseLookup
|
||||||
LookupInvitation::setServerByField('invitation_key', $key);
|
LookupInvitation::setServerByField('invitation_key', $key);
|
||||||
} elseif ($key = request()->proposal_invitation_key) {
|
} elseif ($key = request()->proposal_invitation_key) {
|
||||||
LookupProposalInvitation::setServerByField('invitation_key', $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')) {
|
} elseif ($key = request()->contact_key ?: session('contact_key')) {
|
||||||
LookupContact::setServerByField('contact_key', $key);
|
LookupContact::setServerByField('contact_key', $key);
|
||||||
} elseif ($key = request()->account_key) {
|
} elseif ($key = request()->account_key) {
|
||||||
LookupAccount::setServerByField('account_key', $key);
|
LookupAccount::setServerByField('account_key', $key);
|
||||||
|
} elseif($key = request()->MailboxHash) {
|
||||||
|
LookupTicketInvitation::setServerByField('ticket_hash', $key);
|
||||||
} else {
|
} else {
|
||||||
$subdomain = Utils::getSubdomain(\Request::server('HTTP_HOST'));
|
$subdomain = Utils::getSubdomain(\Request::server('HTTP_HOST'));
|
||||||
if ($subdomain != 'app') {
|
if ($subdomain != 'app') {
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,7 @@ class DuplicateSubmissionCheck
|
||||||
{
|
{
|
||||||
if ($request->is('api/v1/*')
|
if ($request->is('api/v1/*')
|
||||||
|| $request->is('save_sidebar_state')
|
|| $request->is('save_sidebar_state')
|
||||||
|
|| $request->is('tickets/search')
|
||||||
|| $request->is('documents')) {
|
|| $request->is('documents')) {
|
||||||
return $next($request);
|
return $next($request);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
50
app/Http/Middleware/InboundTicketCheck.php
Normal file
50
app/Http/Middleware/InboundTicketCheck.php
Normal file
|
|
@ -0,0 +1,50 @@
|
||||||
|
<?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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -62,9 +62,8 @@ class StartupCheck
|
||||||
|
|
||||||
if (Utils::isSelfHost()) {
|
if (Utils::isSelfHost()) {
|
||||||
// Check if config:cache may have been run
|
// Check if config:cache may have been run
|
||||||
if (! env('APP_URL')) {
|
if (app()->configurationIsCached()) {
|
||||||
echo "<p>There appears to be a problem with your configuration, please check your .env file.</p>" .
|
echo 'Config caching is not currently supported, please run the following command to clear the cache.<pre>php artisan config:clear</pre>';
|
||||||
"<p>If you've run 'php artisan config:cache' you will need to run 'php artisan config:clear'</p>.";
|
|
||||||
exit;
|
exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -72,8 +71,8 @@ class StartupCheck
|
||||||
$file = storage_path() . '/version.txt';
|
$file = storage_path() . '/version.txt';
|
||||||
$version = @file_get_contents($file);
|
$version = @file_get_contents($file);
|
||||||
if ($version != NINJA_VERSION) {
|
if ($version != NINJA_VERSION) {
|
||||||
if (version_compare(phpversion(), '7.0.0', '<')) {
|
if (version_compare(phpversion(), '7.1.0', '<')) {
|
||||||
dd('Please update PHP to >= 7.0.0');
|
dd('Please update PHP to >= 7.1.0');
|
||||||
}
|
}
|
||||||
$handle = fopen($file, 'w');
|
$handle = fopen($file, 'w');
|
||||||
fwrite($handle, NINJA_VERSION);
|
fwrite($handle, NINJA_VERSION);
|
||||||
|
|
@ -95,7 +94,7 @@ class StartupCheck
|
||||||
Session::put(SESSION_COUNTER, ++$count);
|
Session::put(SESSION_COUNTER, ++$count);
|
||||||
|
|
||||||
if (Utils::isNinja()) {
|
if (Utils::isNinja()) {
|
||||||
if ($coupon = request()->coupon) {
|
if (($coupon = request()->coupon) && ! $company->hasActivePlan()) {
|
||||||
if ($code = config('ninja.coupon_50_off')) {
|
if ($code = config('ninja.coupon_50_off')) {
|
||||||
if (hash_equals($coupon, $code)) {
|
if (hash_equals($coupon, $code)) {
|
||||||
$company->applyDiscount(.5);
|
$company->applyDiscount(.5);
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,7 @@ class VerifyCsrfToken extends BaseVerifier
|
||||||
'payment_hook/*',
|
'payment_hook/*',
|
||||||
'buy_now*',
|
'buy_now*',
|
||||||
'hook/bot/*',
|
'hook/bot/*',
|
||||||
|
'tickets/inbound',
|
||||||
];
|
];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Reference in a new issue