Compare commits
179 Commits
feature/IO
...
feature/IO
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fbc622fa04 | ||
|
|
64851047bf | ||
|
|
73fac34ef4 | ||
|
|
5ca34105ef | ||
|
|
fcfa1a9be8 | ||
|
|
41849644f3 | ||
|
|
f36fb06dd6 | ||
|
|
af4c4a4fa3 | ||
|
|
d6045a9334 | ||
|
|
dcc29f23d4 | ||
|
|
638a9fc76b | ||
|
|
53e3b3fa03 | ||
|
|
56c1b6f992 | ||
|
|
22f9a7ee3d | ||
|
|
cfcad472fd | ||
|
|
1a622f1b2c | ||
|
|
87e3adf579 | ||
|
|
aa7a4ccdd0 | ||
|
|
28dc10f5a1 | ||
|
|
b2d615b9c1 | ||
|
|
1e40a22762 | ||
|
|
d1407162d9 | ||
|
|
7a1984d037 | ||
|
|
9bcc449f20 | ||
|
|
bc7d0ef171 | ||
|
|
3e9b046476 | ||
|
|
e0ccd62c82 | ||
|
|
fd0970aef2 | ||
|
|
b673bcae7a | ||
|
|
63673548a0 | ||
|
|
29b74a8c0e | ||
|
|
2658626c7e | ||
|
|
763b199646 | ||
|
|
17905fa844 | ||
|
|
74a0b78a71 | ||
|
|
797a423702 | ||
|
|
2fce8c9644 | ||
|
|
9cd39c1c3e | ||
|
|
6264a2f45c | ||
|
|
94e47d14ad | ||
|
|
9319f492dd | ||
|
|
8f04c5a12c | ||
|
|
436a41405d | ||
|
|
a2150009db | ||
|
|
e1c785322f | ||
|
|
c1d71720ab | ||
|
|
89ff7740e2 | ||
|
|
4e69fe819e | ||
|
|
a8cc3fa190 | ||
|
|
10fceb7ddf | ||
|
|
6c1a0cff8d | ||
|
|
d92d2cca9a | ||
|
|
62a800a2c0 | ||
|
|
9c408d8bf5 | ||
|
|
45ad09c100 | ||
|
|
dd5cafcd42 | ||
|
|
eb48b56f47 | ||
|
|
a879e99e77 | ||
|
|
ddd816e7ca | ||
|
|
d646e5f285 | ||
|
|
c10517a11b | ||
|
|
ba683a2e8a | ||
|
|
6b66b76f84 | ||
|
|
c3fe763261 | ||
|
|
5209c12b89 | ||
|
|
bf7aa17f65 | ||
|
|
cd6e0dcde3 | ||
|
|
a2822f5592 | ||
|
|
ca129fa4a0 | ||
|
|
cbe0c78553 | ||
|
|
2e763f1dd5 | ||
|
|
b8942c320e | ||
|
|
eee135f4ef | ||
|
|
de92b2d47e | ||
|
|
5d7384aa8b | ||
|
|
bf18e687da | ||
|
|
e69e844568 | ||
|
|
2b5268fb77 | ||
|
|
eebe7edba8 | ||
|
|
1a5c74dc79 | ||
|
|
be62ab5ff9 | ||
|
|
85497eb815 | ||
|
|
5724d0129c | ||
|
|
fd579fc509 | ||
|
|
6e21b1bdf6 | ||
|
|
a7ad18fae2 | ||
|
|
8bfa879485 | ||
|
|
ea774ff22b | ||
|
|
88101b0252 | ||
|
|
60ec76701d | ||
|
|
6b52723ba9 | ||
|
|
910c2a0f9b | ||
|
|
6c93e600c4 | ||
|
|
e70edaec7c | ||
|
|
acaba96e3b | ||
|
|
12d1613b04 | ||
|
|
df878672fc | ||
|
|
076115253f | ||
|
|
6f58528de2 | ||
|
|
3defe7201f | ||
|
|
4ce75ead52 | ||
|
|
6de7ec00fe | ||
|
|
90ea2cd699 | ||
|
|
800552210b | ||
|
|
80abea56b4 | ||
|
|
480f081c40 | ||
|
|
9529335c96 | ||
|
|
4334b3f419 | ||
|
|
94353bb342 | ||
|
|
5aad7acdd5 | ||
|
|
cd4f7ffb9c | ||
|
|
400dc79ed6 | ||
|
|
1dfb309223 | ||
|
|
29c9fb37a1 | ||
|
|
41d6f0a4bc | ||
|
|
af70c80e09 | ||
|
|
b02d4e0fdd | ||
|
|
27bf8d9ed6 | ||
|
|
582ad03e05 | ||
|
|
babdfe4cc5 | ||
|
|
8a7a94dd70 | ||
|
|
2654519277 | ||
|
|
02eddcbbf4 | ||
|
|
b967bb6d4e | ||
|
|
f60870a087 | ||
|
|
39aa21d985 | ||
|
|
512bb5e013 | ||
|
|
78f041a34f | ||
|
|
2c456cbf03 | ||
|
|
da76021802 | ||
|
|
6de06e084b | ||
|
|
cb49c91983 | ||
|
|
b0df5fa91c | ||
|
|
0652404334 | ||
|
|
bd7d8068df | ||
|
|
4dd868130c | ||
|
|
71860cf899 | ||
|
|
3512905264 | ||
|
|
2c072a9e7a | ||
|
|
fee5bee569 | ||
|
|
0a1cdbdfe3 | ||
|
|
8af79989ff | ||
|
|
5d2bdc7ee1 | ||
|
|
255d65e47d | ||
|
|
f0805e0a79 | ||
|
|
c875ade35c | ||
|
|
31b4f4e561 | ||
|
|
2c1844fb13 | ||
|
|
bd59e40761 | ||
|
|
0652114013 | ||
|
|
3150647ff6 | ||
|
|
e2258bb91f | ||
|
|
3dd4b3dd77 | ||
|
|
d2f7585ea5 | ||
|
|
1af511be2f | ||
|
|
14b38604a3 | ||
|
|
40c7b706aa | ||
|
|
88c03ce655 | ||
|
|
d5b1496898 | ||
|
|
3486e16d4e | ||
|
|
3641363d3d | ||
|
|
fde13436c9 | ||
|
|
b9a9f07d7b | ||
|
|
f4473d11a8 | ||
|
|
965af6da5f | ||
|
|
fb5c5561e9 | ||
|
|
a5e3985745 | ||
|
|
88ee4f13e1 | ||
|
|
fa05d0b401 | ||
|
|
cf017fb80b | ||
|
|
56c366e9e8 | ||
|
|
07b7394fec | ||
|
|
0617d79d19 | ||
|
|
885e9c6958 | ||
|
|
6bf5f2fe77 | ||
|
|
a3cc5c2324 | ||
|
|
a44ed3c406 | ||
|
|
aa5110ae13 | ||
|
|
c8ee9ca5a7 |
@@ -1009,27 +1009,6 @@
|
||||
</translation>
|
||||
</translations>
|
||||
</concept_node>
|
||||
<concept_node>
|
||||
<name>smspaymentreminder</name>
|
||||
<definition_loaded>false</definition_loaded>
|
||||
<description></description>
|
||||
<comment></comment>
|
||||
<default_text></default_text>
|
||||
<translations>
|
||||
<translation>
|
||||
<language>en-US</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
<translation>
|
||||
<language>es-MX</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
<translation>
|
||||
<language>fr-CA</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
</translations>
|
||||
</concept_node>
|
||||
<concept_node>
|
||||
<name>suggesteddates</name>
|
||||
<definition_loaded>false</definition_loaded>
|
||||
@@ -1474,6 +1453,27 @@
|
||||
</translation>
|
||||
</translations>
|
||||
</concept_node>
|
||||
<concept_node>
|
||||
<name>admin_jobuninvoice</name>
|
||||
<definition_loaded>false</definition_loaded>
|
||||
<description></description>
|
||||
<comment></comment>
|
||||
<default_text></default_text>
|
||||
<translations>
|
||||
<translation>
|
||||
<language>en-US</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
<translation>
|
||||
<language>es-MX</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
<translation>
|
||||
<language>fr-CA</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
</translations>
|
||||
</concept_node>
|
||||
<concept_node>
|
||||
<name>admin_jobunvoid</name>
|
||||
<definition_loaded>false</definition_loaded>
|
||||
@@ -3571,6 +3571,27 @@
|
||||
<folder_node>
|
||||
<name>validation</name>
|
||||
<children>
|
||||
<concept_node>
|
||||
<name>closingperiod</name>
|
||||
<definition_loaded>false</definition_loaded>
|
||||
<description></description>
|
||||
<comment></comment>
|
||||
<default_text></default_text>
|
||||
<translations>
|
||||
<translation>
|
||||
<language>en-US</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
<translation>
|
||||
<language>es-MX</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
<translation>
|
||||
<language>fr-CA</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
</translations>
|
||||
</concept_node>
|
||||
<concept_node>
|
||||
<name>inventoryquantity</name>
|
||||
<definition_loaded>false</definition_loaded>
|
||||
@@ -4205,6 +4226,27 @@
|
||||
</translation>
|
||||
</translations>
|
||||
</concept_node>
|
||||
<concept_node>
|
||||
<name>closingperiod</name>
|
||||
<definition_loaded>false</definition_loaded>
|
||||
<description></description>
|
||||
<comment></comment>
|
||||
<default_text></default_text>
|
||||
<translations>
|
||||
<translation>
|
||||
<language>en-US</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
<translation>
|
||||
<language>es-MX</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
<translation>
|
||||
<language>fr-CA</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
</translations>
|
||||
</concept_node>
|
||||
<concept_node>
|
||||
<name>country</name>
|
||||
<definition_loaded>false</definition_loaded>
|
||||
@@ -4318,6 +4360,48 @@
|
||||
<folder_node>
|
||||
<name>dms</name>
|
||||
<children>
|
||||
<concept_node>
|
||||
<name>apcontrol</name>
|
||||
<definition_loaded>false</definition_loaded>
|
||||
<description></description>
|
||||
<comment></comment>
|
||||
<default_text></default_text>
|
||||
<translations>
|
||||
<translation>
|
||||
<language>en-US</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
<translation>
|
||||
<language>es-MX</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
<translation>
|
||||
<language>fr-CA</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
</translations>
|
||||
</concept_node>
|
||||
<concept_node>
|
||||
<name>appostingaccount</name>
|
||||
<definition_loaded>false</definition_loaded>
|
||||
<description></description>
|
||||
<comment></comment>
|
||||
<default_text></default_text>
|
||||
<translations>
|
||||
<translation>
|
||||
<language>en-US</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
<translation>
|
||||
<language>es-MX</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
<translation>
|
||||
<language>fr-CA</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
</translations>
|
||||
</concept_node>
|
||||
<concept_node>
|
||||
<name>cashierid</name>
|
||||
<definition_loaded>false</definition_loaded>
|
||||
@@ -6622,6 +6706,27 @@
|
||||
</translation>
|
||||
</translations>
|
||||
</concept_node>
|
||||
<concept_node>
|
||||
<name>void</name>
|
||||
<definition_loaded>false</definition_loaded>
|
||||
<description></description>
|
||||
<comment></comment>
|
||||
<default_text></default_text>
|
||||
<translations>
|
||||
<translation>
|
||||
<language>en-US</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
<translation>
|
||||
<language>es-MX</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
<translation>
|
||||
<language>fr-CA</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
</translations>
|
||||
</concept_node>
|
||||
</children>
|
||||
</folder_node>
|
||||
<folder_node>
|
||||
@@ -14004,6 +14109,48 @@
|
||||
</translation>
|
||||
</translations>
|
||||
</concept_node>
|
||||
<concept_node>
|
||||
<name>scheduledintoday</name>
|
||||
<definition_loaded>false</definition_loaded>
|
||||
<description></description>
|
||||
<comment></comment>
|
||||
<default_text></default_text>
|
||||
<translations>
|
||||
<translation>
|
||||
<language>en-US</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
<translation>
|
||||
<language>es-MX</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
<translation>
|
||||
<language>fr-CA</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
</translations>
|
||||
</concept_node>
|
||||
<concept_node>
|
||||
<name>scheduledouttoday</name>
|
||||
<definition_loaded>false</definition_loaded>
|
||||
<description></description>
|
||||
<comment></comment>
|
||||
<default_text></default_text>
|
||||
<translations>
|
||||
<translation>
|
||||
<language>en-US</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
<translation>
|
||||
<language>es-MX</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
<translation>
|
||||
<language>fr-CA</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
</translations>
|
||||
</concept_node>
|
||||
</children>
|
||||
</folder_node>
|
||||
</children>
|
||||
@@ -16060,6 +16207,27 @@
|
||||
</translation>
|
||||
</translations>
|
||||
</concept_node>
|
||||
<concept_node>
|
||||
<name>copied</name>
|
||||
<definition_loaded>false</definition_loaded>
|
||||
<description></description>
|
||||
<comment></comment>
|
||||
<default_text></default_text>
|
||||
<translations>
|
||||
<translation>
|
||||
<language>en-US</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
<translation>
|
||||
<language>es-MX</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
<translation>
|
||||
<language>fr-CA</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
</translations>
|
||||
</concept_node>
|
||||
<concept_node>
|
||||
<name>copylink</name>
|
||||
<definition_loaded>false</definition_loaded>
|
||||
@@ -16396,6 +16564,27 @@
|
||||
</translation>
|
||||
</translations>
|
||||
</concept_node>
|
||||
<concept_node>
|
||||
<name>sendbysms</name>
|
||||
<definition_loaded>false</definition_loaded>
|
||||
<description></description>
|
||||
<comment></comment>
|
||||
<default_text></default_text>
|
||||
<translations>
|
||||
<translation>
|
||||
<language>en-US</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
<translation>
|
||||
<language>es-MX</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
<translation>
|
||||
<language>fr-CA</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
</translations>
|
||||
</concept_node>
|
||||
<concept_node>
|
||||
<name>senderrortosupport</name>
|
||||
<definition_loaded>false</definition_loaded>
|
||||
@@ -19108,6 +19297,27 @@
|
||||
</translation>
|
||||
</translations>
|
||||
</concept_node>
|
||||
<concept_node>
|
||||
<name>openingip</name>
|
||||
<definition_loaded>false</definition_loaded>
|
||||
<description></description>
|
||||
<comment></comment>
|
||||
<default_text></default_text>
|
||||
<translations>
|
||||
<translation>
|
||||
<language>en-US</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
<translation>
|
||||
<language>es-MX</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
<translation>
|
||||
<language>fr-CA</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
</translations>
|
||||
</concept_node>
|
||||
<concept_node>
|
||||
<name>title</name>
|
||||
<definition_loaded>false</definition_loaded>
|
||||
@@ -23584,6 +23794,27 @@
|
||||
</translation>
|
||||
</translations>
|
||||
</concept_node>
|
||||
<concept_node>
|
||||
<name>date_void</name>
|
||||
<definition_loaded>false</definition_loaded>
|
||||
<description></description>
|
||||
<comment></comment>
|
||||
<default_text></default_text>
|
||||
<translations>
|
||||
<translation>
|
||||
<language>en-US</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
<translation>
|
||||
<language>es-MX</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
<translation>
|
||||
<language>fr-CA</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
</translations>
|
||||
</concept_node>
|
||||
<concept_node>
|
||||
<name>ded_amt</name>
|
||||
<definition_loaded>false</definition_loaded>
|
||||
@@ -23844,6 +24075,27 @@
|
||||
</translation>
|
||||
</translations>
|
||||
</concept_node>
|
||||
<concept_node>
|
||||
<name>dms_unsold</name>
|
||||
<definition_loaded>false</definition_loaded>
|
||||
<description></description>
|
||||
<comment></comment>
|
||||
<default_text></default_text>
|
||||
<translations>
|
||||
<translation>
|
||||
<language>en-US</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
<translation>
|
||||
<language>es-MX</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
<translation>
|
||||
<language>fr-CA</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
</translations>
|
||||
</concept_node>
|
||||
<concept_node>
|
||||
<name>dms_wip_acctnumber</name>
|
||||
<definition_loaded>false</definition_loaded>
|
||||
@@ -28343,6 +28595,27 @@
|
||||
</translation>
|
||||
</translations>
|
||||
</concept_node>
|
||||
<concept_node>
|
||||
<name>closingperiod</name>
|
||||
<definition_loaded>false</definition_loaded>
|
||||
<description></description>
|
||||
<comment></comment>
|
||||
<default_text></default_text>
|
||||
<translations>
|
||||
<translation>
|
||||
<language>en-US</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
<translation>
|
||||
<language>es-MX</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
<translation>
|
||||
<language>fr-CA</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
</translations>
|
||||
</concept_node>
|
||||
<concept_node>
|
||||
<name>contracts</name>
|
||||
<definition_loaded>false</definition_loaded>
|
||||
@@ -32753,27 +33026,6 @@
|
||||
</translation>
|
||||
</translations>
|
||||
</concept_node>
|
||||
<concept_node>
|
||||
<name>paymentremindersms</name>
|
||||
<definition_loaded>false</definition_loaded>
|
||||
<description></description>
|
||||
<comment></comment>
|
||||
<default_text></default_text>
|
||||
<translations>
|
||||
<translation>
|
||||
<language>en-US</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
<translation>
|
||||
<language>es-MX</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
<translation>
|
||||
<language>fr-CA</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
</translations>
|
||||
</concept_node>
|
||||
<concept_node>
|
||||
<name>phonebook</name>
|
||||
<definition_loaded>false</definition_loaded>
|
||||
@@ -36978,6 +37230,32 @@
|
||||
<folder_node>
|
||||
<name>payments</name>
|
||||
<children>
|
||||
<folder_node>
|
||||
<name>actions</name>
|
||||
<children>
|
||||
<concept_node>
|
||||
<name>generatepaymentlink</name>
|
||||
<definition_loaded>false</definition_loaded>
|
||||
<description></description>
|
||||
<comment></comment>
|
||||
<default_text></default_text>
|
||||
<translations>
|
||||
<translation>
|
||||
<language>en-US</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
<translation>
|
||||
<language>es-MX</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
<translation>
|
||||
<language>fr-CA</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
</translations>
|
||||
</concept_node>
|
||||
</children>
|
||||
</folder_node>
|
||||
<folder_node>
|
||||
<name>errors</name>
|
||||
<children>
|
||||
@@ -37023,6 +37301,27 @@
|
||||
</translation>
|
||||
</translations>
|
||||
</concept_node>
|
||||
<concept_node>
|
||||
<name>inserting</name>
|
||||
<definition_loaded>false</definition_loaded>
|
||||
<description></description>
|
||||
<comment></comment>
|
||||
<default_text></default_text>
|
||||
<translations>
|
||||
<translation>
|
||||
<language>en-US</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
<translation>
|
||||
<language>es-MX</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
<translation>
|
||||
<language>fr-CA</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
</translations>
|
||||
</concept_node>
|
||||
</children>
|
||||
</folder_node>
|
||||
<folder_node>
|
||||
@@ -37411,6 +37710,48 @@
|
||||
</translation>
|
||||
</translations>
|
||||
</concept_node>
|
||||
<concept_node>
|
||||
<name>markexported</name>
|
||||
<definition_loaded>false</definition_loaded>
|
||||
<description></description>
|
||||
<comment></comment>
|
||||
<default_text></default_text>
|
||||
<translations>
|
||||
<translation>
|
||||
<language>en-US</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
<translation>
|
||||
<language>es-MX</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
<translation>
|
||||
<language>fr-CA</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
</translations>
|
||||
</concept_node>
|
||||
<concept_node>
|
||||
<name>markforreexport</name>
|
||||
<definition_loaded>false</definition_loaded>
|
||||
<description></description>
|
||||
<comment></comment>
|
||||
<default_text></default_text>
|
||||
<translations>
|
||||
<translation>
|
||||
<language>en-US</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
<translation>
|
||||
<language>es-MX</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
<translation>
|
||||
<language>fr-CA</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
</translations>
|
||||
</concept_node>
|
||||
<concept_node>
|
||||
<name>new</name>
|
||||
<definition_loaded>false</definition_loaded>
|
||||
@@ -37453,6 +37794,27 @@
|
||||
</translation>
|
||||
</translations>
|
||||
</concept_node>
|
||||
<concept_node>
|
||||
<name>smspaymentreminder</name>
|
||||
<definition_loaded>false</definition_loaded>
|
||||
<description></description>
|
||||
<comment></comment>
|
||||
<default_text></default_text>
|
||||
<translations>
|
||||
<translation>
|
||||
<language>en-US</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
<translation>
|
||||
<language>es-MX</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
<translation>
|
||||
<language>fr-CA</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
</translations>
|
||||
</concept_node>
|
||||
<concept_node>
|
||||
<name>title</name>
|
||||
<definition_loaded>false</definition_loaded>
|
||||
@@ -37542,6 +37904,27 @@
|
||||
</translation>
|
||||
</translations>
|
||||
</concept_node>
|
||||
<concept_node>
|
||||
<name>markreexported</name>
|
||||
<definition_loaded>false</definition_loaded>
|
||||
<description></description>
|
||||
<comment></comment>
|
||||
<default_text></default_text>
|
||||
<translations>
|
||||
<translation>
|
||||
<language>en-US</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
<translation>
|
||||
<language>es-MX</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
<translation>
|
||||
<language>fr-CA</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
</translations>
|
||||
</concept_node>
|
||||
<concept_node>
|
||||
<name>payment</name>
|
||||
<definition_loaded>false</definition_loaded>
|
||||
@@ -39355,6 +39738,27 @@
|
||||
</translation>
|
||||
</translations>
|
||||
</concept_node>
|
||||
<concept_node>
|
||||
<name>mpi_final_repair_acct_sheet</name>
|
||||
<definition_loaded>false</definition_loaded>
|
||||
<description></description>
|
||||
<comment></comment>
|
||||
<default_text></default_text>
|
||||
<translations>
|
||||
<translation>
|
||||
<language>en-US</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
<translation>
|
||||
<language>es-MX</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
<translation>
|
||||
<language>fr-CA</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
</translations>
|
||||
</concept_node>
|
||||
<concept_node>
|
||||
<name>paint_grid</name>
|
||||
<definition_loaded>false</definition_loaded>
|
||||
@@ -40433,6 +40837,27 @@
|
||||
<folder_node>
|
||||
<name>jobs</name>
|
||||
<children>
|
||||
<concept_node>
|
||||
<name>individual_job_note</name>
|
||||
<definition_loaded>false</definition_loaded>
|
||||
<description></description>
|
||||
<comment></comment>
|
||||
<default_text></default_text>
|
||||
<translations>
|
||||
<translation>
|
||||
<language>en-US</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
<translation>
|
||||
<language>es-MX</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
<translation>
|
||||
<language>fr-CA</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
</translations>
|
||||
</concept_node>
|
||||
<concept_node>
|
||||
<name>parts_order</name>
|
||||
<definition_loaded>false</definition_loaded>
|
||||
@@ -40454,6 +40879,27 @@
|
||||
</translation>
|
||||
</translations>
|
||||
</concept_node>
|
||||
<concept_node>
|
||||
<name>parts_return_slip</name>
|
||||
<definition_loaded>false</definition_loaded>
|
||||
<description></description>
|
||||
<comment></comment>
|
||||
<default_text></default_text>
|
||||
<translations>
|
||||
<translation>
|
||||
<language>en-US</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
<translation>
|
||||
<language>es-MX</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
<translation>
|
||||
<language>fr-CA</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
</translations>
|
||||
</concept_node>
|
||||
<concept_node>
|
||||
<name>sublet_order</name>
|
||||
<definition_loaded>false</definition_loaded>
|
||||
@@ -43276,6 +43722,27 @@
|
||||
</translation>
|
||||
</translations>
|
||||
</concept_node>
|
||||
<concept_node>
|
||||
<name>jobs_scheduled_completion</name>
|
||||
<definition_loaded>false</definition_loaded>
|
||||
<description></description>
|
||||
<comment></comment>
|
||||
<default_text></default_text>
|
||||
<translations>
|
||||
<translation>
|
||||
<language>en-US</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
<translation>
|
||||
<language>es-MX</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
<translation>
|
||||
<language>fr-CA</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
</translations>
|
||||
</concept_node>
|
||||
<concept_node>
|
||||
<name>lag_time</name>
|
||||
<definition_loaded>false</definition_loaded>
|
||||
@@ -44284,6 +44751,27 @@
|
||||
</translation>
|
||||
</translations>
|
||||
</concept_node>
|
||||
<concept_node>
|
||||
<name>work_in_progress_jobs</name>
|
||||
<definition_loaded>false</definition_loaded>
|
||||
<description></description>
|
||||
<comment></comment>
|
||||
<default_text></default_text>
|
||||
<translations>
|
||||
<translation>
|
||||
<language>en-US</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
<translation>
|
||||
<language>es-MX</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
<translation>
|
||||
<language>fr-CA</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
</translations>
|
||||
</concept_node>
|
||||
<concept_node>
|
||||
<name>work_in_progress_labour</name>
|
||||
<definition_loaded>false</definition_loaded>
|
||||
@@ -44378,6 +44866,27 @@
|
||||
</translation>
|
||||
</translations>
|
||||
</concept_node>
|
||||
<concept_node>
|
||||
<name>estimators</name>
|
||||
<definition_loaded>false</definition_loaded>
|
||||
<description></description>
|
||||
<comment></comment>
|
||||
<default_text></default_text>
|
||||
<translations>
|
||||
<translation>
|
||||
<language>en-US</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
<translation>
|
||||
<language>es-MX</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
<translation>
|
||||
<language>fr-CA</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
</translations>
|
||||
</concept_node>
|
||||
<concept_node>
|
||||
<name>ins_co_nm_filter</name>
|
||||
<definition_loaded>false</definition_loaded>
|
||||
|
||||
@@ -148,6 +148,10 @@
|
||||
background: #e7f3ff !important;
|
||||
}
|
||||
|
||||
.ant-table-tbody > tr.ant-table-row-selected > td {
|
||||
background: #e6f7ff !important;
|
||||
}
|
||||
|
||||
.job-line-manual {
|
||||
color: tomato;
|
||||
font-style: italic;
|
||||
|
||||
@@ -1,49 +0,0 @@
|
||||
import React from "react";
|
||||
import { connect } from "react-redux";
|
||||
import { createStructuredSelector } from "reselect";
|
||||
import { logImEXEvent } from "../../firebase/firebase.utils";
|
||||
import { setEmailOptions } from "../../redux/email/email.actions";
|
||||
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||
import { TemplateList } from "../../utils/TemplateConstants";
|
||||
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
bodyshop: selectBodyshop,
|
||||
});
|
||||
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
setEmailOptions: (e) => dispatch(setEmailOptions(e)),
|
||||
});
|
||||
|
||||
function Test({ bodyshop, setEmailOptions }) {
|
||||
return (
|
||||
<div>
|
||||
<button
|
||||
onClick={() => {
|
||||
setEmailOptions({
|
||||
messageOptions: {
|
||||
to: ["patrickwf@gmail.com"],
|
||||
replyTo: bodyshop.email,
|
||||
},
|
||||
template: {
|
||||
name: TemplateList().parts_order.key,
|
||||
variables: {
|
||||
id: "a7c2d4e1-f519-42a9-a071-c48cf0f22979",
|
||||
},
|
||||
},
|
||||
});
|
||||
}}
|
||||
>
|
||||
send email
|
||||
</button>
|
||||
<button
|
||||
onClick={() => {
|
||||
logImEXEvent("IMEXEVENT", { somethignArThare: 5 });
|
||||
}}
|
||||
>
|
||||
Log an ImEX Event.
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(Test);
|
||||
63
client/src/components/_test/payment_response.json
Normal file
63
client/src/components/_test/payment_response.json
Normal file
@@ -0,0 +1,63 @@
|
||||
{
|
||||
"status": 24201299,
|
||||
"custid": 19607899,
|
||||
"paymentid": 24201299,
|
||||
"response": "A",
|
||||
"authcode": "498680",
|
||||
"declinereason": "Approved",
|
||||
"fee": 0,
|
||||
"invoice": "",
|
||||
"account": "john",
|
||||
"amount": 1000,
|
||||
"amountincludesfee": false,
|
||||
"total": 1000,
|
||||
"paymenttype": "C",
|
||||
"methodhint": "VI ***1111",
|
||||
"cardbrand": "Visa",
|
||||
"cardnumdisplay": "***1111",
|
||||
"receiptelements": {
|
||||
"authcode": "498680",
|
||||
"cust_srv_ph_num": "1-555-555-5555",
|
||||
"rcpt_pg_ftr_txt": "Thank You\nPlease Come Again",
|
||||
"rcpt_currency": "USD",
|
||||
"responsecode": "A",
|
||||
"rcpt_pay_mthd": "Visa",
|
||||
"transid": "C00 915799",
|
||||
"merch_disp_nm": "CP Devel Test",
|
||||
"rcpt_input_mthd": "Keyed",
|
||||
"rcpt_pg_hdr_txt": "Welcome!",
|
||||
"rcpt_tran_time": "Thursday February 23 2023, 11:25:36 pm +08",
|
||||
"rcpt_trans_type": "Normal Transaction (Sale)",
|
||||
"message": "Approved",
|
||||
"rcpt_dba_addr": "1234 Storefront Ave\nSome City, UT 84111",
|
||||
"avsdata": "N",
|
||||
"receiptrequirements": "S",
|
||||
"rcpt_cardnum": "************1111",
|
||||
"cv2result": "M",
|
||||
"rfnd_policy_txt": "<b>No Refunds</b>\nStore Credit Only",
|
||||
"labels": {
|
||||
"tranref": "REF#",
|
||||
"tid": "TID",
|
||||
"validationcode": "ValCode",
|
||||
"emvapplicationid": "AID",
|
||||
"emvatc": "ATC",
|
||||
"rcpt_pay_mthd": "Pay Method",
|
||||
"transid": "TransID",
|
||||
"rcpt_input_mthd": "IMode",
|
||||
"emvtsi": "TSI",
|
||||
"emvac": "AC",
|
||||
"rcpt_trans_type": "TranType",
|
||||
"emvapplicationname": "PApp",
|
||||
"visarewards": "RewardsProg"
|
||||
}
|
||||
},
|
||||
"receipttoken": "H4sIAAAAAAAAACXMTQuCMBgA4P/ynh3tw_3dBI/ipQ8NOtRN53QiblpBRfTfCzo/8LwhxGAdZCCwFYoJJFQjI2kvHdGu74lVkgmrWyWNhASW5jW7cB87yHjKKePGJODnxrrnMl7dDTKmEJlSOqV/_N30XPpyj2Eddq57_KKZ8FLzmh_G1VQnVfhjiXGK1XYTc/h8AVOkf4qUAAAA",
|
||||
"call": "card_payment",
|
||||
"nonce": "488b5568-b5c1-4f38-8b2f-3b050f3abb11P",
|
||||
"hmac": "JyPAJ9Yx0SlYBTtqns1OxAFRt+xF3l2UiLPO5zTDRBE=",
|
||||
"paymentreferenceid": "C19607899P24201299",
|
||||
"cardnum": "...1111",
|
||||
"email": "",
|
||||
"nameOnCard": "John Allen",
|
||||
"cardType": "visa"
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
import React from "react";
|
||||
import QboAuthorizeComponent from "../qbo-authorize/qbo-authorize.component";
|
||||
export default function Test() {
|
||||
return (
|
||||
<div>
|
||||
<QboAuthorizeComponent />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
31
client/src/components/_test/test.page.jsx
Normal file
31
client/src/components/_test/test.page.jsx
Normal file
@@ -0,0 +1,31 @@
|
||||
import { Button } from "antd";
|
||||
import React from "react";
|
||||
import { connect } from "react-redux";
|
||||
import { createStructuredSelector } from "reselect";
|
||||
import { setModalContext } from "../../redux/modals/modals.actions";
|
||||
|
||||
const mapStateToProps = createStructuredSelector({});
|
||||
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
setRefundPaymentContext: (context) =>
|
||||
dispatch(setModalContext({ context: context, modal: "refund_payment" })),
|
||||
});
|
||||
|
||||
function Test({ setRefundPaymentContext, refundPaymentModal }) {
|
||||
console.log("refundPaymentModal", refundPaymentModal);
|
||||
return (
|
||||
<div>
|
||||
<Button
|
||||
onClick={() =>
|
||||
setRefundPaymentContext({
|
||||
context: {},
|
||||
})
|
||||
}
|
||||
>
|
||||
Open Modal
|
||||
</Button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(Test);
|
||||
@@ -1,18 +1,18 @@
|
||||
import { useApolloClient, useMutation } from "@apollo/client";
|
||||
import { Button, Checkbox, Form, Modal, notification, Space } from "antd";
|
||||
import { Button, Checkbox, Form, Modal, Space, notification } from "antd";
|
||||
import _ from "lodash";
|
||||
import React, { useEffect, useState, useMemo } from "react";
|
||||
import React, { useEffect, useMemo, useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { connect } from "react-redux";
|
||||
import { createStructuredSelector } from "reselect";
|
||||
import { INSERT_NEW_BILL } from "../../graphql/bills.queries";
|
||||
import { UPDATE_INVENTORY_LINES } from "../../graphql/inventory.queries";
|
||||
import { UPDATE_JOB_LINE } from "../../graphql/jobs-lines.queries";
|
||||
import {
|
||||
QUERY_JOB_LBR_ADJUSTMENTS,
|
||||
UPDATE_JOB,
|
||||
} from "../../graphql/jobs.queries";
|
||||
import { MUTATION_MARK_RETURN_RECEIVED } from "../../graphql/parts-orders.queries";
|
||||
import { UPDATE_INVENTORY_LINES } from "../../graphql/inventory.queries";
|
||||
import { insertAuditTrail } from "../../redux/application/application.actions";
|
||||
import { toggleModalVisible } from "../../redux/modals/modals.actions";
|
||||
import { selectBillEnterModal } from "../../redux/modals/modals.selectors";
|
||||
@@ -20,15 +20,15 @@ import {
|
||||
selectBodyshop,
|
||||
selectCurrentUser,
|
||||
} from "../../redux/user/user.selectors";
|
||||
import confirmDialog from "../../utils/asyncConfirm";
|
||||
import AuditTrailMapping from "../../utils/AuditTrailMappings";
|
||||
import BillFormContainer from "../bill-form/bill-form.container";
|
||||
import { CalculateBillTotal } from "../bill-form/bill-form.totals.utility";
|
||||
import { handleUpload } from "../documents-upload/documents-upload.utility";
|
||||
import { handleUpload as handleLocalUpload } from "../documents-local-upload/documents-local-upload.utility";
|
||||
import useLocalStorage from "../../utils/useLocalStorage";
|
||||
import { GenerateDocument } from "../../utils/RenderTemplate";
|
||||
import { TemplateList } from "../../utils/TemplateConstants";
|
||||
import confirmDialog from "../../utils/asyncConfirm";
|
||||
import useLocalStorage from "../../utils/useLocalStorage";
|
||||
import BillFormContainer from "../bill-form/bill-form.container";
|
||||
import { CalculateBillTotal } from "../bill-form/bill-form.totals.utility";
|
||||
import { handleUpload as handleLocalUpload } from "../documents-local-upload/documents-local-upload.utility";
|
||||
import { handleUpload } from "../documents-upload/documents-upload.utility";
|
||||
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
billEnterModal: selectBillEnterModal,
|
||||
@@ -126,6 +126,17 @@ function BillEnterModalContainer({
|
||||
deductedfromlbr: deductedfromlbr,
|
||||
lbr_adjustment,
|
||||
joblineid: i.joblineid === "noline" ? null : i.joblineid,
|
||||
applicable_taxes: {
|
||||
federal:
|
||||
(i.applicable_taxes && i.applicable_taxes.federal) ||
|
||||
false,
|
||||
state:
|
||||
(i.applicable_taxes && i.applicable_taxes.state) ||
|
||||
false,
|
||||
local:
|
||||
(i.applicable_taxes && i.applicable_taxes.local) ||
|
||||
false,
|
||||
},
|
||||
};
|
||||
}),
|
||||
},
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import Icon, { UploadOutlined } from "@ant-design/icons";
|
||||
import { useApolloClient } from "@apollo/client";
|
||||
import { MdOpenInNew } from "react-icons/md";
|
||||
import { useTreatments } from "@splitsoftware/splitio-react";
|
||||
import {
|
||||
Alert,
|
||||
Divider,
|
||||
@@ -12,14 +12,17 @@ import {
|
||||
Switch,
|
||||
Upload,
|
||||
} from "antd";
|
||||
import moment from "moment";
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { MdOpenInNew } from "react-icons/md";
|
||||
import { connect } from "react-redux";
|
||||
import { Link } from "react-router-dom";
|
||||
import { createStructuredSelector } from "reselect";
|
||||
import { CHECK_BILL_INVOICE_NUMBER } from "../../graphql/bills.queries";
|
||||
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||
import AlertComponent from "../alert/alert.component";
|
||||
import BillFormLinesExtended from "../bill-form-lines-extended/bill-form-lines-extended.component";
|
||||
import FormDatePicker from "../form-date-picker/form-date-picker.component";
|
||||
import FormFieldsChanged from "../form-fields-changed-alert/form-fields-changed-alert.component";
|
||||
import CurrencyInput from "../form-items-formatted/currency-form-item.component";
|
||||
@@ -28,8 +31,6 @@ import LayoutFormRow from "../layout-form-row/layout-form-row.component";
|
||||
import VendorSearchSelect from "../vendor-search-select/vendor-search-select.component";
|
||||
import BillFormLines from "./bill-form.lines.component";
|
||||
import { CalculateBillTotal } from "./bill-form.totals.utility";
|
||||
import { useTreatments } from "@splitsoftware/splitio-react";
|
||||
import BillFormLinesExtended from "../bill-form-lines-extended/bill-form-lines-extended.component";
|
||||
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
bodyshop: selectBodyshop,
|
||||
@@ -58,6 +59,11 @@ export function BillFormComponent({
|
||||
{},
|
||||
bodyshop.imexshopid
|
||||
);
|
||||
const { ClosingPeriod } = useTreatments(
|
||||
["ClosingPeriod"],
|
||||
{},
|
||||
bodyshop.imexshopid
|
||||
);
|
||||
|
||||
const handleVendorSelect = (props, opt) => {
|
||||
setDiscount(opt.discount);
|
||||
@@ -259,6 +265,37 @@ export function BillFormComponent({
|
||||
required: true,
|
||||
//message: t("general.validation.required"),
|
||||
},
|
||||
({ getFieldValue }) => ({
|
||||
validator(rule, value) {
|
||||
if (
|
||||
ClosingPeriod.treatment === "on" &&
|
||||
bodyshop.accountingconfig.ClosingPeriod
|
||||
) {
|
||||
if (
|
||||
moment(value)
|
||||
.startOf("day")
|
||||
.isSameOrAfter(
|
||||
moment(
|
||||
bodyshop.accountingconfig.ClosingPeriod[0]
|
||||
).startOf("day")
|
||||
) &&
|
||||
moment(value)
|
||||
.startOf("day")
|
||||
.isSameOrBefore(
|
||||
moment(
|
||||
bodyshop.accountingconfig.ClosingPeriod[1]
|
||||
).endOf("day")
|
||||
)
|
||||
) {
|
||||
return Promise.resolve();
|
||||
} else {
|
||||
return Promise.reject(t("bills.validation.closingperiod"));
|
||||
}
|
||||
} else {
|
||||
return Promise.resolve();
|
||||
}
|
||||
},
|
||||
}),
|
||||
]}
|
||||
>
|
||||
<FormDatePicker disabled={disabled} />
|
||||
|
||||
@@ -0,0 +1,374 @@
|
||||
import { DeleteFilled } from "@ant-design/icons";
|
||||
import { useLazyQuery, useMutation } from "@apollo/client";
|
||||
import {
|
||||
Button,
|
||||
Card,
|
||||
Col,
|
||||
Form,
|
||||
Input,
|
||||
Row,
|
||||
Space,
|
||||
Spin,
|
||||
Statistic,
|
||||
notification,
|
||||
} from "antd";
|
||||
import axios from "axios";
|
||||
import moment from "moment";
|
||||
import React, { useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { connect } from "react-redux";
|
||||
import { createStructuredSelector } from "reselect";
|
||||
import {
|
||||
INSERT_PAYMENT_RESPONSE,
|
||||
QUERY_RO_AND_OWNER_BY_JOB_PKS,
|
||||
} from "../../graphql/payment_response.queries";
|
||||
import { INSERT_NEW_PAYMENT } from "../../graphql/payments.queries";
|
||||
import { insertAuditTrail } from "../../redux/application/application.actions";
|
||||
import { toggleModalVisible } from "../../redux/modals/modals.actions";
|
||||
import { selectCardPayment } from "../../redux/modals/modals.selectors";
|
||||
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||
import AuditTrailMapping from "../../utils/AuditTrailMappings";
|
||||
import CurrencyFormItemComponent from "../form-items-formatted/currency-form-item.component";
|
||||
import JobSearchSelectComponent from "../job-search-select/job-search-select.component";
|
||||
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
cardPaymentModal: selectCardPayment,
|
||||
bodyshop: selectBodyshop,
|
||||
});
|
||||
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
insertAuditTrail: ({ jobid, operation }) =>
|
||||
dispatch(insertAuditTrail({ jobid, operation })),
|
||||
toggleModalVisible: () => dispatch(toggleModalVisible("cardPayment")),
|
||||
});
|
||||
|
||||
const CardPaymentModalComponent = ({
|
||||
bodyshop,
|
||||
cardPaymentModal,
|
||||
toggleModalVisible,
|
||||
insertAuditTrail,
|
||||
}) => {
|
||||
const { context } = cardPaymentModal;
|
||||
|
||||
const [form] = Form.useForm();
|
||||
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [insertPayment] = useMutation(INSERT_NEW_PAYMENT);
|
||||
const [insertPaymentResponse] = useMutation(INSERT_PAYMENT_RESPONSE);
|
||||
const { t } = useTranslation();
|
||||
|
||||
const [, { data, refetch, queryLoading }] = useLazyQuery(
|
||||
QUERY_RO_AND_OWNER_BY_JOB_PKS,
|
||||
{
|
||||
variables: { jobids: [context.jobid] },
|
||||
skip: true,
|
||||
}
|
||||
);
|
||||
|
||||
console.log("🚀 ~ file: card-payment-modal.component..jsx:61 ~ data:", data);
|
||||
//Initialize the intellipay window.
|
||||
const SetIntellipayCallbackFunctions = () => {
|
||||
console.log("*** Set IntelliPay callback functions.");
|
||||
window.intellipay.runOnClose(() => {
|
||||
//window.intellipay.initialize();
|
||||
});
|
||||
|
||||
window.intellipay.runOnApproval(async function (response) {
|
||||
console.warn("*** Running On Approval Script ***");
|
||||
form.setFieldValue("paymentResponse", response);
|
||||
form.submit();
|
||||
});
|
||||
|
||||
window.intellipay.runOnNonApproval(async function (response) {
|
||||
// Mutate unsuccessful payment
|
||||
|
||||
const { payments } = form.getFieldsValue();
|
||||
|
||||
await insertPaymentResponse({
|
||||
variables: {
|
||||
paymentResponse: payments.map((payment) => ({
|
||||
amount: payment.amount,
|
||||
bodyshopid: bodyshop.id,
|
||||
jobid: payment.jobid,
|
||||
declinereason: response.declinereason,
|
||||
ext_paymentid: response.paymentid.toString(),
|
||||
successful: false,
|
||||
response,
|
||||
})),
|
||||
},
|
||||
});
|
||||
|
||||
payments.forEach((payment) =>
|
||||
insertAuditTrail({
|
||||
jobid: payment.jobid,
|
||||
operation: AuditTrailMapping.failedpayment(),
|
||||
})
|
||||
);
|
||||
});
|
||||
};
|
||||
|
||||
const handleFinish = async (values) => {
|
||||
try {
|
||||
await insertPayment({
|
||||
variables: {
|
||||
paymentInput: values.payments.map((payment) => ({
|
||||
amount: payment.amount,
|
||||
transactionid: (values.paymentResponse.paymentid || "").toString(),
|
||||
payer: t("payments.labels.customer"),
|
||||
type: values.paymentResponse.cardbrand,
|
||||
jobid: payment.jobid,
|
||||
date: moment(Date.now()),
|
||||
payment_responses: {
|
||||
data: [
|
||||
{
|
||||
amount: payment.amount,
|
||||
bodyshopid: bodyshop.id,
|
||||
|
||||
jobid: payment.jobid,
|
||||
declinereason: values.paymentResponse.declinereason,
|
||||
ext_paymentid: values.paymentResponse.paymentid.toString(),
|
||||
successful: true,
|
||||
response: values.paymentResponse,
|
||||
},
|
||||
],
|
||||
},
|
||||
})),
|
||||
},
|
||||
refetchQueries: ["GET_JOB_BY_PK"],
|
||||
});
|
||||
toggleModalVisible();
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
notification.open({
|
||||
type: "error",
|
||||
message: t("payments.errors.inserting", { error: error.message }),
|
||||
});
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
const handleIntelliPayCharge = async () => {
|
||||
setLoading(true);
|
||||
|
||||
//Validate
|
||||
try {
|
||||
await form.validateFields();
|
||||
} catch (error) {
|
||||
setLoading(false);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await axios.post("/intellipay/lightbox_credentials", {
|
||||
bodyshop,
|
||||
refresh: !!window.intellipay,
|
||||
});
|
||||
|
||||
if (window.intellipay) {
|
||||
// eslint-disable-next-line no-eval
|
||||
eval(response.data);
|
||||
SetIntellipayCallbackFunctions();
|
||||
window.intellipay.autoOpen();
|
||||
} else {
|
||||
var rg = document.createRange();
|
||||
let node = rg.createContextualFragment(response.data);
|
||||
document.documentElement.appendChild(node);
|
||||
SetIntellipayCallbackFunctions();
|
||||
window.intellipay.isAutoOpen = true;
|
||||
window.intellipay.initialize();
|
||||
}
|
||||
} catch (error) {
|
||||
notification.open({
|
||||
type: "error",
|
||||
message: t("job_payments.notifications.error.openingip"),
|
||||
});
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Card title="Card Payment">
|
||||
<Spin spinning={loading}>
|
||||
<Form
|
||||
onFinish={handleFinish}
|
||||
form={form}
|
||||
layout="vertical"
|
||||
initialValues={{
|
||||
payments: context.jobid ? [{ jobid: context.jobid }] : [],
|
||||
}}
|
||||
>
|
||||
<Form.List name={["payments"]}>
|
||||
{(fields, { add, remove, move }) => {
|
||||
return (
|
||||
<div>
|
||||
{fields.map((field, index) => (
|
||||
<Form.Item key={field.key}>
|
||||
<Row gutter={[16, 16]}>
|
||||
<Col span={16}>
|
||||
<Form.Item
|
||||
key={`${index}jobid`}
|
||||
label={t("jobs.fields.ro_number")}
|
||||
name={[field.name, "jobid"]}
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
//message: t("general.validation.required"),
|
||||
},
|
||||
]}
|
||||
>
|
||||
<JobSearchSelectComponent
|
||||
notExported={false}
|
||||
clm_no
|
||||
/>
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col span={6}>
|
||||
<Form.Item
|
||||
key={`${index}amount`}
|
||||
label={t("payments.fields.amount")}
|
||||
name={[field.name, "amount"]}
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
//message: t("general.validation.required"),
|
||||
},
|
||||
]}
|
||||
>
|
||||
<CurrencyFormItemComponent />
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col span={2}>
|
||||
<DeleteFilled
|
||||
style={{ margin: "1rem" }}
|
||||
onClick={() => {
|
||||
remove(field.name);
|
||||
}}
|
||||
/>
|
||||
</Col>
|
||||
</Row>
|
||||
</Form.Item>
|
||||
))}
|
||||
<Form.Item>
|
||||
<Button
|
||||
type="dashed"
|
||||
onClick={() => {
|
||||
add();
|
||||
}}
|
||||
style={{ width: "100%" }}
|
||||
>
|
||||
{t("general.actions.add")}
|
||||
</Button>
|
||||
</Form.Item>
|
||||
</div>
|
||||
);
|
||||
}}
|
||||
</Form.List>
|
||||
|
||||
<Form.Item
|
||||
shouldUpdate={(prevValues, curValues) =>
|
||||
prevValues.payments?.map((p) => p?.jobid).join() !==
|
||||
curValues.payments?.map((p) => p?.jobid).join()
|
||||
}
|
||||
>
|
||||
{() => {
|
||||
console.log("Updating the owner info section.");
|
||||
//If all of the job ids have been fileld in, then query and update the IP field.
|
||||
const { payments } = form.getFieldsValue();
|
||||
if (
|
||||
payments?.length > 0 &&
|
||||
payments?.filter((p) => p?.jobid).length === payments?.length
|
||||
) {
|
||||
console.log("**Calling refetch.");
|
||||
refetch({ jobids: payments.map((p) => p.jobid) });
|
||||
}
|
||||
console.log(
|
||||
"Acc info",
|
||||
data,
|
||||
payments && data && data.jobs.length > 0
|
||||
? data.jobs.map((j) => j.ro_number).join(", ")
|
||||
: null
|
||||
);
|
||||
return (
|
||||
<>
|
||||
<Input
|
||||
className="ipayfield"
|
||||
data-ipayname="account"
|
||||
//type="hidden"
|
||||
value={
|
||||
payments && data && data.jobs.length > 0
|
||||
? data.jobs.map((j) => j.ro_number).join(", ")
|
||||
: null
|
||||
}
|
||||
hidden
|
||||
/>
|
||||
<Input
|
||||
className="ipayfield"
|
||||
data-ipayname="email"
|
||||
// type="hidden"
|
||||
value={
|
||||
payments && data && data.jobs.length > 0
|
||||
? data.jobs.filter((j) => j.ownr_ea)[0]?.ownr_ea
|
||||
: null
|
||||
}
|
||||
hidden
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}}
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
shouldUpdate={(prevValues, curValues) =>
|
||||
prevValues.payments?.map((p) => p?.amount).join() !==
|
||||
curValues.payments?.map((p) => p?.amount).join()
|
||||
}
|
||||
>
|
||||
{() => {
|
||||
const { payments } = form.getFieldsValue();
|
||||
const totalAmountToCharge = payments?.reduce((acc, val) => {
|
||||
return acc + (val?.amount || 0);
|
||||
}, 0);
|
||||
|
||||
return (
|
||||
<Space style={{ float: "right" }}>
|
||||
<Statistic
|
||||
title="Amount To Charge"
|
||||
value={totalAmountToCharge}
|
||||
precision={2}
|
||||
/>
|
||||
<Input
|
||||
className="ipayfield"
|
||||
data-ipayname="amount"
|
||||
//type="hidden"
|
||||
value={totalAmountToCharge?.toFixed(2)}
|
||||
hidden
|
||||
/>
|
||||
<Button
|
||||
type="primary"
|
||||
// data-ipayname="submit"
|
||||
className="ipayfield"
|
||||
loading={queryLoading || loading}
|
||||
disabled={!(totalAmountToCharge > 0)}
|
||||
onClick={handleIntelliPayCharge}
|
||||
>
|
||||
{t("job_payments.buttons.proceedtopayment")}
|
||||
</Button>
|
||||
</Space>
|
||||
);
|
||||
}}
|
||||
</Form.Item>
|
||||
|
||||
{/* Lightbox payment response when it is completed */}
|
||||
<Form.Item name="paymentResponse" hidden>
|
||||
<Input type="hidden" />
|
||||
</Form.Item>
|
||||
</Form>
|
||||
</Spin>
|
||||
</Card>
|
||||
);
|
||||
};
|
||||
|
||||
export default connect(
|
||||
mapStateToProps,
|
||||
mapDispatchToProps
|
||||
)(CardPaymentModalComponent);
|
||||
@@ -0,0 +1,57 @@
|
||||
import { Button, Modal } from "antd";
|
||||
import React from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { connect } from "react-redux";
|
||||
import { createStructuredSelector } from "reselect";
|
||||
import { toggleModalVisible } from "../../redux/modals/modals.actions";
|
||||
import { selectCardPayment } from "../../redux/modals/modals.selectors";
|
||||
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||
import CardPaymentModalComponent from "./card-payment-modal.component.";
|
||||
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
cardPaymentModal: selectCardPayment,
|
||||
bodyshop: selectBodyshop,
|
||||
});
|
||||
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
toggleModalVisible: () => dispatch(toggleModalVisible("cardPayment")),
|
||||
});
|
||||
|
||||
function CardPaymentModalContainer({
|
||||
cardPaymentModal,
|
||||
toggleModalVisible,
|
||||
bodyshop,
|
||||
}) {
|
||||
const { visible } = cardPaymentModal;
|
||||
const { t } = useTranslation();
|
||||
|
||||
const handleCancel = () => {
|
||||
toggleModalVisible();
|
||||
};
|
||||
|
||||
const handleOK = () => {
|
||||
toggleModalVisible();
|
||||
};
|
||||
|
||||
return (
|
||||
<Modal
|
||||
open={visible}
|
||||
onOk={handleOK}
|
||||
onCancel={handleCancel}
|
||||
footer={[
|
||||
<Button key="back" onClick={handleCancel}>
|
||||
{t("job_payments.buttons.goback")}
|
||||
</Button>,
|
||||
]}
|
||||
width="80%"
|
||||
destroyOnClose
|
||||
>
|
||||
<CardPaymentModalComponent />
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
|
||||
export default connect(
|
||||
mapStateToProps,
|
||||
mapDispatchToProps
|
||||
)(CardPaymentModalContainer);
|
||||
@@ -59,6 +59,14 @@ export default function ContractsCarsComponent({
|
||||
sortOrder:
|
||||
state.sortedInfo.columnKey === "model" && state.sortedInfo.order,
|
||||
},
|
||||
{
|
||||
title: t("courtesycars.fields.color"),
|
||||
dataIndex: "color",
|
||||
key: "color",
|
||||
sorter: (a, b) => alphaSort(a.color, b.color),
|
||||
sortOrder:
|
||||
state.sortedInfo.columnKey === "color" && state.sortedInfo.order,
|
||||
},
|
||||
{
|
||||
title: t("courtesycars.fields.plate"),
|
||||
dataIndex: "plate",
|
||||
@@ -93,6 +101,9 @@ export default function ContractsCarsComponent({
|
||||
(cc.model || "")
|
||||
.toLowerCase()
|
||||
.includes(state.search.toLowerCase()) ||
|
||||
(cc.color || "")
|
||||
.toLowerCase()
|
||||
.includes(state.search.toLowerCase()) ||
|
||||
(cc.plate || "").toLowerCase().includes(state.search.toLowerCase())
|
||||
);
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@ export default function DataLabel({
|
||||
<div {...props} style={{ display: "flex" }}>
|
||||
<div
|
||||
style={{
|
||||
flex: 2,
|
||||
// flex: 2,
|
||||
marginRight: ".2rem",
|
||||
}}
|
||||
>
|
||||
|
||||
@@ -11,6 +11,7 @@ import {
|
||||
Select,
|
||||
Space,
|
||||
Statistic,
|
||||
Switch,
|
||||
Typography,
|
||||
} from "antd";
|
||||
import Dinero from "dinero.js";
|
||||
@@ -183,6 +184,13 @@ export function DmsPostForm({ bodyshop, socket, job, logsRef }) {
|
||||
<Space>
|
||||
<DmsCdkMakes form={form} socket={socket} job={job} />
|
||||
<DmsCdkMakesRefetch />
|
||||
<Form.Item
|
||||
name="dms_unsold"
|
||||
label={t("jobs.fields.dms.dms_unsold")}
|
||||
initialValue={false}
|
||||
>
|
||||
<Switch />
|
||||
</Form.Item>
|
||||
</Space>
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
import { useTreatments } from "@splitsoftware/splitio-react";
|
||||
import Icon, {
|
||||
BankFilled,
|
||||
BarChartOutlined,
|
||||
CarFilled,
|
||||
ClockCircleFilled,
|
||||
CheckCircleOutlined,
|
||||
ClockCircleFilled,
|
||||
DashboardFilled,
|
||||
DollarCircleFilled,
|
||||
ExportOutlined,
|
||||
@@ -26,6 +25,7 @@ import Icon, {
|
||||
UnorderedListOutlined,
|
||||
UserOutlined,
|
||||
} from "@ant-design/icons";
|
||||
import { useTreatments } from "@splitsoftware/splitio-react";
|
||||
import { Layout, Menu } from "antd";
|
||||
import React from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
@@ -70,6 +70,8 @@ const mapDispatchToProps = (dispatch) => ({
|
||||
setReportCenterContext: (context) =>
|
||||
dispatch(setModalContext({ context: context, modal: "reportCenter" })),
|
||||
signOutStart: () => dispatch(signOutStart()),
|
||||
setCardPaymentContext: (context) =>
|
||||
dispatch(setModalContext({ context: context, modal: "cardPayment" })),
|
||||
});
|
||||
|
||||
function Header({
|
||||
@@ -83,6 +85,7 @@ function Header({
|
||||
setPaymentContext,
|
||||
setReportCenterContext,
|
||||
recentItems,
|
||||
setCardPaymentContext,
|
||||
}) {
|
||||
const { Simple_Inventory } = useTreatments(
|
||||
["Simple_Inventory"],
|
||||
@@ -94,6 +97,11 @@ function Header({
|
||||
{},
|
||||
bodyshop && bodyshop.imexshopid
|
||||
);
|
||||
const { ImEXPay } = useTreatments(
|
||||
["ImEXPay"],
|
||||
{},
|
||||
bodyshop && bodyshop.imexshopid
|
||||
);
|
||||
|
||||
const { t } = useTranslation();
|
||||
|
||||
@@ -240,6 +248,20 @@ function Header({
|
||||
>
|
||||
{t("menus.header.enterpayment")}
|
||||
</Menu.Item>
|
||||
{ImEXPay.treatment === "on" && (
|
||||
<Menu.Item
|
||||
key="entercardpayments"
|
||||
onClick={() => {
|
||||
setCardPaymentContext({
|
||||
actions: {},
|
||||
context: {},
|
||||
});
|
||||
}}
|
||||
icon={<Icon component={FaCreditCard} />}
|
||||
>
|
||||
{t("menus.header.entercardpayment")}
|
||||
</Menu.Item>
|
||||
)}
|
||||
<Menu.Divider key="div5" />
|
||||
<Menu.Item key="timetickets" icon={<FieldTimeOutlined />}>
|
||||
<Link to="/manage/timetickets">
|
||||
@@ -252,7 +274,11 @@ function Header({
|
||||
onClick={() => {
|
||||
setTimeTicketContext({
|
||||
actions: {},
|
||||
context: {},
|
||||
context: {
|
||||
created_by: currentUser.displayName
|
||||
? currentUser.email.concat(" | ", currentUser.displayName)
|
||||
: currentUser.email,
|
||||
},
|
||||
});
|
||||
}}
|
||||
>
|
||||
|
||||
@@ -5,6 +5,7 @@ import { useTranslation } from "react-i18next";
|
||||
import { connect } from "react-redux";
|
||||
import { createStructuredSelector } from "reselect";
|
||||
import { SEARCH_VENDOR_AUTOCOMPLETE_WITH_ADDR } from "../../graphql/vendors.queries";
|
||||
import { selectTechnician } from "../../redux/tech/tech.selectors";
|
||||
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||
import { GenerateDocument } from "../../utils/RenderTemplate";
|
||||
import { TemplateList } from "../../utils/TemplateConstants";
|
||||
@@ -13,13 +14,14 @@ import VendorSearchSelect from "../vendor-search-select/vendor-search-select.com
|
||||
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
bodyshop: selectBodyshop,
|
||||
technician: selectTechnician,
|
||||
});
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
//setUserLanguage: language => dispatch(setUserLanguage(language))
|
||||
});
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(Jobd3RdPartyModal);
|
||||
|
||||
export function Jobd3RdPartyModal({ bodyshop, jobId, job }) {
|
||||
export function Jobd3RdPartyModal({ bodyshop, jobId, job, technician }) {
|
||||
const [isModalVisible, setIsModalVisible] = useState(false);
|
||||
const { t } = useTranslation();
|
||||
const [form] = Form.useForm();
|
||||
@@ -212,7 +214,9 @@ export function Jobd3RdPartyModal({ bodyshop, jobId, job }) {
|
||||
]}
|
||||
>
|
||||
<Radio.Group>
|
||||
<Radio value={"e"}>{t("parts_orders.labels.email")}</Radio>
|
||||
{!technician ? (
|
||||
<Radio value={"e"}>{t("parts_orders.labels.email")}</Radio>
|
||||
) : null}
|
||||
<Radio value={"p"}>{t("parts_orders.labels.print")}</Radio>
|
||||
</Radio.Group>
|
||||
</Form.Item>
|
||||
|
||||
@@ -29,11 +29,11 @@ import { GenerateDocument } from "../../utils/RenderTemplate";
|
||||
import { TemplateList } from "../../utils/TemplateConstants";
|
||||
import ChatOpenButton from "../chat-open-button/chat-open-button.component";
|
||||
import DataLabel from "../data-label/data-label.component";
|
||||
import OwnerNameDisplay from "../owner-name-display/owner-name-display.component";
|
||||
import ScheduleManualEvent from "../schedule-manual-event/schedule-manual-event.component";
|
||||
import ScheduleAtChange from "./job-at-change.component";
|
||||
import ScheduleEventColor from "./schedule-event.color.component";
|
||||
import ScheduleEventNote from "./schedule-event.note.component";
|
||||
import OwnerNameDisplay from "../owner-name-display/owner-name-display.component";
|
||||
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
bodyshop: selectBodyshop,
|
||||
@@ -208,46 +208,56 @@ export function ScheduleEventComponent({
|
||||
<Button>{t("appointments.actions.sendreminder")}</Button>
|
||||
</Dropdown>
|
||||
) : null}
|
||||
<Popover
|
||||
trigger="click"
|
||||
disabled={event.arrived}
|
||||
content={
|
||||
<Form
|
||||
layout="vertical"
|
||||
onFinish={({ lost_sale_reason }) => {
|
||||
handleCancel({ id: event.id, lost_sale_reason });
|
||||
}}
|
||||
>
|
||||
<Form.Item
|
||||
name="lost_sale_reason"
|
||||
label={t("jobs.fields.lost_sale_reason")}
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
//message: t("general.validation.required"),
|
||||
},
|
||||
]}
|
||||
>
|
||||
<Select
|
||||
options={bodyshop.md_lost_sale_reasons.map((lsr) => ({
|
||||
label: lsr,
|
||||
value: lsr,
|
||||
}))}
|
||||
/>
|
||||
</Form.Item>
|
||||
<Button htmlType="submit">
|
||||
{t("appointments.actions.cancel")}
|
||||
</Button>
|
||||
</Form>
|
||||
}
|
||||
>
|
||||
{event.arrived ? (
|
||||
<Button
|
||||
// onClick={() => handleCancel(event.id)}
|
||||
disabled={event.arrived}
|
||||
>
|
||||
{t("appointments.actions.cancel")}
|
||||
</Button>
|
||||
</Popover>
|
||||
) : (
|
||||
<Popover
|
||||
trigger="click"
|
||||
disabled={event.arrived}
|
||||
content={
|
||||
<Form
|
||||
layout="vertical"
|
||||
onFinish={({ lost_sale_reason }) => {
|
||||
handleCancel({ id: event.id, lost_sale_reason });
|
||||
}}
|
||||
>
|
||||
<Form.Item
|
||||
name="lost_sale_reason"
|
||||
label={t("jobs.fields.lost_sale_reason")}
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
//message: t("general.validation.required"),
|
||||
},
|
||||
]}
|
||||
>
|
||||
<Select
|
||||
options={bodyshop.md_lost_sale_reasons.map((lsr) => ({
|
||||
label: lsr,
|
||||
value: lsr,
|
||||
}))}
|
||||
/>
|
||||
</Form.Item>
|
||||
<Button htmlType="submit">
|
||||
{t("appointments.actions.cancel")}
|
||||
</Button>
|
||||
</Form>
|
||||
}
|
||||
>
|
||||
<Button
|
||||
// onClick={() => handleCancel(event.id)}
|
||||
disabled={event.arrived}
|
||||
>
|
||||
{t("appointments.actions.cancel")}
|
||||
</Button>
|
||||
</Popover>
|
||||
)}
|
||||
|
||||
{event.isintake ? (
|
||||
<Button
|
||||
disabled={event.arrived}
|
||||
|
||||
@@ -401,7 +401,7 @@ export function JobLinesComponent({
|
||||
const markedTypes = [e.key];
|
||||
if (e.key === "PAN") markedTypes.push("PAP");
|
||||
if (e.key === "PAS") markedTypes.push("PASL");
|
||||
setSelectedLines(
|
||||
setSelectedLines((selectedLines) =>
|
||||
_.uniq([
|
||||
...selectedLines,
|
||||
...jobLines.filter((item) => markedTypes.includes(item.part_type)),
|
||||
@@ -614,8 +614,17 @@ export function JobLinesComponent({
|
||||
onSelectAll: (selected, selectedRows, changeRows) => {
|
||||
setSelectedLines(selectedRows);
|
||||
},
|
||||
onSelect: (record, selected, selectedRows, nativeEvent) =>
|
||||
setSelectedLines(selectedRows),
|
||||
onSelect: (record, selected, selectedRows, nativeEvent) => {
|
||||
if (selected) {
|
||||
setSelectedLines((selectedLines) =>
|
||||
_.uniqBy([...selectedLines, record], "id")
|
||||
);
|
||||
} else {
|
||||
setSelectedLines((selectedLines) =>
|
||||
selectedLines.filter((l) => l.id !== record.id)
|
||||
);
|
||||
}
|
||||
},
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -1,19 +1,26 @@
|
||||
import { Button, Card, Space, Table } from "antd";
|
||||
import { EditFilled } from "@ant-design/icons";
|
||||
import { Button, Card, Space, Table } from "antd";
|
||||
import Dinero from "dinero.js";
|
||||
import React, { useMemo, useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { connect } from "react-redux";
|
||||
import { createStructuredSelector } from "reselect";
|
||||
import { selectJobReadOnly } from "../../redux/application/application.selectors";
|
||||
import {
|
||||
openChatByPhone,
|
||||
setMessage,
|
||||
} from "../../redux/messaging/messaging.actions";
|
||||
import { setModalContext } from "../../redux/modals/modals.actions";
|
||||
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||
import CurrencyFormatter from "../../utils/CurrencyFormatter";
|
||||
import { DateFormatter } from "../../utils/DateFormatter";
|
||||
import { alphaSort, dateSort } from "../../utils/sorters";
|
||||
import { TemplateList } from "../../utils/TemplateConstants";
|
||||
import { alphaSort, dateSort } from "../../utils/sorters";
|
||||
import DataLabel from "../data-label/data-label.component";
|
||||
import PaymentExpandedRowComponent from "../payment-expanded-row/payment-expanded-row.component";
|
||||
import PaymentsGenerateLink from "../payments-generate-link/payments-generate-link.component";
|
||||
import PrintWrapperComponent from "../print-wrapper/print-wrapper.component";
|
||||
import { useTreatments } from "@splitsoftware/splitio-react";
|
||||
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
bodyshop: selectBodyshop,
|
||||
@@ -23,20 +30,34 @@ const mapStateToProps = createStructuredSelector({
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
setPaymentContext: (context) =>
|
||||
dispatch(setModalContext({ context: context, modal: "payment" })),
|
||||
setCardPaymentContext: (context) =>
|
||||
dispatch(setModalContext({ context: context, modal: "cardPayment" })),
|
||||
openChatByPhone: (phone) => dispatch(openChatByPhone(phone)),
|
||||
setMessage: (text) => dispatch(setMessage(text)),
|
||||
});
|
||||
|
||||
export function JobPayments({
|
||||
job,
|
||||
jobRO,
|
||||
bodyshop,
|
||||
setMessage,
|
||||
openChatByPhone,
|
||||
setPaymentContext,
|
||||
setCardPaymentContext,
|
||||
refetch,
|
||||
}) {
|
||||
const { ImEXPay } = useTreatments(
|
||||
["ImEXPay"],
|
||||
{},
|
||||
bodyshop && bodyshop.imexshopid
|
||||
);
|
||||
|
||||
const { t } = useTranslation();
|
||||
const [state, setState] = useState({
|
||||
sortedInfo: {},
|
||||
filteredInfo: {},
|
||||
});
|
||||
|
||||
const columns = [
|
||||
{
|
||||
title: t("payments.fields.date"),
|
||||
@@ -149,6 +170,21 @@ export function JobPayments({
|
||||
title={t("payments.labels.title")}
|
||||
extra={
|
||||
<Space wrap>
|
||||
{ImEXPay.treatment === "on" && (
|
||||
<>
|
||||
<Button
|
||||
onClick={() =>
|
||||
setCardPaymentContext({
|
||||
actions: { refetch },
|
||||
context: { jobid: job.id, balance },
|
||||
})
|
||||
}
|
||||
>
|
||||
{t("menus.header.entercardpayment")}
|
||||
</Button>
|
||||
<PaymentsGenerateLink job={job} />
|
||||
</>
|
||||
)}
|
||||
<Button
|
||||
disabled={!job.converted}
|
||||
onClick={() =>
|
||||
@@ -160,6 +196,7 @@ export function JobPayments({
|
||||
>
|
||||
{t("menus.header.enterpayment")}
|
||||
</Button>
|
||||
|
||||
<DataLabel
|
||||
valueStyle={{ color: balance.getAmount() !== 0 ? "red" : "green" }}
|
||||
label={t("payments.labels.balance")}
|
||||
@@ -178,6 +215,11 @@ export function JobPayments({
|
||||
scroll={{
|
||||
x: true,
|
||||
}}
|
||||
expandable={{
|
||||
expandedRowRender: (record) => (
|
||||
<PaymentExpandedRowComponent record={record} bodyshop={bodyshop} />
|
||||
),
|
||||
}}
|
||||
summary={() => (
|
||||
<>
|
||||
<Table.Summary.Row>
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
import FormFieldsChanged from "../form-fields-changed-alert/form-fields-changed-alert.component";
|
||||
import { useMutation } from "@apollo/client";
|
||||
import { Button, Form, notification } from "antd";
|
||||
import moment from "moment";
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { UPDATE_JOB } from "../../graphql/jobs.queries";
|
||||
import DateTimePicker from "../form-date-time-picker/form-date-time-picker.component";
|
||||
import LayoutFormRow from "../layout-form-row/layout-form-row.component";
|
||||
import moment from "moment";
|
||||
import FormDatePicker from "../form-date-picker/form-date-picker.component";
|
||||
import AuditTrailMapping from "../../utils/AuditTrailMappings";
|
||||
import FormDatePicker from "../form-date-picker/form-date-picker.component";
|
||||
import DateTimePicker from "../form-date-time-picker/form-date-time-picker.component";
|
||||
import FormFieldsChanged from "../form-fields-changed-alert/form-fields-changed-alert.component";
|
||||
import LayoutFormRow from "../layout-form-row/layout-form-row.component";
|
||||
|
||||
import { connect } from "react-redux";
|
||||
import { createStructuredSelector } from "reselect";
|
||||
@@ -38,8 +38,8 @@ export function JobsAdminDatesChange({ insertAuditTrail, job }) {
|
||||
setLoading(true);
|
||||
const result = await updateJob({
|
||||
variables: { jobId: job.id, job: values },
|
||||
refetchQueries: ['GET_JOB_BY_PK'],
|
||||
awaitRefetchQueries:true
|
||||
refetchQueries: ["GET_JOB_BY_PK"],
|
||||
awaitRefetchQueries: true,
|
||||
});
|
||||
|
||||
const changedAuditFields = form.getFieldsValue(
|
||||
@@ -126,7 +126,10 @@ export function JobsAdminDatesChange({ insertAuditTrail, job }) {
|
||||
<Form.Item label={t("jobs.fields.actual_in")} name="actual_in">
|
||||
<DateTimePicker />
|
||||
</Form.Item>
|
||||
<Form.Item label={t("jobs.fields.date_repairstarted")} name="date_repairstarted">
|
||||
<Form.Item
|
||||
label={t("jobs.fields.date_repairstarted")}
|
||||
name="date_repairstarted"
|
||||
>
|
||||
<DateTimePicker />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
@@ -173,6 +176,9 @@ export function JobsAdminDatesChange({ insertAuditTrail, job }) {
|
||||
>
|
||||
<DateTimePicker />
|
||||
</Form.Item>
|
||||
<Form.Item label={t("jobs.fields.date_void")} name="date_void">
|
||||
<DateTimePicker />
|
||||
</Form.Item>
|
||||
</LayoutFormRow>
|
||||
</Form>
|
||||
|
||||
|
||||
@@ -1,19 +1,18 @@
|
||||
import { useMutation } from "@apollo/client";
|
||||
import { gql, useMutation } from "@apollo/client";
|
||||
import { Button, notification } from "antd";
|
||||
import { gql } from "@apollo/client";
|
||||
import React, { useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
import moment from "moment";
|
||||
import { connect } from "react-redux";
|
||||
import { createStructuredSelector } from "reselect";
|
||||
import { INSERT_EXPORT_LOG } from "../../graphql/accounting.queries";
|
||||
import { insertAuditTrail } from "../../redux/application/application.actions";
|
||||
import {
|
||||
selectBodyshop,
|
||||
selectCurrentUser,
|
||||
} from "../../redux/user/user.selectors";
|
||||
import moment from "moment";
|
||||
import AuditTrailMapping from "../../utils/AuditTrailMappings";
|
||||
import { insertAuditTrail } from "../../redux/application/application.actions";
|
||||
import { INSERT_EXPORT_LOG } from "../../graphql/accounting.queries";
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
bodyshop: selectBodyshop,
|
||||
currentUser: selectCurrentUser,
|
||||
@@ -150,6 +149,10 @@ export function JobAdminMarkReexport({
|
||||
|
||||
if (!result.errors) {
|
||||
notification["success"]({ message: t("jobs.successes.save") });
|
||||
insertAuditTrail({
|
||||
jobid: job.id,
|
||||
operation: AuditTrailMapping.admin_jobuninvoice(),
|
||||
});
|
||||
} else {
|
||||
notification["error"]({
|
||||
message: t("jobs.errors.saving", {
|
||||
|
||||
@@ -33,8 +33,9 @@ export function JobsAdminUnvoid({
|
||||
mutation UNVOID_JOB($jobId: uuid!) {
|
||||
update_jobs_by_pk(pk_columns: {id: $jobId}, _set: {voided: false, status: "${
|
||||
bodyshop.md_ro_statuses.default_imported
|
||||
}"}) {
|
||||
}", date_void: null}) {
|
||||
id
|
||||
date_void
|
||||
voided
|
||||
status
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@ import {
|
||||
useApolloClient,
|
||||
useLazyQuery,
|
||||
useMutation,
|
||||
useQuery
|
||||
useQuery,
|
||||
} from "@apollo/client";
|
||||
import { useTreatments } from "@splitsoftware/splitio-react";
|
||||
import { Col, notification, Row } from "antd";
|
||||
@@ -20,7 +20,7 @@ import { logImEXEvent } from "../../firebase/firebase.utils";
|
||||
import {
|
||||
DELETE_AVAILABLE_JOB,
|
||||
QUERY_AVAILABLE_JOBS,
|
||||
QUERY_AVAILABLE_NEW_JOBS_EST_DATA_BY_PK
|
||||
QUERY_AVAILABLE_NEW_JOBS_EST_DATA_BY_PK,
|
||||
} from "../../graphql/available-jobs.queries";
|
||||
import { INSERT_NEW_JOB, UPDATE_JOB } from "../../graphql/jobs.queries";
|
||||
import { INSERT_NEW_NOTE } from "../../graphql/notes.queries";
|
||||
@@ -28,7 +28,7 @@ import { SEARCH_VEHICLE_BY_VIN } from "../../graphql/vehicles.queries";
|
||||
import { insertAuditTrail } from "../../redux/application/application.actions";
|
||||
import {
|
||||
selectBodyshop,
|
||||
selectCurrentUser
|
||||
selectCurrentUser,
|
||||
} from "../../redux/user/user.selectors";
|
||||
import confirmDialog from "../../utils/asyncConfirm";
|
||||
import AuditTrailMapping from "../../utils/AuditTrailMappings";
|
||||
@@ -135,6 +135,7 @@ export function JobsAvailableContainer({
|
||||
owner_owing: Dinero(newTotals.totals.custPayable.total).toFormat("0.00"),
|
||||
job_totals: newTotals,
|
||||
date_open: moment(),
|
||||
status: bodyshop.md_ro_statuses.default_imported,
|
||||
notes: {
|
||||
data: {
|
||||
created_by: currentUser.email,
|
||||
|
||||
@@ -4,22 +4,35 @@ import axios from "axios";
|
||||
import React, { useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { connect } from "react-redux";
|
||||
import { useHistory } from "react-router-dom";
|
||||
import { createStructuredSelector } from "reselect";
|
||||
import { auth } from "../../firebase/firebase.utils";
|
||||
import { auth, logImEXEvent } from "../../firebase/firebase.utils";
|
||||
import { INSERT_EXPORT_LOG } from "../../graphql/accounting.queries";
|
||||
import { UPDATE_JOB } from "../../graphql/jobs.queries";
|
||||
import {
|
||||
selectBodyshop,
|
||||
selectCurrentUser,
|
||||
} from "../../redux/user/user.selectors";
|
||||
import { logImEXEvent } from "../../firebase/firebase.utils";
|
||||
import { INSERT_EXPORT_LOG } from "../../graphql/accounting.queries";
|
||||
import { useHistory } from "react-router-dom";
|
||||
import client from "../../utils/GraphQLClient";
|
||||
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
bodyshop: selectBodyshop,
|
||||
currentUser: selectCurrentUser,
|
||||
});
|
||||
|
||||
function updateJobCache(items) {
|
||||
client.cache.modify({
|
||||
id: "ROOT_QUERY",
|
||||
fields: {
|
||||
jobs(existingJobs = []) {
|
||||
return existingJobs.filter(
|
||||
(jobRef) => jobRef.__ref.includes(items) === false
|
||||
);
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export function JobsCloseExportButton({
|
||||
bodyshop,
|
||||
currentUser,
|
||||
@@ -101,6 +114,9 @@ export function JobsCloseExportButton({
|
||||
|
||||
//Check to see if any of them failed. If they didn't don't execute the update.
|
||||
const failedTransactions = PartnerResponse.data.filter((r) => !r.success);
|
||||
const successfulTransactions = PartnerResponse.data.filter(
|
||||
(r) => r.success
|
||||
);
|
||||
if (failedTransactions.length > 0) {
|
||||
//Uh oh. At least one was no good.
|
||||
failedTransactions.forEach((ft) => {
|
||||
@@ -159,12 +175,15 @@ export function JobsCloseExportButton({
|
||||
},
|
||||
});
|
||||
|
||||
if (!jobUpdateResponse.errors) {
|
||||
if (!!!jobUpdateResponse.errors) {
|
||||
notification.open({
|
||||
type: "success",
|
||||
key: "jobsuccessexport",
|
||||
message: t("jobs.successes.exported"),
|
||||
});
|
||||
updateJobCache(
|
||||
jobUpdateResponse.data.update_jobs.returning.map((job) => job.id)
|
||||
);
|
||||
} else {
|
||||
notification["error"]({
|
||||
message: t("jobs.errors.exporting", {
|
||||
@@ -173,13 +192,31 @@ export function JobsCloseExportButton({
|
||||
});
|
||||
}
|
||||
}
|
||||
if (bodyshop.accountingconfig && bodyshop.accountingconfig.qbo && successfulTransactions.length > 0) {
|
||||
notification.open({
|
||||
type: "success",
|
||||
key: "jobsuccessexport",
|
||||
message: t("jobs.successes.exported"),
|
||||
});
|
||||
updateJobCache([
|
||||
...new Set(
|
||||
successfulTransactions.map(
|
||||
(st) =>
|
||||
st[
|
||||
bodyshop.accountingconfig && bodyshop.accountingconfig.qbo
|
||||
? "jobid"
|
||||
: "id"
|
||||
]
|
||||
)
|
||||
),
|
||||
]);
|
||||
}
|
||||
if (setSelectedJobs) {
|
||||
setSelectedJobs((selectedJobs) => {
|
||||
return selectedJobs.filter((i) => i !== jobId);
|
||||
});
|
||||
}
|
||||
}
|
||||
if (bodyshop.accountingconfig && bodyshop.accountingconfig.qbo) refetch();
|
||||
setLoading(false);
|
||||
};
|
||||
|
||||
|
||||
@@ -141,6 +141,10 @@ export function JobsDetailDatesComponent({ jobRO, job, bodyshop }) {
|
||||
<Form.Item label={t("jobs.fields.date_exported")} name="date_exported">
|
||||
<DateTimePicker disabled={true || jobRO} />
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item label={t("jobs.fields.date_void")} name="date_void">
|
||||
<DateTimePicker disabled={true || jobRO} />
|
||||
</Form.Item>
|
||||
</FormRow>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -5,10 +5,10 @@ import {
|
||||
Dropdown,
|
||||
Form,
|
||||
Menu,
|
||||
notification,
|
||||
Popconfirm,
|
||||
Popover,
|
||||
Select,
|
||||
notification,
|
||||
} from "antd";
|
||||
import React, { useMemo } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
@@ -24,12 +24,12 @@ import {
|
||||
selectBodyshop,
|
||||
selectCurrentUser,
|
||||
} from "../../redux/user/user.selectors";
|
||||
import RbacWrapper from "../rbac-wrapper/rbac-wrapper.component";
|
||||
import JobsDetailHeaderActionsAddevent from "./jobs-detail-header-actions.addevent";
|
||||
import AddToProduction from "./jobs-detail-header-actions.addtoproduction.util";
|
||||
import JobsDetaiLheaderCsi from "./jobs-detail-header-actions.csi.component";
|
||||
import DuplicateJob from "./jobs-detail-header-actions.duplicate.util";
|
||||
import JobsDetailHeaderActionsExportcustdataComponent from "./jobs-detail-header-actions.exportcustdata.component";
|
||||
import RbacWrapper from "../rbac-wrapper/rbac-wrapper.component";
|
||||
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
bodyshop: selectBodyshop,
|
||||
@@ -48,6 +48,8 @@ const mapDispatchToProps = (dispatch) => ({
|
||||
dispatch(setModalContext({ context: context, modal: "jobCosting" })),
|
||||
setTimeTicketContext: (context) =>
|
||||
dispatch(setModalContext({ context: context, modal: "timeTicket" })),
|
||||
setCardPaymentContext: (context) =>
|
||||
dispatch(setModalContext({ context: context, modal: "cardPayment" })),
|
||||
});
|
||||
|
||||
export function JobsDetailHeaderActions({
|
||||
@@ -61,6 +63,7 @@ export function JobsDetailHeaderActions({
|
||||
setJobCostingContext,
|
||||
jobRO,
|
||||
setTimeTicketContext,
|
||||
setCardPaymentContext,
|
||||
}) {
|
||||
const { t } = useTranslation();
|
||||
const client = useApolloClient();
|
||||
@@ -137,63 +140,67 @@ export function JobsDetailHeaderActions({
|
||||
<Menu.Item
|
||||
disabled={job.status !== bodyshop.md_ro_statuses.default_scheduled}
|
||||
>
|
||||
<Popover
|
||||
trigger="click"
|
||||
disabled={job.status !== bodyshop.md_ro_statuses.default_scheduled}
|
||||
content={
|
||||
<Form
|
||||
layout="vertical"
|
||||
onFinish={async ({ lost_sale_reason }) => {
|
||||
const jobUpdate = await cancelAllAppointments({
|
||||
variables: {
|
||||
jobid: job.id,
|
||||
job: {
|
||||
date_scheduled: null,
|
||||
scheduled_in: null,
|
||||
scheduled_completion: null,
|
||||
lost_sale_reason,
|
||||
status: bodyshop.md_ro_statuses.default_imported,
|
||||
{job.status !== bodyshop.md_ro_statuses.default_scheduled ? (
|
||||
t("menus.jobsactions.cancelallappointments")
|
||||
) : (
|
||||
<Popover
|
||||
trigger="click"
|
||||
disabled={job.status !== bodyshop.md_ro_statuses.default_scheduled}
|
||||
content={
|
||||
<Form
|
||||
layout="vertical"
|
||||
onFinish={async ({ lost_sale_reason }) => {
|
||||
const jobUpdate = await cancelAllAppointments({
|
||||
variables: {
|
||||
jobid: job.id,
|
||||
job: {
|
||||
date_scheduled: null,
|
||||
scheduled_in: null,
|
||||
scheduled_completion: null,
|
||||
lost_sale_reason,
|
||||
status: bodyshop.md_ro_statuses.default_imported,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
if (!jobUpdate.errors) {
|
||||
notification["success"]({
|
||||
message: t("appointments.successes.canceled"),
|
||||
});
|
||||
return;
|
||||
}
|
||||
}}
|
||||
>
|
||||
<Form.Item
|
||||
name="lost_sale_reason"
|
||||
label={t("jobs.fields.lost_sale_reason")}
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
//message: t("general.validation.required"),
|
||||
},
|
||||
]}
|
||||
if (!jobUpdate.errors) {
|
||||
notification["success"]({
|
||||
message: t("appointments.successes.canceled"),
|
||||
});
|
||||
return;
|
||||
}
|
||||
}}
|
||||
>
|
||||
<Select
|
||||
options={bodyshop.md_lost_sale_reasons.map((lsr) => ({
|
||||
label: lsr,
|
||||
value: lsr,
|
||||
}))}
|
||||
/>
|
||||
</Form.Item>
|
||||
<Button
|
||||
htmlType="submit"
|
||||
disabled={
|
||||
job.status !== bodyshop.md_ro_statuses.default_scheduled
|
||||
}
|
||||
>
|
||||
{t("appointments.actions.cancel")}
|
||||
</Button>
|
||||
</Form>
|
||||
}
|
||||
>
|
||||
{t("menus.jobsactions.cancelallappointments")}
|
||||
</Popover>
|
||||
<Form.Item
|
||||
name="lost_sale_reason"
|
||||
label={t("jobs.fields.lost_sale_reason")}
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
//message: t("general.validation.required"),
|
||||
},
|
||||
]}
|
||||
>
|
||||
<Select
|
||||
options={bodyshop.md_lost_sale_reasons.map((lsr) => ({
|
||||
label: lsr,
|
||||
value: lsr,
|
||||
}))}
|
||||
/>
|
||||
</Form.Item>
|
||||
<Button
|
||||
htmlType="submit"
|
||||
disabled={
|
||||
job.status !== bodyshop.md_ro_statuses.default_scheduled
|
||||
}
|
||||
>
|
||||
{t("appointments.actions.cancel")}
|
||||
</Button>
|
||||
</Form>
|
||||
}
|
||||
>
|
||||
{t("menus.jobsactions.cancelallappointments")}
|
||||
</Popover>
|
||||
)}
|
||||
</Menu.Item>
|
||||
<Menu.Item
|
||||
disabled={
|
||||
@@ -239,7 +246,12 @@ export function JobsDetailHeaderActions({
|
||||
|
||||
setTimeTicketContext({
|
||||
actions: {},
|
||||
context: { jobId: job.id },
|
||||
context: {
|
||||
jobId: job.id,
|
||||
created_by: currentUser.displayName
|
||||
? currentUser.email.concat(" | ", currentUser.displayName)
|
||||
: currentUser.email,
|
||||
},
|
||||
});
|
||||
}}
|
||||
>
|
||||
@@ -259,6 +271,18 @@ export function JobsDetailHeaderActions({
|
||||
>
|
||||
{t("menus.header.enterpayment")}
|
||||
</Menu.Item>
|
||||
<Menu.Item
|
||||
key="entercardpayments"
|
||||
disabled={!job.converted}
|
||||
onClick={() => {
|
||||
setCardPaymentContext({
|
||||
actions: {},
|
||||
context: { jobid: job.id },
|
||||
});
|
||||
}}
|
||||
>
|
||||
{t("menus.header.entercardpayment")}
|
||||
</Menu.Item>
|
||||
<Menu.Item key="cccontract" disabled={jobRO || !job.converted}>
|
||||
<Link
|
||||
to={{
|
||||
@@ -480,6 +504,7 @@ export function JobsDetailHeaderActions({
|
||||
scheduled_in: null,
|
||||
scheduled_completion: null,
|
||||
inproduction: false,
|
||||
date_void: new Date(),
|
||||
},
|
||||
note: [
|
||||
{
|
||||
|
||||
@@ -13,12 +13,26 @@ import {
|
||||
selectBodyshop,
|
||||
selectCurrentUser,
|
||||
} from "../../redux/user/user.selectors";
|
||||
import client from "../../utils/GraphQLClient";
|
||||
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
bodyshop: selectBodyshop,
|
||||
currentUser: selectCurrentUser,
|
||||
});
|
||||
|
||||
function updateJobCache(items) {
|
||||
client.cache.modify({
|
||||
id: "ROOT_QUERY",
|
||||
fields: {
|
||||
jobs(existingJobs = []) {
|
||||
return existingJobs.filter(
|
||||
(jobRef) => jobRef.__ref.includes(items) === false
|
||||
);
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export function JobsExportAllButton({
|
||||
bodyshop,
|
||||
currentUser,
|
||||
@@ -96,7 +110,9 @@ export function JobsExportAllButton({
|
||||
Object.keys(groupedData).map(async (key) => {
|
||||
//Check to see if any of them failed. If they didn't don't execute the update.
|
||||
const failedTransactions = groupedData[key].filter((r) => !r.success);
|
||||
|
||||
const successfulTransactions = groupedData[key].filter(
|
||||
(r) => r.success
|
||||
);
|
||||
if (failedTransactions.length > 0) {
|
||||
//Uh oh. At least one was no good.
|
||||
failedTransactions.forEach((ft) => {
|
||||
@@ -155,12 +171,17 @@ export function JobsExportAllButton({
|
||||
},
|
||||
});
|
||||
|
||||
if (!jobUpdateResponse.errors) {
|
||||
if (!!!jobUpdateResponse.errors) {
|
||||
notification.open({
|
||||
type: "success",
|
||||
key: "jobsuccessexport",
|
||||
message: t("jobs.successes.exported"),
|
||||
});
|
||||
updateJobCache(
|
||||
jobUpdateResponse.data.update_jobs.returning.map(
|
||||
(job) => job.id
|
||||
)
|
||||
);
|
||||
} else {
|
||||
notification["error"]({
|
||||
message: t("jobs.errors.exporting", {
|
||||
@@ -169,14 +190,31 @@ export function JobsExportAllButton({
|
||||
});
|
||||
}
|
||||
}
|
||||
if (bodyshop.accountingconfig && bodyshop.accountingconfig.qbo && successfulTransactions.length > 0) {
|
||||
notification.open({
|
||||
type: "success",
|
||||
key: "jobsuccessexport",
|
||||
message: t("jobs.successes.exported"),
|
||||
});
|
||||
updateJobCache([
|
||||
...new Set(
|
||||
successfulTransactions.map(
|
||||
(st) =>
|
||||
st[
|
||||
bodyshop.accountingconfig && bodyshop.accountingconfig.qbo
|
||||
? "jobid"
|
||||
: "id"
|
||||
]
|
||||
)
|
||||
),
|
||||
]);
|
||||
}
|
||||
}
|
||||
})
|
||||
);
|
||||
if (bodyshop.accountingconfig && bodyshop.accountingconfig.qbo) refetch();
|
||||
|
||||
if (!!completedCallback) completedCallback([]);
|
||||
if (!!loadingCallback) loadingCallback(false);
|
||||
|
||||
setLoading(false);
|
||||
};
|
||||
|
||||
|
||||
@@ -231,7 +231,14 @@ export function LaborAllocationsTable({
|
||||
{summary.adjustments.toFixed(1)}
|
||||
</Table.Summary.Cell>
|
||||
<Table.Summary.Cell>
|
||||
{summary.difference.toFixed(1)}
|
||||
<Typography.Text
|
||||
style={{
|
||||
fontWeight: "bold",
|
||||
color: summary.difference >= 0 ? "green" : "red",
|
||||
}}
|
||||
>
|
||||
{summary.difference.toFixed(1)}
|
||||
</Typography.Text>
|
||||
</Table.Summary.Cell>
|
||||
</Table.Summary.Row>
|
||||
)}
|
||||
|
||||
@@ -6,6 +6,7 @@ import { Link } from "react-router-dom";
|
||||
import { createStructuredSelector } from "reselect";
|
||||
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||
import CurrencyFormatter from "../../utils/CurrencyFormatter";
|
||||
import { alphaSort, statusSort } from "../../utils/sorters";
|
||||
import OwnerDetailUpdateJobsComponent from "../owner-detail-update-jobs/owner-detail-update-jobs.component";
|
||||
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
@@ -15,6 +16,15 @@ const mapStateToProps = createStructuredSelector({
|
||||
function OwnerDetailJobsComponent({ bodyshop, owner }) {
|
||||
const { t } = useTranslation();
|
||||
const [selectedJobs, setSelectedJobs] = useState([]);
|
||||
const [state, setState] = useState({
|
||||
sortedInfo: {},
|
||||
filteredInfo: { text: "" },
|
||||
});
|
||||
|
||||
const handleTableChange = (pagination, filters, sorter) => {
|
||||
setState({ ...state, filteredInfo: filters, sortedInfo: sorter });
|
||||
};
|
||||
|
||||
const columns = [
|
||||
{
|
||||
title: t("jobs.fields.ro_number"),
|
||||
@@ -26,6 +36,9 @@ function OwnerDetailJobsComponent({ bodyshop, owner }) {
|
||||
{record.ro_number || t("general.labels.na")}
|
||||
</Link>
|
||||
),
|
||||
sorter: (a, b) => alphaSort(a.ro_number, b.ro_number),
|
||||
sortOrder:
|
||||
state.sortedInfo.columnKey === "ro_number" && state.sortedInfo.order,
|
||||
},
|
||||
{
|
||||
title: t("jobs.fields.vehicle"),
|
||||
@@ -46,11 +59,17 @@ function OwnerDetailJobsComponent({ bodyshop, owner }) {
|
||||
title: t("jobs.fields.clm_no"),
|
||||
dataIndex: "clm_no",
|
||||
key: "clm_no",
|
||||
sorter: (a, b) => alphaSort(a.clm_no, b.clm_no),
|
||||
sortOrder:
|
||||
state.sortedInfo.columnKey === "clm_no" && state.sortedInfo.order,
|
||||
},
|
||||
{
|
||||
title: t("jobs.fields.status"),
|
||||
dataIndex: "status",
|
||||
key: "status",
|
||||
sorter: (a, b) => statusSort(a.status, b.status, bodyshop.md_ro_statuses.statuses),
|
||||
sortOrder:
|
||||
state.sortedInfo.columnKey === "status" && state.sortedInfo.order,
|
||||
},
|
||||
|
||||
{
|
||||
@@ -60,6 +79,9 @@ function OwnerDetailJobsComponent({ bodyshop, owner }) {
|
||||
render: (text, record) => (
|
||||
<CurrencyFormatter>{record.clm_total}</CurrencyFormatter>
|
||||
),
|
||||
sorter: (a, b) => a.clm_total - b.clm_total,
|
||||
sortOrder:
|
||||
state.sortedInfo.columnKey === "clm_total" && state.sortedInfo.order,
|
||||
},
|
||||
];
|
||||
|
||||
@@ -80,6 +102,7 @@ function OwnerDetailJobsComponent({ bodyshop, owner }) {
|
||||
scroll={{ x: true }}
|
||||
rowKey="id"
|
||||
dataSource={owner.jobs}
|
||||
onChange={handleTableChange}
|
||||
rowSelection={{
|
||||
onSelect: (record, selected, selectedRows) => {
|
||||
setSelectedJobs(selectedRows ? selectedRows.map((i) => i.id) : []);
|
||||
|
||||
@@ -113,6 +113,8 @@ export function PartsOrderListTableComponent({
|
||||
id: pol.id,
|
||||
line_desc: pol.line_desc,
|
||||
quantity: pol.quantity,
|
||||
act_price: pol.act_price,
|
||||
oem_partno: pol.oem_partno,
|
||||
};
|
||||
}),
|
||||
},
|
||||
|
||||
@@ -79,6 +79,20 @@ export function PartsReceiveModalComponent({ bodyshop, form }) {
|
||||
>
|
||||
<Input />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("joblines.fields.oem_partno")}
|
||||
key={`${index}oem_partno`}
|
||||
name={[field.name, "oem_partno"]}
|
||||
>
|
||||
<Input disabled />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("joblines.fields.act_price")}
|
||||
key={`${index}act_price`}
|
||||
name={[field.name, "act_price"]}
|
||||
>
|
||||
<Input disabled />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("joblines.fields.location")}
|
||||
key={`${index}location`}
|
||||
|
||||
@@ -1,26 +1,39 @@
|
||||
import { useMutation } from "@apollo/client";
|
||||
import { Button, notification } from "antd";
|
||||
import axios from "axios";
|
||||
import _ from "lodash";
|
||||
import React, { useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { connect } from "react-redux";
|
||||
import { Link } from "react-router-dom";
|
||||
import { createStructuredSelector } from "reselect";
|
||||
import { auth } from "../../firebase/firebase.utils";
|
||||
import { auth, logImEXEvent } from "../../firebase/firebase.utils";
|
||||
import { INSERT_EXPORT_LOG } from "../../graphql/accounting.queries";
|
||||
import { UPDATE_BILLS } from "../../graphql/bills.queries";
|
||||
import {
|
||||
selectBodyshop,
|
||||
selectCurrentUser,
|
||||
} from "../../redux/user/user.selectors";
|
||||
import { logImEXEvent } from "../../firebase/firebase.utils";
|
||||
import _ from "lodash";
|
||||
import { INSERT_EXPORT_LOG } from "../../graphql/accounting.queries";
|
||||
import { Link } from "react-router-dom";
|
||||
import client from "../../utils/GraphQLClient";
|
||||
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
bodyshop: selectBodyshop,
|
||||
currentUser: selectCurrentUser,
|
||||
});
|
||||
|
||||
function updateBillCache(items) {
|
||||
client.cache.modify({
|
||||
id: "ROOT_QUERY",
|
||||
fields: {
|
||||
bills(existingJobs = []) {
|
||||
return existingJobs.filter(
|
||||
(billRef) => billRef.__ref.includes(items) === false
|
||||
);
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export function PayableExportAll({
|
||||
bodyshop,
|
||||
currentUser,
|
||||
@@ -97,7 +110,9 @@ export function PayableExportAll({
|
||||
proms.push(
|
||||
(async () => {
|
||||
const failedTransactions = groupedData[key].filter((r) => !r.success);
|
||||
|
||||
const successfulTransactions = groupedData[key].filter(
|
||||
(r) => r.success
|
||||
);
|
||||
if (failedTransactions.length > 0) {
|
||||
//Uh oh. At least one was no good.
|
||||
failedTransactions.map((ft) =>
|
||||
@@ -143,7 +158,15 @@ export function PayableExportAll({
|
||||
|
||||
const billUpdateResponse = await updateBill({
|
||||
variables: {
|
||||
billIdList: [key],
|
||||
billIdList: successfulTransactions.map(
|
||||
(st) =>
|
||||
st[
|
||||
bodyshop.accountingconfig &&
|
||||
bodyshop.accountingconfig.qbo
|
||||
? "billid"
|
||||
: "id"
|
||||
]
|
||||
),
|
||||
bill: {
|
||||
exported: true,
|
||||
exported_at: new Date(),
|
||||
@@ -156,6 +179,11 @@ export function PayableExportAll({
|
||||
key: "billsuccessexport",
|
||||
message: t("bills.successes.exported"),
|
||||
});
|
||||
updateBillCache(
|
||||
billUpdateResponse.data.update_bills.returning.map(
|
||||
(bill) => bill.id
|
||||
)
|
||||
);
|
||||
} else {
|
||||
notification["error"]({
|
||||
message: t("bills.errors.exporting", {
|
||||
@@ -164,6 +192,26 @@ export function PayableExportAll({
|
||||
});
|
||||
}
|
||||
}
|
||||
if (bodyshop.accountingconfig && bodyshop.accountingconfig.qbo && successfulTransactions.length > 0) {
|
||||
notification.open({
|
||||
type: "success",
|
||||
key: "billsuccessexport",
|
||||
message: t("bills.successes.exported"),
|
||||
});
|
||||
updateBillCache([
|
||||
...new Set(
|
||||
successfulTransactions.map(
|
||||
(st) =>
|
||||
st[
|
||||
bodyshop.accountingconfig &&
|
||||
bodyshop.accountingconfig.qbo
|
||||
? "billid"
|
||||
: "id"
|
||||
]
|
||||
)
|
||||
),
|
||||
]);
|
||||
}
|
||||
}
|
||||
})()
|
||||
);
|
||||
@@ -172,8 +220,6 @@ export function PayableExportAll({
|
||||
await Promise.all(proms);
|
||||
if (!!completedCallback) completedCallback([]);
|
||||
if (!!loadingCallback) loadingCallback(false);
|
||||
if (bodyshop.accountingconfig && bodyshop.accountingconfig.qbo) refetch();
|
||||
|
||||
setLoading(false);
|
||||
};
|
||||
|
||||
|
||||
@@ -4,22 +4,35 @@ import axios from "axios";
|
||||
import React, { useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { connect } from "react-redux";
|
||||
import { Link } from "react-router-dom";
|
||||
import { createStructuredSelector } from "reselect";
|
||||
import { auth } from "../../firebase/firebase.utils";
|
||||
import { auth, logImEXEvent } from "../../firebase/firebase.utils";
|
||||
import { INSERT_EXPORT_LOG } from "../../graphql/accounting.queries";
|
||||
import { UPDATE_BILLS } from "../../graphql/bills.queries";
|
||||
import {
|
||||
selectBodyshop,
|
||||
selectCurrentUser,
|
||||
} from "../../redux/user/user.selectors";
|
||||
import { logImEXEvent } from "../../firebase/firebase.utils";
|
||||
import { INSERT_EXPORT_LOG } from "../../graphql/accounting.queries";
|
||||
import { Link } from "react-router-dom";
|
||||
import client from "../../utils/GraphQLClient";
|
||||
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
bodyshop: selectBodyshop,
|
||||
currentUser: selectCurrentUser,
|
||||
});
|
||||
|
||||
function updateBillCache(items) {
|
||||
client.cache.modify({
|
||||
id: "ROOT_QUERY",
|
||||
fields: {
|
||||
bills(existingJobs = []) {
|
||||
return existingJobs.filter(
|
||||
(billRef) => billRef.__ref.includes(items) === false
|
||||
);
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export function PayableExportButton({
|
||||
bodyshop,
|
||||
currentUser,
|
||||
@@ -159,6 +172,11 @@ export function PayableExportButton({
|
||||
key: "billsuccessexport",
|
||||
message: t("bills.successes.exported"),
|
||||
});
|
||||
updateBillCache(
|
||||
billUpdateResponse.data.update_bills.returning.map(
|
||||
(bill) => bill.id
|
||||
)
|
||||
);
|
||||
} else {
|
||||
notification["error"]({
|
||||
message: t("bills.errors.exporting", {
|
||||
@@ -167,7 +185,25 @@ export function PayableExportButton({
|
||||
});
|
||||
}
|
||||
}
|
||||
if (bodyshop.accountingconfig && bodyshop.accountingconfig.qbo) refetch();
|
||||
if (bodyshop.accountingconfig && bodyshop.accountingconfig.qbo && successfulTransactions.length > 0) {
|
||||
notification.open({
|
||||
type: "success",
|
||||
key: "billsuccessexport",
|
||||
message: t("bills.successes.exported"),
|
||||
});
|
||||
updateBillCache([
|
||||
...new Set(
|
||||
successfulTransactions.map(
|
||||
(st) =>
|
||||
st[
|
||||
bodyshop.accountingconfig && bodyshop.accountingconfig.qbo
|
||||
? "billid"
|
||||
: "id"
|
||||
]
|
||||
)
|
||||
),
|
||||
]);
|
||||
}
|
||||
|
||||
if (setSelectedBills) {
|
||||
setSelectedBills((selectedBills) => {
|
||||
|
||||
@@ -0,0 +1,186 @@
|
||||
import { useMutation, useQuery } from "@apollo/client";
|
||||
import {
|
||||
Button,
|
||||
Descriptions,
|
||||
InputNumber,
|
||||
Modal,
|
||||
Space,
|
||||
notification,
|
||||
} from "antd";
|
||||
import axios from "axios";
|
||||
import moment from "moment";
|
||||
import React, { useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import {
|
||||
GET_REFUNDABLE_AMOUNT_BY_JOBID,
|
||||
INSERT_PAYMENT_RESPONSE,
|
||||
QUERY_PAYMENT_RESPONSE_BY_PAYMENT_ID,
|
||||
} from "../../graphql/payment_response.queries";
|
||||
import { INSERT_NEW_PAYMENT } from "../../graphql/payments.queries";
|
||||
import CurrencyFormatter from "../../utils/CurrencyFormatter";
|
||||
import { DateTimeFormatter } from "../../utils/DateFormatter";
|
||||
|
||||
const { confirm } = Modal;
|
||||
|
||||
const openNotificationWithIcon = (type, t) => {
|
||||
notification[type]({
|
||||
message: t("job_payments.notifications.error.title"),
|
||||
description: t("job_payments.notifications.error.description"),
|
||||
});
|
||||
};
|
||||
|
||||
const PaymentExpandedRowComponent = ({ record, bodyshop }) => {
|
||||
const [refundAmount, setRefundAmount] = useState(0);
|
||||
const [insertPayment] = useMutation(INSERT_NEW_PAYMENT);
|
||||
const [insertPaymentResponse] = useMutation(INSERT_PAYMENT_RESPONSE);
|
||||
const { t } = useTranslation();
|
||||
|
||||
const { loading, error, data } = useQuery(
|
||||
QUERY_PAYMENT_RESPONSE_BY_PAYMENT_ID,
|
||||
{
|
||||
variables: {
|
||||
paymentid: record.id,
|
||||
fetchPolicy: "network-only",
|
||||
nextFetchPolicy: "network-only",
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
const { data: refundable_amount, refetch } = useQuery(
|
||||
GET_REFUNDABLE_AMOUNT_BY_JOBID,
|
||||
{
|
||||
variables: {
|
||||
jobid: record.jobid,
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
const insertPayments = async (payment_response, refund_response) => {
|
||||
await insertPayment({
|
||||
variables: {
|
||||
paymentInput: {
|
||||
amount: -refund_response.data.amount,
|
||||
transactionid: payment_response.response.receiptelements.transid,
|
||||
payer: record.payer,
|
||||
type: "Refund",
|
||||
jobid: payment_response.jobid,
|
||||
date: moment(Date.now()),
|
||||
},
|
||||
},
|
||||
update(cache, { data }) {
|
||||
cache.modify({
|
||||
id: cache.identify({
|
||||
id: payment_response.jobid,
|
||||
__typename: "jobs",
|
||||
}),
|
||||
fields: {
|
||||
payments(payments) {
|
||||
return [...data.insert_payments.returning, ...payments];
|
||||
},
|
||||
},
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
await insertPaymentResponse({
|
||||
variables: {
|
||||
paymentResponse: {
|
||||
amount: -refund_response.data.amount,
|
||||
bodyshopid: payment_response.bodyshopid,
|
||||
paymentid: payment_response.paymentid,
|
||||
jobid: payment_response.jobid,
|
||||
declinereason: "Refund",
|
||||
ext_paymentid: payment_response.ext_paymentid,
|
||||
successful: true,
|
||||
response: refund_response.data,
|
||||
},
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
const showConfirm = (payment_response) => {
|
||||
confirm({
|
||||
title: "Do you want to refund payment?",
|
||||
content:
|
||||
"The payment will be refunded. Click OK to confirm and Cancel to dismiss.",
|
||||
async onOk() {
|
||||
const refundResponse = await axios.post("/intellipay/payment_refund", {
|
||||
bodyshop,
|
||||
amount: refundAmount,
|
||||
paymentid: payment_response.ext_paymentid,
|
||||
});
|
||||
|
||||
if (refundResponse.data.status < 0) {
|
||||
openNotificationWithIcon("error", t);
|
||||
return;
|
||||
}
|
||||
|
||||
insertPayments(payment_response, refundResponse);
|
||||
|
||||
// refetch refundable amount
|
||||
refetch();
|
||||
},
|
||||
onCancel() {},
|
||||
});
|
||||
};
|
||||
|
||||
if (loading) return null;
|
||||
|
||||
if (error) return <p>Error loading data. Please Reload</p>;
|
||||
|
||||
const payment_response = data.payment_response[0];
|
||||
const max_refundable_amount =
|
||||
refundable_amount?.payment_response_aggregate.aggregate.sum.amount;
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Descriptions
|
||||
title={t("job_payments.titles.descriptions")}
|
||||
contentStyle={{ fontWeight: "600" }}
|
||||
column={4}
|
||||
>
|
||||
<Descriptions.Item label={t("job_payments.titles.payer")}>
|
||||
{record.payer}
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label={t("job_payments.titles.payername")}>
|
||||
{payment_response?.response?.nameOnCard ?? ""}
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label={t("job_payments.titles.amount")}>
|
||||
<CurrencyFormatter>{record.amount}</CurrencyFormatter>
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label={t("job_payments.titles.dateOfPayment")}>
|
||||
{<DateTimeFormatter>{record.created_at}</DateTimeFormatter>}
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label={t("job_payments.titles.transactionid")}>
|
||||
{record.transactionid}
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label={t("job_payments.titles.paymentid")}>
|
||||
{payment_response?.response?.paymentreferenceid ?? ""}
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label={t("job_payments.titles.paymenttype")}>
|
||||
{record.type}
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label={t("job_payments.titles.paymentnum")}>
|
||||
{record.paymentnum}
|
||||
</Descriptions.Item>
|
||||
{payment_response && (
|
||||
<Descriptions.Item label={t("job_payments.titles.refundamount")}>
|
||||
<Space>
|
||||
<InputNumber
|
||||
onChange={setRefundAmount}
|
||||
max={max_refundable_amount}
|
||||
min={0}
|
||||
/>
|
||||
|
||||
<Button onClick={() => showConfirm(payment_response)}>
|
||||
{t("job_payments.buttons.refundpayment")}
|
||||
</Button>
|
||||
</Space>
|
||||
</Descriptions.Item>
|
||||
)}
|
||||
</Descriptions>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default PaymentExpandedRowComponent;
|
||||
@@ -13,12 +13,26 @@ import {
|
||||
selectBodyshop,
|
||||
selectCurrentUser,
|
||||
} from "../../redux/user/user.selectors";
|
||||
import client from "../../utils/GraphQLClient";
|
||||
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
bodyshop: selectBodyshop,
|
||||
currentUser: selectCurrentUser,
|
||||
});
|
||||
|
||||
function updatePaymentCache(items) {
|
||||
client.cache.modify({
|
||||
id: "ROOT_QUERY",
|
||||
fields: {
|
||||
payments(existingJobs = []) {
|
||||
return existingJobs.filter(
|
||||
(paymentRef) => paymentRef.__ref.includes(items) === false
|
||||
);
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export function PaymentExportButton({
|
||||
bodyshop,
|
||||
currentUser,
|
||||
@@ -157,6 +171,11 @@ export function PaymentExportButton({
|
||||
key: "paymentsuccessexport",
|
||||
message: t("payments.successes.exported"),
|
||||
});
|
||||
updatePaymentCache(
|
||||
paymentUpdateResponse.data.update_payments.returning.map(
|
||||
(payment) => payment.id
|
||||
)
|
||||
);
|
||||
} else {
|
||||
notification["error"]({
|
||||
message: t("payments.errors.exporting", {
|
||||
@@ -172,7 +191,25 @@ export function PaymentExportButton({
|
||||
});
|
||||
}
|
||||
}
|
||||
if (bodyshop.accountingconfig && bodyshop.accountingconfig.qbo) refetch();
|
||||
if (bodyshop.accountingconfig && bodyshop.accountingconfig.qbo && successfulTransactions.length > 0) {
|
||||
notification.open({
|
||||
type: "success",
|
||||
key: "paymentsuccessexport",
|
||||
message: t("payments.successes.exported"),
|
||||
});
|
||||
updatePaymentCache([
|
||||
...new Set(
|
||||
successfulTransactions.map(
|
||||
(st) =>
|
||||
st[
|
||||
bodyshop.accountingconfig && bodyshop.accountingconfig.qbo
|
||||
? "paymentid"
|
||||
: "id"
|
||||
]
|
||||
)
|
||||
),
|
||||
]);
|
||||
}
|
||||
if (!!loadingCallback) loadingCallback(false);
|
||||
setLoading(false);
|
||||
};
|
||||
|
||||
@@ -13,11 +13,25 @@ import {
|
||||
selectBodyshop,
|
||||
selectCurrentUser,
|
||||
} from "../../redux/user/user.selectors";
|
||||
import client from "../../utils/GraphQLClient";
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
bodyshop: selectBodyshop,
|
||||
currentUser: selectCurrentUser,
|
||||
});
|
||||
|
||||
function updatePaymentCache(items) {
|
||||
client.cache.modify({
|
||||
id: "ROOT_QUERY",
|
||||
fields: {
|
||||
payments(existingJobs = []) {
|
||||
return existingJobs.filter(
|
||||
(paymentRef) => paymentRef.__ref.includes(items) === false
|
||||
);
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export function PaymentsExportAllButton({
|
||||
bodyshop,
|
||||
currentUser,
|
||||
@@ -25,7 +39,7 @@ export function PaymentsExportAllButton({
|
||||
disabled,
|
||||
loadingCallback,
|
||||
completedCallback,
|
||||
refetch
|
||||
refetch,
|
||||
}) {
|
||||
const { t } = useTranslation();
|
||||
const [updatePayments] = useMutation(UPDATE_PAYMENTS);
|
||||
@@ -84,7 +98,9 @@ export function PaymentsExportAllButton({
|
||||
proms.push(
|
||||
(async () => {
|
||||
const failedTransactions = groupedData[key].filter((r) => !r.success);
|
||||
|
||||
const successfulTransactions = groupedData[key].filter(
|
||||
(r) => r.success
|
||||
);
|
||||
if (failedTransactions.length > 0) {
|
||||
//Uh oh. At least one was no good.
|
||||
failedTransactions.map((ft) =>
|
||||
@@ -130,7 +146,15 @@ export function PaymentsExportAllButton({
|
||||
});
|
||||
const paymentUpdateResponse = await updatePayments({
|
||||
variables: {
|
||||
paymentIdList: [key],
|
||||
paymentIdList: successfulTransactions.map(
|
||||
(st) =>
|
||||
st[
|
||||
bodyshop.accountingconfig &&
|
||||
bodyshop.accountingconfig.qbo
|
||||
? "paymentid"
|
||||
: "id"
|
||||
]
|
||||
),
|
||||
payment: {
|
||||
exportedat: new Date(),
|
||||
},
|
||||
@@ -142,6 +166,11 @@ export function PaymentsExportAllButton({
|
||||
key: "paymentsuccessexport",
|
||||
message: t("payments.successes.exported"),
|
||||
});
|
||||
updatePaymentCache(
|
||||
paymentUpdateResponse.data.update_payments.returning.map(
|
||||
(payment) => payment.id
|
||||
)
|
||||
);
|
||||
} else {
|
||||
notification["error"]({
|
||||
message: t("payments.errors.exporting", {
|
||||
@@ -150,6 +179,26 @@ export function PaymentsExportAllButton({
|
||||
});
|
||||
}
|
||||
}
|
||||
if (bodyshop.accountingconfig && bodyshop.accountingconfig.qbo && successfulTransactions.length > 0) {
|
||||
notification.open({
|
||||
type: "success",
|
||||
key: "paymentsuccessexport",
|
||||
message: t("payments.successes.exported"),
|
||||
});
|
||||
updatePaymentCache([
|
||||
...new Set(
|
||||
successfulTransactions.map(
|
||||
(st) =>
|
||||
st[
|
||||
bodyshop.accountingconfig &&
|
||||
bodyshop.accountingconfig.qbo
|
||||
? "paymentid"
|
||||
: "id"
|
||||
]
|
||||
)
|
||||
),
|
||||
]);
|
||||
}
|
||||
}
|
||||
})()
|
||||
);
|
||||
@@ -157,7 +206,6 @@ export function PaymentsExportAllButton({
|
||||
await Promise.all(proms);
|
||||
if (!!completedCallback) completedCallback([]);
|
||||
if (!!loadingCallback) loadingCallback(false);
|
||||
if (bodyshop.accountingconfig && bodyshop.accountingconfig.qbo) refetch();
|
||||
setLoading(false);
|
||||
};
|
||||
|
||||
|
||||
@@ -0,0 +1,156 @@
|
||||
import { CopyFilled } from "@ant-design/icons";
|
||||
import { Button, Form, Popover, Space, message } from "antd";
|
||||
import axios from "axios";
|
||||
import Dinero from "dinero.js";
|
||||
import { parsePhoneNumber } from "libphonenumber-js";
|
||||
import React, { useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { connect } from "react-redux";
|
||||
import { createStructuredSelector } from "reselect";
|
||||
import {
|
||||
openChatByPhone,
|
||||
setMessage,
|
||||
} from "../../redux/messaging/messaging.actions";
|
||||
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||
import CurrencyFormItemComponent from "../form-items-formatted/currency-form-item.component";
|
||||
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
bodyshop: selectBodyshop,
|
||||
});
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
openChatByPhone: (phone) => dispatch(openChatByPhone(phone)),
|
||||
setMessage: (text) => dispatch(setMessage(text)),
|
||||
});
|
||||
export default connect(
|
||||
mapStateToProps,
|
||||
mapDispatchToProps
|
||||
)(PaymentsGenerateLink);
|
||||
|
||||
export function PaymentsGenerateLink({
|
||||
bodyshop,
|
||||
callback,
|
||||
job,
|
||||
openChatByPhone,
|
||||
setMessage,
|
||||
}) {
|
||||
const { t } = useTranslation();
|
||||
const [form] = Form.useForm();
|
||||
|
||||
const [visible, setVisible] = useState(false);
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [paymentLink, setPaymentLink] = useState(null);
|
||||
|
||||
const handleFinish = async ({ amount }) => {
|
||||
setLoading(true);
|
||||
|
||||
const p = parsePhoneNumber(job.ownr_ph1, "CA");
|
||||
setLoading(true);
|
||||
const response = await axios.post("/intellipay/generate_payment_url", {
|
||||
bodyshop,
|
||||
amount: amount,
|
||||
account: job.ro_number,
|
||||
invoice: job.id,
|
||||
});
|
||||
setLoading(false);
|
||||
setPaymentLink(response.data.shorUrl);
|
||||
|
||||
openChatByPhone({
|
||||
phone_num: p.formatInternational(),
|
||||
jobid: job.id,
|
||||
});
|
||||
setMessage(
|
||||
t("payments.labels.smspaymentreminder", {
|
||||
shopname: bodyshop.shopname,
|
||||
amount: amount,
|
||||
payment_link: response.data.shorUrl,
|
||||
})
|
||||
);
|
||||
|
||||
//Add in confirmation & errors.
|
||||
if (callback) callback();
|
||||
|
||||
// setVisible(false);
|
||||
setLoading(false);
|
||||
};
|
||||
|
||||
const popContent = (
|
||||
<div>
|
||||
<Form onFinish={handleFinish} layout="vertical" form={form}>
|
||||
<Form.Item
|
||||
label={t("payments.fields.amount")}
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
//message: t("general.validation.required"),
|
||||
},
|
||||
]}
|
||||
name="amount"
|
||||
>
|
||||
<CurrencyFormItemComponent />
|
||||
</Form.Item>
|
||||
{paymentLink && (
|
||||
<Space direction="vertical">
|
||||
<Space
|
||||
style={{ cursor: "pointer" }}
|
||||
onClick={() => {
|
||||
navigator.clipboard.writeText(paymentLink);
|
||||
message.success(t("general.actions.copied"));
|
||||
}}
|
||||
>
|
||||
<div
|
||||
onClick={() => {
|
||||
//Copy the link.
|
||||
}}
|
||||
>
|
||||
{paymentLink}
|
||||
</div>{" "}
|
||||
<CopyFilled />
|
||||
</Space>
|
||||
<Button
|
||||
onClick={() => {
|
||||
const p = parsePhoneNumber(job.ownr_ph1, "CA");
|
||||
openChatByPhone({
|
||||
phone_num: p.formatInternational(),
|
||||
jobid: job.id,
|
||||
});
|
||||
setMessage(
|
||||
t("payments.labels.smspaymentreminder", {
|
||||
shopname: bodyshop.shopname,
|
||||
amount: Dinero({
|
||||
amount: Math.round(form.getFieldValue("amount") * 100),
|
||||
}).toFormat(),
|
||||
payment_link: paymentLink,
|
||||
})
|
||||
);
|
||||
}}
|
||||
>
|
||||
{t("general.actions.sendbysms")}
|
||||
</Button>
|
||||
</Space>
|
||||
)}
|
||||
</Form>
|
||||
<Space>
|
||||
<Button type="primary" onClick={() => form.submit()}>
|
||||
{t("general.actions.submit")}
|
||||
</Button>
|
||||
<Button
|
||||
onClick={() => {
|
||||
form.resetFields();
|
||||
setPaymentLink(null);
|
||||
setVisible(false);
|
||||
}}
|
||||
>
|
||||
{t("general.actions.cancel")}
|
||||
</Button>
|
||||
</Space>
|
||||
</div>
|
||||
);
|
||||
|
||||
return (
|
||||
<Popover content={popContent} visible={visible}>
|
||||
<Button onClick={() => setVisible(true)} loading={loading}>
|
||||
{t("payments.actions.generatepaymentlink")}
|
||||
</Button>
|
||||
</Popover>
|
||||
);
|
||||
}
|
||||
@@ -5,11 +5,13 @@ import { connect } from "react-redux";
|
||||
import { createStructuredSelector } from "reselect";
|
||||
import { setEmailOptions } from "../../redux/email/email.actions";
|
||||
import { selectPrintCenter } from "../../redux/modals/modals.selectors";
|
||||
import { selectTechnician } from "../../redux/tech/tech.selectors";
|
||||
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||
import { GenerateDocument } from "../../utils/RenderTemplate";
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
printCenterModal: selectPrintCenter,
|
||||
bodyshop: selectBodyshop,
|
||||
technician: selectTechnician,
|
||||
});
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
setEmailOptions: (e) => dispatch(setEmailOptions(e)),
|
||||
@@ -22,6 +24,7 @@ export function PrintCenterItemComponent({
|
||||
id,
|
||||
bodyshop,
|
||||
disabled,
|
||||
technician,
|
||||
}) {
|
||||
const [loading, setLoading] = useState(false);
|
||||
const { context } = printCenterModal;
|
||||
@@ -44,19 +47,24 @@ export function PrintCenterItemComponent({
|
||||
<Space wrap>
|
||||
{item.title}
|
||||
<PrinterOutlined onClick={renderToNewWindow} />
|
||||
<MailOutlined
|
||||
onClick={() => {
|
||||
GenerateDocument(
|
||||
{
|
||||
name: item.key,
|
||||
variables: { id: id },
|
||||
},
|
||||
{ to: context.job && context.job.ownr_ea, subject: item.subject },
|
||||
"e",
|
||||
id
|
||||
);
|
||||
}}
|
||||
/>
|
||||
{!technician ? (
|
||||
<MailOutlined
|
||||
onClick={() => {
|
||||
GenerateDocument(
|
||||
{
|
||||
name: item.key,
|
||||
variables: { id: id },
|
||||
},
|
||||
{
|
||||
to: context.job && context.job.ownr_ea,
|
||||
subject: item.subject,
|
||||
},
|
||||
"e",
|
||||
id
|
||||
);
|
||||
}}
|
||||
/>
|
||||
) : null}
|
||||
{loading && <Spin />}
|
||||
</Space>
|
||||
</li>
|
||||
|
||||
@@ -1,31 +1,33 @@
|
||||
import { PrinterFilled } from "@ant-design/icons";
|
||||
import { useQuery } from "@apollo/client";
|
||||
import { Descriptions, Drawer, Space, PageHeader, Button } from "antd";
|
||||
import { Button, Descriptions, Drawer, PageHeader, Space } from "antd";
|
||||
import queryString from "query-string";
|
||||
import React from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { connect } from "react-redux";
|
||||
import { useHistory, useLocation } from "react-router-dom";
|
||||
import { createStructuredSelector } from "reselect";
|
||||
import { QUERY_JOB_CARD_DETAILS } from "../../graphql/jobs.queries";
|
||||
import { setModalContext } from "../../redux/modals/modals.actions";
|
||||
import { selectTechnician } from "../../redux/tech/tech.selectors";
|
||||
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||
import CurrencyFormatter from "../../utils/CurrencyFormatter";
|
||||
import { DateFormatter } from "../../utils/DateFormatter";
|
||||
import AlertComponent from "../alert/alert.component";
|
||||
import StartChatButton from "../chat-open-button/chat-open-button.component";
|
||||
import JobAtChange from "../job-at-change/job-at-change.component";
|
||||
import JobDetailCardsDocumentsComponent from "../job-detail-cards/job-detail-cards.documents.component";
|
||||
import JobDetailCardsNotesComponent from "../job-detail-cards/job-detail-cards.notes.component";
|
||||
import JobDetailCardsPartsComponent from "../job-detail-cards/job-detail-cards.parts.component";
|
||||
import JobEmployeeAssignments from "../job-employee-assignments/job-employee-assignments.container";
|
||||
import LoadingSkeleton from "../loading-skeleton/loading-skeleton.component";
|
||||
import ProductionRemoveButton from "../production-remove-button/production-remove-button.component";
|
||||
import JobAtChange from "../job-at-change/job-at-change.component";
|
||||
import { PrinterFilled } from "@ant-design/icons";
|
||||
import OwnerNameDisplay from "../owner-name-display/owner-name-display.component";
|
||||
import { connect } from "react-redux";
|
||||
import { createStructuredSelector } from "reselect";
|
||||
import { setModalContext } from "../../redux/modals/modals.actions";
|
||||
import ScoreboardAddButton from "../job-scoreboard-add-button/job-scoreboard-add-button.component";
|
||||
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||
import LoadingSkeleton from "../loading-skeleton/loading-skeleton.component";
|
||||
import OwnerNameDisplay from "../owner-name-display/owner-name-display.component";
|
||||
import ProductionRemoveButton from "../production-remove-button/production-remove-button.component";
|
||||
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
bodyshop: selectBodyshop,
|
||||
technician: selectTechnician,
|
||||
});
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
setPrintCenterContext: (context) =>
|
||||
@@ -40,6 +42,7 @@ export function ProductionListDetail({
|
||||
bodyshop,
|
||||
jobs,
|
||||
setPrintCenterContext,
|
||||
technician,
|
||||
}) {
|
||||
const search = queryString.parse(useLocation().search);
|
||||
const history = useHistory();
|
||||
@@ -66,7 +69,9 @@ export function ProductionListDetail({
|
||||
title={theJob.ro_number}
|
||||
extra={
|
||||
<Space wrap>
|
||||
<ProductionRemoveButton jobId={theJob.id} />{" "}
|
||||
{!technician ? (
|
||||
<ProductionRemoveButton jobId={theJob.id} />
|
||||
) : null}
|
||||
<Button
|
||||
onClick={() => {
|
||||
setPrintCenterContext({
|
||||
@@ -82,7 +87,9 @@ export function ProductionListDetail({
|
||||
<PrinterFilled />
|
||||
{t("jobs.actions.printCenter")}
|
||||
</Button>
|
||||
<ScoreboardAddButton job={data ? data.jobs_by_pk : {}} />
|
||||
{!technician ? (
|
||||
<ScoreboardAddButton job={data ? data.jobs_by_pk : {}} />
|
||||
) : null}
|
||||
</Space>
|
||||
}
|
||||
/>
|
||||
|
||||
@@ -59,11 +59,12 @@ export function ScheduleManualEvent({ bodyshop, event }) {
|
||||
refetchQueries: ["QUERY_ALL_ACTIVE_APPOINTMENTS"],
|
||||
});
|
||||
}
|
||||
form.resetFields();
|
||||
setVisibility(false);
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
} finally {
|
||||
setLoading(false);
|
||||
setVisibility(false);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -47,9 +47,7 @@ export function ScoreboardChart({ sbEntriesByDate, bodyshop }) {
|
||||
bodyhrs: dayAcc.bodyhrs + dayVal.bodyhrs,
|
||||
painthrs: dayAcc.painthrs + dayVal.painthrs,
|
||||
sales:
|
||||
dayAcc.painthrs +
|
||||
dayVal.job.job_totals.totals.subtotal.amount / 100 +
|
||||
2500,
|
||||
dayAcc.sales + dayVal.job.job_totals.totals.subtotal.amount / 100,
|
||||
};
|
||||
},
|
||||
{ bodyhrs: 0, painthrs: 0, sales: 0 }
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
import React, { useEffect, useState } from "react";
|
||||
import ShopInfoComponent from "./shop-info.component";
|
||||
import { useMutation, useQuery } from "@apollo/client";
|
||||
import { Form, notification } from "antd";
|
||||
import { useQuery, useMutation } from "@apollo/client";
|
||||
import { QUERY_BODYSHOP, UPDATE_SHOP } from "../../graphql/bodyshop.queries";
|
||||
import LoadingSpinner from "../loading-spinner/loading-spinner.component";
|
||||
import AlertComponent from "../alert/alert.component";
|
||||
import moment from "moment";
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { logImEXEvent } from "../../firebase/firebase.utils";
|
||||
import { QUERY_BODYSHOP, UPDATE_SHOP } from "../../graphql/bodyshop.queries";
|
||||
import AlertComponent from "../alert/alert.component";
|
||||
import FormsFieldChanged from "../form-fields-changed-alert/form-fields-changed-alert.component";
|
||||
import moment from "moment";
|
||||
import LoadingSpinner from "../loading-spinner/loading-spinner.component";
|
||||
import ShopInfoComponent from "./shop-info.component";
|
||||
export default function ShopInfoContainer() {
|
||||
const [form] = Form.useForm();
|
||||
const { t } = useTranslation();
|
||||
@@ -52,13 +52,28 @@ export default function ShopInfoContainer() {
|
||||
onFinish={handleFinish}
|
||||
initialValues={
|
||||
data
|
||||
? {
|
||||
...data.bodyshops[0],
|
||||
schedule_start_time: moment(
|
||||
data.bodyshops[0].schedule_start_time
|
||||
),
|
||||
schedule_end_time: moment(data.bodyshops[0].schedule_end_time),
|
||||
}
|
||||
? data.bodyshops[0].accountingconfig.ClosingPeriod
|
||||
? {
|
||||
...data.bodyshops[0],
|
||||
accountingconfig: {
|
||||
...data.bodyshops[0].accountingconfig,
|
||||
ClosingPeriod: [
|
||||
moment(data.bodyshops[0].accountingconfig.ClosingPeriod[0]),
|
||||
moment(data.bodyshops[0].accountingconfig.ClosingPeriod[1]),
|
||||
],
|
||||
},
|
||||
schedule_start_time: moment(
|
||||
data.bodyshops[0].schedule_start_time
|
||||
),
|
||||
schedule_end_time: moment(data.bodyshops[0].schedule_end_time),
|
||||
}
|
||||
: {
|
||||
...data.bodyshops[0],
|
||||
schedule_start_time: moment(
|
||||
data.bodyshops[0].schedule_start_time
|
||||
),
|
||||
schedule_end_time: moment(data.bodyshops[0].schedule_end_time),
|
||||
}
|
||||
: null
|
||||
}
|
||||
>
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import { DeleteFilled } from "@ant-design/icons";
|
||||
import { useTreatments } from "@splitsoftware/splitio-react";
|
||||
import {
|
||||
Button,
|
||||
DatePicker,
|
||||
Form,
|
||||
Input,
|
||||
InputNumber,
|
||||
@@ -9,8 +11,13 @@ import {
|
||||
Space,
|
||||
Switch,
|
||||
} from "antd";
|
||||
import momentTZ from "moment-timezone";
|
||||
import React from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { connect } from "react-redux";
|
||||
import { createStructuredSelector } from "reselect";
|
||||
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||
import DatePickerRanges from "../../utils/DatePickerRanges";
|
||||
import CurrencyInput from "../form-items-formatted/currency-form-item.component";
|
||||
import FormItemEmail from "../form-items-formatted/email-form-item.component";
|
||||
import PhoneFormItem, {
|
||||
@@ -18,12 +25,22 @@ import PhoneFormItem, {
|
||||
} from "../form-items-formatted/phone-form-item.component";
|
||||
import FormListMoveArrows from "../form-list-move-arrows/form-list-move-arrows.component";
|
||||
import LayoutFormRow from "../layout-form-row/layout-form-row.component";
|
||||
|
||||
import momentTZ from "moment-timezone";
|
||||
const timeZonesList = momentTZ.tz.names();
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
bodyshop: selectBodyshop,
|
||||
});
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
//setUserLanguage: language => dispatch(setUserLanguage(language))
|
||||
});
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(ShopInfoGeneral);
|
||||
|
||||
export default function ShopInfoGeneral({ form }) {
|
||||
export function ShopInfoGeneral({ form, bodyshop }) {
|
||||
const { t } = useTranslation();
|
||||
const { ClosingPeriod } = useTreatments(
|
||||
["ClosingPeriod"],
|
||||
{},
|
||||
bodyshop && bodyshop.imexshopid
|
||||
);
|
||||
|
||||
return (
|
||||
<div>
|
||||
@@ -392,6 +409,20 @@ export default function ShopInfoGeneral({ form }) {
|
||||
>
|
||||
<Select mode="tags" />
|
||||
</Form.Item>
|
||||
{ClosingPeriod.treatment === "on" && (
|
||||
<>
|
||||
<Form.Item
|
||||
allowClear
|
||||
name={["accountingconfig", "ClosingPeriod"]}
|
||||
label={t("bodyshop.fields.closingperiod")} //{t("reportcenter.labels.dates")}
|
||||
>
|
||||
<DatePicker.RangePicker
|
||||
format="MM/DD/YYYY"
|
||||
ranges={DatePickerRanges}
|
||||
/>
|
||||
</Form.Item>
|
||||
</>
|
||||
)}
|
||||
</LayoutFormRow>
|
||||
<LayoutFormRow
|
||||
header={t("bodyshop.labels.scoreboardsetup")}
|
||||
@@ -602,6 +633,20 @@ export default function ShopInfoGeneral({ form }) {
|
||||
>
|
||||
<Select mode="tags" />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
name={["md_email_cc", "parts_return_slip"]}
|
||||
label={t("bodyshop.fields.md_email_cc", {
|
||||
template: "parts_return_slip",
|
||||
})}
|
||||
rules={[
|
||||
{
|
||||
//message: t("general.validation.required"),
|
||||
type: "array",
|
||||
},
|
||||
]}
|
||||
>
|
||||
<Select mode="tags" />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
name={["tt_allow_post_to_invoiced"]}
|
||||
label={t("bodyshop.fields.tt_allow_post_to_invoiced")}
|
||||
|
||||
@@ -162,6 +162,32 @@ export function ShopInfoResponsibilityCenterComponent({ bodyshop, form }) {
|
||||
<Switch />
|
||||
</Form.Item>
|
||||
)}
|
||||
{bodyshop.pbs_serialnumber && (
|
||||
<Form.Item
|
||||
label={t("bodyshop.fields.dms.appostingaccount")}
|
||||
name={["pbs_configuration", "appostingaccount"]}
|
||||
>
|
||||
<Select
|
||||
options={[
|
||||
{ value: "wip", label: "WIP" },
|
||||
{ value: "cogs", label: "COGS" },
|
||||
]}
|
||||
/>
|
||||
</Form.Item>
|
||||
)}
|
||||
{bodyshop.pbs_serialnumber && (
|
||||
<Form.Item
|
||||
label={t("bodyshop.fields.dms.apcontrol")}
|
||||
name={["pbs_configuration", "apcontrol"]}
|
||||
>
|
||||
<Select
|
||||
options={[
|
||||
{ value: "ro", label: "RO Number" },
|
||||
{ value: "vendordmsid", label: "Vendor DMS ID" },
|
||||
]}
|
||||
/>
|
||||
</Form.Item>
|
||||
)}
|
||||
</LayoutFormRow>
|
||||
<LayoutFormRow header={t("bodyshop.labels.dms.cdk.payers")}>
|
||||
<Form.List name={["cdk_configuration", "payers"]}>
|
||||
|
||||
@@ -1,21 +1,26 @@
|
||||
import { useMutation } from "@apollo/client";
|
||||
import { Button, Card, Form, notification, Space } from "antd";
|
||||
import axios from "axios";
|
||||
import moment from "moment";
|
||||
import momenttz from "moment-timezone";
|
||||
import React, { useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { connect } from "react-redux";
|
||||
import { createStructuredSelector } from "reselect";
|
||||
import { INSERT_NEW_TIME_TICKET } from "../../graphql/timetickets.queries";
|
||||
import { selectTechnician } from "../../redux/tech/tech.selectors";
|
||||
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||
import TechClockInComponent from "./tech-job-clock-in-form.component";
|
||||
import TechJobPrintTickets from "../tech-job-print-tickets/tech-job-print-tickets.component";
|
||||
import moment from "moment";
|
||||
import { setModalContext } from "../../redux/modals/modals.actions";
|
||||
import { selectTechnician } from "../../redux/tech/tech.selectors";
|
||||
import {
|
||||
selectBodyshop,
|
||||
selectCurrentUser,
|
||||
} from "../../redux/user/user.selectors";
|
||||
import TechJobPrintTickets from "../tech-job-print-tickets/tech-job-print-tickets.component";
|
||||
import TechClockInComponent from "./tech-job-clock-in-form.component";
|
||||
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
technician: selectTechnician,
|
||||
bodyshop: selectBodyshop,
|
||||
currentUser: selectCurrentUser,
|
||||
});
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
setTimeTicketContext: (context) =>
|
||||
@@ -25,9 +30,10 @@ export function TechClockInContainer({
|
||||
setTimeTicketContext,
|
||||
technician,
|
||||
bodyshop,
|
||||
currentUser,
|
||||
}) {
|
||||
console.log(
|
||||
"🚀 ~ file: tech-job-clock-in-form.container.jsx:29 ~ technician:",
|
||||
"🚀 ~ file: tech-job-clock-in-form.container.jsx:30 ~ technician:",
|
||||
technician
|
||||
);
|
||||
const [form] = Form.useForm();
|
||||
@@ -44,14 +50,20 @@ export function TechClockInContainer({
|
||||
const handleFinish = async (values) => {
|
||||
setLoading(true);
|
||||
const theTime = (await axios.post("/utils/time")).data;
|
||||
|
||||
const result = await insertTimeTicket({
|
||||
variables: {
|
||||
timeTicketInput: [
|
||||
{
|
||||
bodyshopid: bodyshop.id,
|
||||
employeeid: technician.id,
|
||||
date: moment(theTime).format("YYYY-MM-DD"),
|
||||
date:
|
||||
typeof bodyshop.timezone === "string"
|
||||
? momenttz.tz(theTime, bodyshop.timezone).format("YYYY-MM-DD")
|
||||
: typeof bodyshop.timezone === "number"
|
||||
? moment(theTime)
|
||||
.format("YYYY-MM-DD")
|
||||
.utcOffset(bodyshop.timezone)
|
||||
: moment(theTime).format("YYYY-MM-DD"),
|
||||
clockon: moment(theTime),
|
||||
jobid: values.jobid,
|
||||
cost_center: values.cost_center,
|
||||
@@ -66,6 +78,12 @@ export function TechClockInContainer({
|
||||
values.cost_center
|
||||
);
|
||||
}),
|
||||
created_by: currentUser.email.concat(
|
||||
" | ",
|
||||
technician.employee_number
|
||||
.concat(" ", technician.first_name, " ", technician.last_name)
|
||||
.trim()
|
||||
),
|
||||
},
|
||||
],
|
||||
},
|
||||
@@ -100,13 +118,24 @@ export function TechClockInContainer({
|
||||
employeeid: technician.id,
|
||||
flat_rate: emps.flat_rate,
|
||||
},
|
||||
created_by: currentUser.email.concat(
|
||||
" | ",
|
||||
technician.employee_number
|
||||
.concat(
|
||||
" ",
|
||||
technician.first_name,
|
||||
" ",
|
||||
technician.last_name
|
||||
)
|
||||
.trim()
|
||||
),
|
||||
},
|
||||
});
|
||||
}}
|
||||
>
|
||||
{t("timetickets.actions.enter")}
|
||||
</Button>
|
||||
<TechJobPrintTickets />
|
||||
<TechJobPrintTickets attendacePrint={false} />
|
||||
<Button
|
||||
type="primary"
|
||||
onClick={() => form.submit()}
|
||||
|
||||
@@ -5,10 +5,10 @@ import {
|
||||
Col,
|
||||
Form,
|
||||
InputNumber,
|
||||
notification,
|
||||
Popover,
|
||||
Row,
|
||||
Select,
|
||||
notification,
|
||||
} from "antd";
|
||||
import axios from "axios";
|
||||
import React, { useState } from "react";
|
||||
@@ -16,13 +16,14 @@ import { useTranslation } from "react-i18next";
|
||||
import { connect } from "react-redux";
|
||||
import { createStructuredSelector } from "reselect";
|
||||
import { logImEXEvent } from "../../firebase/firebase.utils";
|
||||
import { GET_LINE_TICKET_BY_PK } from "../../graphql/jobs-lines.queries";
|
||||
import { UPDATE_JOB_STATUS } from "../../graphql/jobs.queries";
|
||||
import { UPDATE_TIME_TICKET } from "../../graphql/timetickets.queries";
|
||||
import { selectTechnician } from "../../redux/tech/tech.selectors";
|
||||
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||
import { CalculateAllocationsTotals } from "../labor-allocations-table/labor-allocations-table.utility";
|
||||
import TechJobClockoutDelete from "../tech-job-clock-out-delete/tech-job-clock-out-delete.component";
|
||||
import { LaborAllocationContainer } from "../time-ticket-modal/time-ticket-modal.component";
|
||||
import { GET_LINE_TICKET_BY_PK } from "../../graphql/jobs-lines.queries";
|
||||
import { CalculateAllocationsTotals } from "../labor-allocations-table/labor-allocations-table.utility";
|
||||
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
bodyshop: selectBodyshop,
|
||||
@@ -40,6 +41,7 @@ export function TechClockOffButton({
|
||||
}) {
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [updateTimeticket] = useMutation(UPDATE_TIME_TICKET);
|
||||
const [updateJobStatus] = useMutation(UPDATE_JOB_STATUS);
|
||||
const [form] = Form.useForm();
|
||||
const { queryLoading, data: lineTicketData } = useQuery(
|
||||
GET_LINE_TICKET_BY_PK,
|
||||
@@ -59,7 +61,8 @@ export function TechClockOffButton({
|
||||
|
||||
const handleFinish = async (values) => {
|
||||
logImEXEvent("tech_clock_out_job");
|
||||
|
||||
const status = values.status;
|
||||
delete values.status;
|
||||
setLoading(true);
|
||||
const result = await updateTimeticket({
|
||||
variables: {
|
||||
@@ -98,6 +101,26 @@ export function TechClockOffButton({
|
||||
message: t("timetickets.successes.clockedout"),
|
||||
});
|
||||
}
|
||||
if (!isShiftTicket) {
|
||||
const job_update_result = await updateJobStatus({
|
||||
variables: {
|
||||
jobId: jobId,
|
||||
status: status,
|
||||
},
|
||||
});
|
||||
|
||||
if (!!job_update_result.errors) {
|
||||
notification["error"]({
|
||||
message: t("jobs.errors.updating", {
|
||||
message: JSON.stringify(result.errors),
|
||||
}),
|
||||
});
|
||||
} else {
|
||||
notification["success"]({
|
||||
message: t("jobs.successes.updated"),
|
||||
});
|
||||
}
|
||||
}
|
||||
setLoading(false);
|
||||
if (completedCallback) completedCallback();
|
||||
};
|
||||
@@ -195,7 +218,6 @@ export function TechClockOffButton({
|
||||
</Form.Item>
|
||||
</div>
|
||||
) : null}
|
||||
|
||||
<Form.Item
|
||||
name="cost_center"
|
||||
label={t("timetickets.fields.cost_center")}
|
||||
@@ -228,6 +250,29 @@ export function TechClockOffButton({
|
||||
</Select>
|
||||
</Form.Item>
|
||||
|
||||
{isShiftTicket ? (
|
||||
<div></div>
|
||||
) : (
|
||||
<Form.Item
|
||||
name="status"
|
||||
label={t("jobs.fields.status")}
|
||||
initialValue={
|
||||
lineTicketData && lineTicketData.jobs_by_pk.status
|
||||
}
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
//message: t("general.validation.required"),
|
||||
},
|
||||
]}
|
||||
>
|
||||
<Select>
|
||||
{bodyshop.md_ro_statuses.production_statuses.map((item) => (
|
||||
<Select.Option key={item}></Select.Option>
|
||||
))}
|
||||
</Select>
|
||||
</Form.Item>
|
||||
)}
|
||||
<Button type="primary" htmlType="submit" loading={loading}>
|
||||
{t("general.actions.save")}
|
||||
</Button>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Button, Card, DatePicker, Form, Popover, Space } from "antd";
|
||||
import { Button, Card, DatePicker, Form, Popover, Radio, Space } from "antd";
|
||||
import moment from "moment";
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
@@ -21,12 +21,13 @@ export default connect(
|
||||
mapDispatchToProps
|
||||
)(TechJobPrintTickets);
|
||||
|
||||
export function TechJobPrintTickets({ technician, event }) {
|
||||
export function TechJobPrintTickets({ technician, event, attendacePrint }) {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [form] = Form.useForm();
|
||||
const [visibility, setVisibility] = useState(false);
|
||||
const Templates = TemplateList("report_center");
|
||||
|
||||
useEffect(() => {
|
||||
if (visibility && event) {
|
||||
@@ -44,7 +45,10 @@ export function TechJobPrintTickets({ technician, event }) {
|
||||
try {
|
||||
await GenerateDocument(
|
||||
{
|
||||
name: TemplateList().timetickets_employee.key,
|
||||
name:
|
||||
attendacePrint === true
|
||||
? Templates.attendance_employee.key
|
||||
: Templates.timetickets_employee.key,
|
||||
variables: {
|
||||
...(start
|
||||
? { start: moment(start).startOf("day").format("YYYY-MM-DD") }
|
||||
@@ -60,9 +64,12 @@ export function TechJobPrintTickets({ technician, event }) {
|
||||
},
|
||||
{
|
||||
to: technician.email,
|
||||
subject: TemplateList().timetickets_employee.subject,
|
||||
subject:
|
||||
attendacePrint === true
|
||||
? Templates.attendance_employee.subject
|
||||
: Templates.timetickets_employee.subject,
|
||||
},
|
||||
"p"
|
||||
values.sendby // === "email" ? "e" : "p"
|
||||
);
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
@@ -92,10 +99,25 @@ export function TechJobPrintTickets({ technician, event }) {
|
||||
format={"MM/DD/YYYY"}
|
||||
/>
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item dependencies={["dates"]}>
|
||||
{() => {
|
||||
return (
|
||||
<Form.Item
|
||||
label={t("general.labels.sendby")}
|
||||
name="sendby"
|
||||
initialValue="p"
|
||||
>
|
||||
<Radio.Group>
|
||||
<Radio value="e">{t("general.labels.email")}</Radio>
|
||||
<Radio value="p">{t("general.labels.print")}</Radio>
|
||||
</Radio.Group>
|
||||
</Form.Item>
|
||||
);
|
||||
}}
|
||||
</Form.Item>
|
||||
<Space wrap>
|
||||
<Button type="primary" onClick={() => form.submit()}>
|
||||
{t("general.actions.print")}
|
||||
{t("reportcenter.actions.generate")}
|
||||
</Button>
|
||||
<Button
|
||||
onClick={() => {
|
||||
@@ -118,7 +140,7 @@ export function TechJobPrintTickets({ technician, event }) {
|
||||
return (
|
||||
<Popover content={overlay} visible={visibility}>
|
||||
<Button loading={loading} onClick={handleClick}>
|
||||
{t("general.actions.print")}
|
||||
{t("general.labels.reports")}
|
||||
</Button>
|
||||
</Popover>
|
||||
);
|
||||
|
||||
@@ -9,9 +9,10 @@ import { createStructuredSelector } from "reselect";
|
||||
import {
|
||||
selectAuthLevel,
|
||||
selectBodyshop,
|
||||
selectCurrentUser,
|
||||
} from "../../redux/user/user.selectors";
|
||||
import { onlyUnique } from "../../utils/arrayHelper";
|
||||
import { DateFormatter, DateTimeFormatter } from "../../utils/DateFormatter";
|
||||
import { onlyUnique } from "../../utils/arrayHelper";
|
||||
import { alphaSort, dateSort } from "../../utils/sorters";
|
||||
import RbacWrapper, {
|
||||
HasRbacAccess,
|
||||
@@ -20,6 +21,7 @@ import TimeTicketEnterButton from "../time-ticket-enter-button/time-ticket-enter
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
bodyshop: selectBodyshop,
|
||||
authLevel: selectAuthLevel,
|
||||
currentUser: selectCurrentUser,
|
||||
});
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
//setUserLanguage: language => dispatch(setUserLanguage(language))
|
||||
@@ -29,6 +31,7 @@ export default connect(mapStateToProps, mapDispatchToProps)(TimeTicketList);
|
||||
export function TimeTicketList({
|
||||
bodyshop,
|
||||
authLevel,
|
||||
currentUser,
|
||||
disabled,
|
||||
loading,
|
||||
timetickets,
|
||||
@@ -193,7 +196,15 @@ export function TimeTicketList({
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
title: t("timetickets.fields.created_by"),
|
||||
dataIndex: "created_by",
|
||||
key: "created_by",
|
||||
sorter: (a, b) => alphaSort(a.created_by, b.created_by),
|
||||
sortOrder:
|
||||
state.sortedInfo.columnKey === "created_by" && state.sortedInfo.order,
|
||||
render: (text, record) => record.created_by,
|
||||
},
|
||||
{
|
||||
title: t("general.labels.actions"),
|
||||
dataIndex: "actions",
|
||||
@@ -254,7 +265,12 @@ export function TimeTicketList({
|
||||
(techConsole ? null : (
|
||||
<TimeTicketEnterButton
|
||||
actions={{ refetch }}
|
||||
context={{ jobId: jobId }}
|
||||
context={{
|
||||
jobId: jobId,
|
||||
created_by: currentUser.displayName
|
||||
? currentUser.email.concat(" | ", currentUser.displayName)
|
||||
: currentUser.email,
|
||||
}}
|
||||
disabled={disabled}
|
||||
>
|
||||
{t("timetickets.actions.enter")}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { useMutation, useQuery } from "@apollo/client";
|
||||
import { Button, Form, Modal, notification, PageHeader, Space } from "antd";
|
||||
import { Button, Form, Modal, PageHeader, Space, notification } from "antd";
|
||||
import moment from "moment";
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
@@ -77,6 +77,7 @@ export function TimeTicketModalContainer({
|
||||
)[0].rate
|
||||
: null,
|
||||
bodyshopid: bodyshop.id,
|
||||
created_by: timeTicketModal.context.created_by,
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
@@ -4,48 +4,67 @@ import { useTranslation } from "react-i18next";
|
||||
import { DateTimeFormatter } from "../../utils/DateFormatter";
|
||||
import DataLabel from "../data-label/data-label.component";
|
||||
import TechClockOffButton from "../tech-job-clock-out-button/tech-job-clock-out-button.component";
|
||||
import TechJobPrintTickets from "../tech-job-print-tickets/tech-job-print-tickets.component";
|
||||
|
||||
export default function TimeTicketShiftActive({ timetickets, refetch }) {
|
||||
export default function TimeTicketShiftActive({
|
||||
timetickets,
|
||||
refetch,
|
||||
isTechConsole,
|
||||
}) {
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
<div>
|
||||
{timetickets.length > 0 ? (
|
||||
<div>
|
||||
<Typography.Title level={2}>
|
||||
{t("timetickets.labels.shiftalreadyclockedon")}
|
||||
</Typography.Title>
|
||||
<List
|
||||
grid={{
|
||||
gutter: 32,
|
||||
xs: 1,
|
||||
sm: 2,
|
||||
md: 3,
|
||||
lg: 4,
|
||||
xl: 5,
|
||||
xxl: 6,
|
||||
}}
|
||||
dataSource={timetickets || []}
|
||||
renderItem={(ticket) => (
|
||||
<List.Item>
|
||||
<Card
|
||||
title={t(ticket.memo)}
|
||||
actions={[
|
||||
<TechClockOffButton
|
||||
jobId={ticket.jobid}
|
||||
timeTicketId={ticket.id}
|
||||
completedCallback={refetch}
|
||||
isShiftTicket
|
||||
/>,
|
||||
]}
|
||||
>
|
||||
<DataLabel label={t("timetickets.fields.clockon")}>
|
||||
<DateTimeFormatter>{ticket.clockon}</DateTimeFormatter>
|
||||
</DataLabel>
|
||||
</Card>
|
||||
</List.Item>
|
||||
)}
|
||||
></List>
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
justifyContent: "space-between",
|
||||
flexDirection: "column",
|
||||
height: "100%",
|
||||
}}
|
||||
>
|
||||
<div style={{ display: "flex", justifyContent: "space-between" }}>
|
||||
<Typography.Title level={2}>
|
||||
{t("timetickets.labels.shiftalreadyclockedon")}
|
||||
</Typography.Title>
|
||||
{isTechConsole ? (
|
||||
<TechJobPrintTickets attendacePrint={true} />
|
||||
) : null}
|
||||
</div>
|
||||
<div style={{ flexGrow: 1 }}>
|
||||
<List
|
||||
grid={{
|
||||
gutter: 32,
|
||||
xs: 1,
|
||||
sm: 2,
|
||||
md: 3,
|
||||
lg: 4,
|
||||
xl: 5,
|
||||
xxl: 6,
|
||||
}}
|
||||
dataSource={timetickets || []}
|
||||
renderItem={(ticket) => (
|
||||
<List.Item>
|
||||
<Card
|
||||
title={t(ticket.memo)}
|
||||
actions={[
|
||||
<TechClockOffButton
|
||||
jobId={ticket.jobid}
|
||||
timeTicketId={ticket.id}
|
||||
completedCallback={refetch}
|
||||
isShiftTicket
|
||||
/>,
|
||||
]}
|
||||
>
|
||||
<DataLabel label={t("timetickets.fields.clockon")}>
|
||||
<DateTimeFormatter>{ticket.clockon}</DateTimeFormatter>
|
||||
</DataLabel>
|
||||
</Card>
|
||||
</List.Item>
|
||||
)}
|
||||
></List>
|
||||
</div>
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import { useMutation } from "@apollo/client";
|
||||
import { Button, Form, notification } from "antd";
|
||||
import { Button, Form, Space, notification } from "antd";
|
||||
import axios from "axios";
|
||||
import moment from "moment";
|
||||
import momenttz from "moment-timezone";
|
||||
import React, { useMemo, useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { connect } from "react-redux";
|
||||
@@ -12,6 +13,7 @@ import {
|
||||
selectBodyshop,
|
||||
selectCurrentUser,
|
||||
} from "../../redux/user/user.selectors";
|
||||
import TechJobPrintTickets from "../tech-job-print-tickets/tech-job-print-tickets.component";
|
||||
import TimeTicektShiftComponent from "./time-ticket-shift-form.component";
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
currentUser: selectCurrentUser,
|
||||
@@ -29,6 +31,10 @@ export function TimeTicektShiftContainer({
|
||||
isTechConsole,
|
||||
checkIfAlreadyClocked,
|
||||
}) {
|
||||
console.log(
|
||||
"🚀 ~ file: time-ticket-shift-form.container.jsx:28 ~ technician:",
|
||||
technician
|
||||
);
|
||||
const [form] = Form.useForm();
|
||||
const [insertTimeTicket] = useMutation(INSERT_NEW_TIME_TICKET);
|
||||
const { t } = useTranslation();
|
||||
@@ -63,8 +69,30 @@ export function TimeTicektShiftContainer({
|
||||
employeeid: isTechConsole ? technician.id : employeeId,
|
||||
cost_center: "timetickets.labels.shift",
|
||||
clockon: theTime,
|
||||
date: theTime,
|
||||
date:
|
||||
typeof bodyshop.timezone === "string"
|
||||
? momenttz.tz(theTime, bodyshop.timezone).format("YYYY-MM-DD")
|
||||
: typeof bodyshop.timezone === "number"
|
||||
? moment(theTime)
|
||||
.utcOffset(bodyshop.timezone)
|
||||
.format("YYYY-MM-DD")
|
||||
: moment(theTime).format("YYYY-MM-DD"),
|
||||
memo: values.memo,
|
||||
created_by: isTechConsole
|
||||
? currentUser.email.concat(
|
||||
" | ",
|
||||
technician.employee_number
|
||||
.concat(
|
||||
" ",
|
||||
technician.first_name,
|
||||
" ",
|
||||
technician.last_name
|
||||
)
|
||||
.trim()
|
||||
)
|
||||
: currentUser.displayName
|
||||
? currentUser.email.concat(" | ", currentUser.displayName)
|
||||
: currentUser.email,
|
||||
},
|
||||
],
|
||||
},
|
||||
@@ -98,9 +126,14 @@ export function TimeTicektShiftContainer({
|
||||
initialValues={{ cost_center: t("timetickets.labels.shift") }}
|
||||
>
|
||||
<TimeTicektShiftComponent form={form} />
|
||||
<Button htmlType="submit" loading={loading}>
|
||||
{t("timetickets.actions.clockin")}
|
||||
</Button>
|
||||
<Space wrap>
|
||||
<Button htmlType="submit" loading={loading} type="primary">
|
||||
{t("timetickets.actions.clockin")}
|
||||
</Button>
|
||||
{isTechConsole === true ? (
|
||||
<TechJobPrintTickets attendacePrint={true} />
|
||||
) : null}
|
||||
</Space>
|
||||
</Form>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -76,6 +76,7 @@ export function TimeTicketShiftContainer({
|
||||
<TimeTicketShiftActive
|
||||
timetickets={data ? data.timetickets : []}
|
||||
refetch={refetch}
|
||||
isTechConsole={isTechConsole}
|
||||
/>
|
||||
) : (
|
||||
<TimeTicketShiftFormContainer
|
||||
|
||||
@@ -6,8 +6,9 @@ import { Link } from "react-router-dom";
|
||||
import { createStructuredSelector } from "reselect";
|
||||
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||
import CurrencyFormatter from "../../utils/CurrencyFormatter";
|
||||
import VehicleDetailUpdateJobsComponent from "../vehicle-detail-update-jobs/vehicle-detail-update-jobs.component";
|
||||
import { alphaSort, statusSort } from "../../utils/sorters";
|
||||
import OwnerNameDisplay from "../owner-name-display/owner-name-display.component";
|
||||
import VehicleDetailUpdateJobsComponent from "../vehicle-detail-update-jobs/vehicle-detail-update-jobs.component";
|
||||
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
bodyshop: selectBodyshop,
|
||||
@@ -16,6 +17,14 @@ const mapStateToProps = createStructuredSelector({
|
||||
export function VehicleDetailJobsComponent({ vehicle, bodyshop }) {
|
||||
const { t } = useTranslation();
|
||||
const [selectedJobs, setSelectedJobs] = useState([]);
|
||||
const [state, setState] = useState({
|
||||
sortedInfo: {},
|
||||
filteredInfo: { text: "" },
|
||||
});
|
||||
|
||||
const handleTableChange = (pagination, filters, sorter) => {
|
||||
setState({ ...state, filteredInfo: filters, sortedInfo: sorter });
|
||||
};
|
||||
|
||||
const columns = [
|
||||
{
|
||||
@@ -28,6 +37,9 @@ export function VehicleDetailJobsComponent({ vehicle, bodyshop }) {
|
||||
{record.ro_number || t("general.labels.na")}
|
||||
</Link>
|
||||
),
|
||||
sorter: (a, b) => alphaSort(a.ro_number, b.ro_number),
|
||||
sortOrder:
|
||||
state.sortedInfo.columnKey === "ro_number" && state.sortedInfo.order,
|
||||
},
|
||||
{
|
||||
title: t("jobs.fields.owner"),
|
||||
@@ -43,11 +55,17 @@ export function VehicleDetailJobsComponent({ vehicle, bodyshop }) {
|
||||
title: t("jobs.fields.clm_no"),
|
||||
dataIndex: "clm_no",
|
||||
key: "clm_no",
|
||||
sorter: (a, b) => alphaSort(a.clm_no, b.clm_no),
|
||||
sortOrder:
|
||||
state.sortedInfo.columnKey === "clm_no" && state.sortedInfo.order,
|
||||
},
|
||||
{
|
||||
title: t("jobs.fields.status"),
|
||||
dataIndex: "status",
|
||||
key: "status",
|
||||
sorter: (a, b) => statusSort(a.status, b.status, bodyshop.md_ro_statuses.statuses),
|
||||
sortOrder:
|
||||
state.sortedInfo.columnKey === "status" && state.sortedInfo.order,
|
||||
},
|
||||
|
||||
{
|
||||
@@ -57,6 +75,9 @@ export function VehicleDetailJobsComponent({ vehicle, bodyshop }) {
|
||||
render: (text, record) => (
|
||||
<CurrencyFormatter>{record.clm_total}</CurrencyFormatter>
|
||||
),
|
||||
sorter: (a, b) => a.clm_total - b.clm_total,
|
||||
sortOrder:
|
||||
state.sortedInfo.columnKey === "clm_total" && state.sortedInfo.order,
|
||||
},
|
||||
];
|
||||
|
||||
@@ -76,6 +97,7 @@ export function VehicleDetailJobsComponent({ vehicle, bodyshop }) {
|
||||
rowKey="id"
|
||||
scroll={{ x: true }}
|
||||
dataSource={vehicle.jobs}
|
||||
onChange={handleTableChange}
|
||||
rowSelection={{
|
||||
onSelect: (record, selected, selectedRows) => {
|
||||
setSelectedJobs(selectedRows ? selectedRows.map((i) => i.id) : []);
|
||||
|
||||
@@ -34,6 +34,7 @@ export const GET_LINE_TICKET_BY_PK = gql`
|
||||
id
|
||||
lbr_adjustments
|
||||
converted
|
||||
status
|
||||
}
|
||||
joblines(where: { jobid: { _eq: $id }, removed: { _eq: false } }) {
|
||||
id
|
||||
@@ -56,6 +57,7 @@ export const GET_LINE_TICKET_BY_PK = gql`
|
||||
actualhrs
|
||||
ciecacode
|
||||
cost_center
|
||||
created_by
|
||||
date
|
||||
id
|
||||
jobid
|
||||
|
||||
@@ -574,7 +574,6 @@ export const GET_JOB_BY_PK = gql`
|
||||
est_co_nm
|
||||
est_ct_fn
|
||||
est_ct_ln
|
||||
|
||||
est_ph1
|
||||
est_ea
|
||||
selling_dealer
|
||||
@@ -682,6 +681,7 @@ export const GET_JOB_BY_PK = gql`
|
||||
date_rentalresp
|
||||
date_exported
|
||||
date_repairstarted
|
||||
date_void
|
||||
status
|
||||
owner_owing
|
||||
tax_registration_number
|
||||
@@ -745,8 +745,8 @@ export const GET_JOB_BY_PK = gql`
|
||||
jobid
|
||||
amount
|
||||
payer
|
||||
paymentnum
|
||||
created_at
|
||||
stripeid
|
||||
transactionid
|
||||
memo
|
||||
date
|
||||
@@ -1078,6 +1078,7 @@ export const UPDATE_JOB = gql`
|
||||
scheduled_completion
|
||||
actual_in
|
||||
date_repairstarted
|
||||
date_void
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1125,6 +1126,7 @@ export const VOID_JOB = gql`
|
||||
update_jobs_by_pk(_set: $job, pk_columns: { id: $jobId }) {
|
||||
id
|
||||
date_exported
|
||||
date_void
|
||||
status
|
||||
alt_transport
|
||||
ro_number
|
||||
@@ -1219,10 +1221,10 @@ export const ACTIVE_JOBS_FOR_AUTOCOMPLETE = gql`
|
||||
query ACTIVE_JOBS_FOR_AUTOCOMPLETE($statuses: [String!]!) {
|
||||
jobs(where: { status: { _in: $statuses } }) {
|
||||
id
|
||||
ownr_co_nm
|
||||
ownr_fn
|
||||
ownr_ln
|
||||
ro_number
|
||||
|
||||
vehicleid
|
||||
v_make_desc
|
||||
v_model_desc
|
||||
@@ -1250,6 +1252,7 @@ export const SEARCH_JOBS_FOR_AUTOCOMPLETE = gql`
|
||||
}
|
||||
) {
|
||||
id
|
||||
ownr_co_nm
|
||||
ownr_fn
|
||||
ownr_ln
|
||||
ro_number
|
||||
@@ -1266,6 +1269,7 @@ export const SEARCH_JOBS_BY_ID_FOR_AUTOCOMPLETE = gql`
|
||||
query SEARCH_JOBS_BY_ID_FOR_AUTOCOMPLETE($id: uuid!) {
|
||||
jobs_by_pk(id: $id) {
|
||||
id
|
||||
ownr_co_nm
|
||||
ownr_fn
|
||||
ownr_ln
|
||||
ro_number
|
||||
@@ -1284,6 +1288,7 @@ export const SEARCH_FOR_JOBS = gql`
|
||||
search_jobs(args: { search: $search }, limit: 25) {
|
||||
id
|
||||
ro_number
|
||||
ownr_co_nm
|
||||
ownr_fn
|
||||
ownr_ln
|
||||
}
|
||||
|
||||
@@ -69,7 +69,7 @@ export const QUERY_OWNER_BY_ID = gql`
|
||||
preferred_contact
|
||||
note
|
||||
tax_number
|
||||
jobs {
|
||||
jobs(order_by: { date_open: desc }) {
|
||||
id
|
||||
ro_number
|
||||
clm_no
|
||||
|
||||
53
client/src/graphql/payment_response.queries.js
Normal file
53
client/src/graphql/payment_response.queries.js
Normal file
@@ -0,0 +1,53 @@
|
||||
import { gql } from "@apollo/client";
|
||||
|
||||
export const INSERT_PAYMENT_RESPONSE = gql`
|
||||
mutation INSERT_PAYMENT_RESPONSE(
|
||||
$paymentResponse: [payment_response_insert_input!]!
|
||||
) {
|
||||
insert_payment_response(objects: $paymentResponse) {
|
||||
returning {
|
||||
id
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
export const QUERY_PAYMENT_RESPONSE_BY_PAYMENT_ID = gql`
|
||||
query QUERY_PAYMENT_RESPONSE_BY_PK($paymentid: uuid!) {
|
||||
payment_response(where: { paymentid: { _eq: $paymentid } }) {
|
||||
id
|
||||
jobid
|
||||
bodyshopid
|
||||
paymentid
|
||||
amount
|
||||
declinereason
|
||||
ext_paymentid
|
||||
successful
|
||||
response
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
export const QUERY_RO_AND_OWNER_BY_JOB_PKS = gql`
|
||||
query QUERY_RO_AND_OWNER_BY_JOB_PKS($jobids: [uuid!]!) {
|
||||
jobs(where: { id: { _in: $jobids } }) {
|
||||
ro_number
|
||||
ownr_fn
|
||||
ownr_ln
|
||||
ownr_ea
|
||||
ownr_zip
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
export const GET_REFUNDABLE_AMOUNT_BY_JOBID = gql`
|
||||
query GET_REFUNDABLE_AMOUNT_BY_JOBID($jobid: uuid!) {
|
||||
payment_response_aggregate(where: { jobid: { _eq: $jobid } }) {
|
||||
aggregate {
|
||||
sum {
|
||||
amount
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
@@ -5,6 +5,15 @@ export const INSERT_NEW_PAYMENT = gql`
|
||||
insert_payments(objects: $paymentInput) {
|
||||
returning {
|
||||
id
|
||||
jobid
|
||||
amount
|
||||
payer
|
||||
created_at
|
||||
transactionid
|
||||
memo
|
||||
date
|
||||
type
|
||||
exportedat
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,6 +37,7 @@ export const QUERY_TIME_TICKETS_IN_RANGE = gql`
|
||||
clockon
|
||||
cost_center
|
||||
created_at
|
||||
created_by
|
||||
date
|
||||
id
|
||||
rate
|
||||
@@ -80,6 +81,7 @@ export const QUERY_TIME_TICKETS_TECHNICIAN_IN_RANGE = gql`
|
||||
clockon
|
||||
cost_center
|
||||
created_at
|
||||
created_by
|
||||
date
|
||||
id
|
||||
rate
|
||||
@@ -112,6 +114,7 @@ export const QUERY_TIME_TICKETS_TECHNICIAN_IN_RANGE = gql`
|
||||
clockon
|
||||
cost_center
|
||||
created_at
|
||||
created_by
|
||||
date
|
||||
id
|
||||
rate
|
||||
@@ -151,6 +154,7 @@ export const QUERY_TIME_TICKETS_IN_RANGE_SB = gql`
|
||||
clockon
|
||||
cost_center
|
||||
created_at
|
||||
created_by
|
||||
date
|
||||
id
|
||||
rate
|
||||
@@ -181,6 +185,7 @@ export const QUERY_TIME_TICKETS_IN_RANGE_SB = gql`
|
||||
clockon
|
||||
cost_center
|
||||
created_at
|
||||
created_by
|
||||
date
|
||||
id
|
||||
rate
|
||||
@@ -210,6 +215,7 @@ export const INSERT_NEW_TIME_TICKET = gql`
|
||||
insert_timetickets(objects: $timeTicketInput) {
|
||||
returning {
|
||||
id
|
||||
created_by
|
||||
clockon
|
||||
clockoff
|
||||
employeeid
|
||||
|
||||
@@ -28,11 +28,10 @@ export const QUERY_VEHICLE_BY_ID = gql`
|
||||
updated_at
|
||||
trim_color
|
||||
notes
|
||||
jobs {
|
||||
jobs(order_by: { date_open: desc }) {
|
||||
id
|
||||
ro_number
|
||||
ownr_fn
|
||||
|
||||
ownr_ln
|
||||
owner {
|
||||
id
|
||||
|
||||
@@ -13,7 +13,7 @@ import queryString from "query-string";
|
||||
import React, { useEffect, useRef, useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { connect } from "react-redux";
|
||||
import { useHistory, useLocation, Link } from "react-router-dom";
|
||||
import { Link, useHistory, useLocation } from "react-router-dom";
|
||||
import { createStructuredSelector } from "reselect";
|
||||
import SocketIO from "socket.io-client";
|
||||
import AlertComponent from "../../components/alert/alert.component";
|
||||
@@ -22,6 +22,7 @@ import DmsCustomerSelector from "../../components/dms-customer-selector/dms-cust
|
||||
import DmsLogEvents from "../../components/dms-log-events/dms-log-events.component";
|
||||
import DmsPostForm from "../../components/dms-post-form/dms-post-form.component";
|
||||
import LoadingSpinner from "../../components/loading-spinner/loading-spinner.component";
|
||||
import { OwnerNameDisplayFunction } from "../../components/owner-name-display/owner-name-display.component";
|
||||
import { auth } from "../../firebase/firebase.utils";
|
||||
import { QUERY_JOB_EXPORT_DMS } from "../../graphql/jobs.queries";
|
||||
import {
|
||||
@@ -29,7 +30,6 @@ import {
|
||||
setSelectedHeader,
|
||||
} from "../../redux/application/application.actions";
|
||||
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||
import { OwnerNameDisplayFunction } from "../../components/owner-name-display/owner-name-display.component";
|
||||
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
bodyshop: selectBodyshop,
|
||||
@@ -46,6 +46,7 @@ export const socket = SocketIO(
|
||||
process.env.NODE_ENV === "production"
|
||||
? process.env.REACT_APP_AXIOS_BASE_API_URL
|
||||
: window.location.origin,
|
||||
// "http://localhost:4000", // for dev testing,
|
||||
{
|
||||
path: "/ws",
|
||||
withCredentials: true,
|
||||
|
||||
@@ -8,7 +8,6 @@ import {
|
||||
Form,
|
||||
Input,
|
||||
InputNumber,
|
||||
notification,
|
||||
PageHeader,
|
||||
Popconfirm,
|
||||
Row,
|
||||
@@ -17,12 +16,14 @@ import {
|
||||
Statistic,
|
||||
Switch,
|
||||
Typography,
|
||||
notification,
|
||||
} from "antd";
|
||||
import React, { useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { connect } from "react-redux";
|
||||
//import { useHistory } from "react-router-dom";
|
||||
import { useTreatments } from "@splitsoftware/splitio-react";
|
||||
import Dinero from "dinero.js";
|
||||
import moment from "moment";
|
||||
import { Link } from "react-router-dom";
|
||||
import { createStructuredSelector } from "reselect";
|
||||
@@ -37,7 +38,6 @@ import { generateJobLinesUpdatesForInvoicing } from "../../graphql/jobs-lines.qu
|
||||
import { UPDATE_JOB } from "../../graphql/jobs.queries";
|
||||
import { selectJobReadOnly } from "../../redux/application/application.selectors";
|
||||
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||
import Dinero from "dinero.js";
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
bodyshop: selectBodyshop,
|
||||
jobRO: selectJobReadOnly,
|
||||
@@ -55,6 +55,11 @@ export function JobsCloseComponent({ job, bodyshop, jobRO }) {
|
||||
{},
|
||||
bodyshop && bodyshop.imexshopid
|
||||
);
|
||||
const { ClosingPeriod } = useTreatments(
|
||||
["ClosingPeriod"],
|
||||
{},
|
||||
bodyshop && bodyshop.imexshopid
|
||||
);
|
||||
|
||||
const handleFinish = async ({ removefromproduction, ...values }) => {
|
||||
setLoading(true);
|
||||
@@ -254,12 +259,40 @@ export function JobsCloseComponent({ job, bodyshop, jobRO }) {
|
||||
if (!value || moment(value).isSameOrAfter(moment(), "day")) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
return Promise.reject(
|
||||
new Error(t("jobs.labels.dms.invoicedatefuture"))
|
||||
);
|
||||
},
|
||||
}),
|
||||
({ getFieldValue }) => ({
|
||||
validator(_, value) {
|
||||
if (
|
||||
ClosingPeriod.treatment === "on" &&
|
||||
bodyshop.accountingconfig.ClosingPeriod
|
||||
) {
|
||||
if (
|
||||
moment(value).isSameOrAfter(
|
||||
moment(
|
||||
bodyshop.accountingconfig.ClosingPeriod[0]
|
||||
).startOf("day")
|
||||
) &&
|
||||
moment(value).isSameOrBefore(
|
||||
moment(
|
||||
bodyshop.accountingconfig.ClosingPeriod[1]
|
||||
).endOf("day")
|
||||
)
|
||||
) {
|
||||
return Promise.resolve();
|
||||
} else {
|
||||
return Promise.reject(
|
||||
new Error(t("jobs.labels.closingperiod"))
|
||||
);
|
||||
}
|
||||
} else {
|
||||
return Promise.resolve();
|
||||
}
|
||||
},
|
||||
}),
|
||||
]}
|
||||
>
|
||||
<DateTimePicker
|
||||
|
||||
@@ -16,7 +16,7 @@ import LoadingSpinner from "../../components/loading-spinner/loading-spinner.com
|
||||
import PartnerPingComponent from "../../components/partner-ping/partner-ping.component";
|
||||
import PrintCenterModalContainer from "../../components/print-center-modal/print-center-modal.container";
|
||||
import ShopSubStatusComponent from "../../components/shop-sub-status/shop-sub-status.component";
|
||||
import TestComponent from "../../components/_test/test.component";
|
||||
import TestComponent from "../../components/_test/test.page";
|
||||
import { requestForToken } from "../../firebase/firebase.utils";
|
||||
import {
|
||||
selectBodyshop,
|
||||
@@ -32,6 +32,10 @@ const ManageRootPage = lazy(() =>
|
||||
);
|
||||
const JobsPage = lazy(() => import("../jobs/jobs.page"));
|
||||
|
||||
const CardPaymentModalContainer = lazy(() =>
|
||||
import("../../components/card-payment-modal/card-payment-modal.container.")
|
||||
);
|
||||
|
||||
const JobsDetailPage = lazy(() =>
|
||||
import("../jobs-detail/jobs-detail.page.container")
|
||||
);
|
||||
@@ -196,6 +200,8 @@ export function Manage({ match, conflict, bodyshop }) {
|
||||
>
|
||||
<PaymentModalContainer />
|
||||
|
||||
<CardPaymentModalContainer />
|
||||
|
||||
<BreadCrumbs />
|
||||
<BillEnterModalContainer />
|
||||
<JobCostingModal />
|
||||
|
||||
@@ -1,21 +1,24 @@
|
||||
import { BackTop, Layout } from "antd";
|
||||
import React, { lazy, Suspense, useEffect } from "react";
|
||||
import React, { Suspense, lazy, useEffect } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { connect } from "react-redux";
|
||||
import { Redirect, Route, Switch } from "react-router-dom";
|
||||
import { createStructuredSelector } from "reselect";
|
||||
import ErrorBoundary from "../../components/error-boundary/error-boundary.component";
|
||||
|
||||
import FeatureWrapper from "../../components/feature-wrapper/feature-wrapper.component";
|
||||
import LoadingSpinner from "../../components/loading-spinner/loading-spinner.component";
|
||||
import TechHeader from "../../components/tech-header/tech-header.component";
|
||||
import TechSider from "../../components/tech-sider/tech-sider.component";
|
||||
import { selectTechnician } from "../../redux/tech/tech.selectors";
|
||||
import FeatureWrapper from "../../components/feature-wrapper/feature-wrapper.component";
|
||||
import "./tech.page.styles.scss";
|
||||
import UpdateAlert from "../../components/update-alert/update-alert.component";
|
||||
import { selectTechnician } from "../../redux/tech/tech.selectors";
|
||||
import "./tech.page.styles.scss";
|
||||
const TimeTicketModalContainer = lazy(() =>
|
||||
import("../../components/time-ticket-modal/time-ticket-modal.container")
|
||||
);
|
||||
const EmailOverlayContainer = lazy(() =>
|
||||
import("../../components/email-overlay/email-overlay.container.jsx")
|
||||
);
|
||||
const PrintCenterModalContainer = lazy(() =>
|
||||
import("../../components/print-center-modal/print-center-modal.container")
|
||||
);
|
||||
@@ -69,6 +72,7 @@ export function TechPage({ technician, match }) {
|
||||
>
|
||||
<FeatureWrapper featureName="tech-console">
|
||||
<TimeTicketModalContainer />
|
||||
<EmailOverlayContainer />
|
||||
<PrintCenterModalContainer />
|
||||
<Switch>
|
||||
<Route
|
||||
|
||||
@@ -25,6 +25,7 @@ const INITIAL_STATE = {
|
||||
contractFinder: { ...baseModal },
|
||||
inventoryUpsert: { ...baseModal },
|
||||
ca_bc_eftTableConvert: { ...baseModal },
|
||||
cardPayment: { ...baseModal },
|
||||
};
|
||||
|
||||
const modalsReducer = (state = INITIAL_STATE, action) => {
|
||||
|
||||
@@ -79,3 +79,8 @@ export const selectCaBcEtfTableConvert = createSelector(
|
||||
[selectModals],
|
||||
(modals) => modals.ca_bc_eftTableConvert
|
||||
);
|
||||
|
||||
export const selectCardPayment = createSelector(
|
||||
[selectModals],
|
||||
(modals) => modals.cardPayment
|
||||
);
|
||||
|
||||
@@ -63,7 +63,6 @@
|
||||
"scheduledfor": "Scheduled appointment for: ",
|
||||
"severalerrorsfound": "Several jobs have issues which may prevent accurate smart scheduling. Click to expand.",
|
||||
"smartscheduling": "Smart Scheduling",
|
||||
"smspaymentreminder": "",
|
||||
"suggesteddates": "Suggested Dates"
|
||||
},
|
||||
"successes": {
|
||||
@@ -101,10 +100,11 @@
|
||||
"messages": {
|
||||
"admin_jobmarkexported": "ADMIN: Job marked as exported.",
|
||||
"admin_jobmarkforreexport": "ADMIN: Job marked for re-export.",
|
||||
"admin_jobuninvoice": "ADMIN: Job has been uninvoiced.",
|
||||
"admin_jobunvoid": "ADMIN: Job has been unvoided.",
|
||||
"billposted": "Bill with invoice number {{invoice_number}} posted.",
|
||||
"billupdated": "Bill with invoice number {{invoice_number}} updated.",
|
||||
"failedpayment": "",
|
||||
"failedpayment": "Failed payment",
|
||||
"jobassignmentchange": "Employee {{name}} assigned to {{operation}}",
|
||||
"jobassignmentremoved": "Employee assignment removed for {{operation}}",
|
||||
"jobchecklist": "Checklist type \"{{type}}\" completed. In production set to {{inproduction}}. Status set to {{status}}.",
|
||||
@@ -222,6 +222,7 @@
|
||||
"reexport": "Bill marked for re-export."
|
||||
},
|
||||
"validation": {
|
||||
"closingperiod": "This Bill Date is outside of the Closing Period.",
|
||||
"inventoryquantity": "Quantity must be greater than or equal to what has been added to inventory ({{number}}).",
|
||||
"manualinhouse": "Manual posting to the in house vendor is restricted. ",
|
||||
"unique_invoice_number": "This invoice number has already been entered for this vendor."
|
||||
@@ -261,6 +262,7 @@
|
||||
"bill_local_tax_rate": "Bill - Provincial/State Tax Rate %",
|
||||
"bill_state_tax_rate": "Bill - Provincial/State Tax Rate %",
|
||||
"city": "City",
|
||||
"closingperiod": "Closing Period",
|
||||
"country": "Country",
|
||||
"dailybodytarget": "Scoreboard - Daily Body Target",
|
||||
"dailypainttarget": "Scoreboard - Daily Paint Target",
|
||||
@@ -269,6 +271,8 @@
|
||||
"templates": "Delivery Templates"
|
||||
},
|
||||
"dms": {
|
||||
"apcontrol": "AP Control Number",
|
||||
"appostingaccount": "AP Posting Account",
|
||||
"cashierid": "Cashier ID",
|
||||
"default_journal": "Default Journal",
|
||||
"disablebillwip": "Disable bill WIP for A/P Posting",
|
||||
@@ -486,7 +490,7 @@
|
||||
"lam": "Mechanical",
|
||||
"lar": "Refinish",
|
||||
"las": "Structural",
|
||||
"lau": "Detail",
|
||||
"lau": "User Defined",
|
||||
"local_tax": "Local Tax",
|
||||
"mapa": "Paint Materials",
|
||||
"mash": "Shop Materials",
|
||||
@@ -1010,6 +1014,7 @@
|
||||
"cancel": "Cancel",
|
||||
"clear": "Clear",
|
||||
"close": "Close",
|
||||
"copied": "Copied!",
|
||||
"copylink": "Copy Link",
|
||||
"create": "Create",
|
||||
"delete": "Delete",
|
||||
@@ -1026,6 +1031,7 @@
|
||||
"saveandnew": "Save and New",
|
||||
"selectall": "Select All",
|
||||
"send": "Send",
|
||||
"sendbysms": "Send by SMS",
|
||||
"senderrortosupport": "Send Error to Support",
|
||||
"submit": "Submit",
|
||||
"tryagain": "Try Again",
|
||||
@@ -1087,6 +1093,7 @@
|
||||
"passwordsdonotmatch": "The passwords you have entered do not match.",
|
||||
"print": "Print",
|
||||
"refresh": "Refresh",
|
||||
"reports": "Reports",
|
||||
"required": "Required",
|
||||
"saturday": "Saturday",
|
||||
"search": "Search...",
|
||||
@@ -1186,26 +1193,28 @@
|
||||
},
|
||||
"job_payments": {
|
||||
"buttons": {
|
||||
"goback": "",
|
||||
"proceedtopayment": "",
|
||||
"refundpayment": ""
|
||||
"goback": "Go Back",
|
||||
"proceedtopayment": "Proceed to Payment",
|
||||
"refundpayment": "Refund Payment"
|
||||
},
|
||||
"notifications": {
|
||||
"error": {
|
||||
"description": "",
|
||||
"title": ""
|
||||
"description": "Please try again. Make sure the refund amount does not exceeds the payment amount.",
|
||||
"openingip": "Error connecting to IntelliPay service.",
|
||||
"title": "Error placing refund"
|
||||
}
|
||||
},
|
||||
"titles": {
|
||||
"amount": "",
|
||||
"dateOfPayment": "",
|
||||
"descriptions": "",
|
||||
"payer": "",
|
||||
"payername": "",
|
||||
"paymentid": "",
|
||||
"paymenttype": "",
|
||||
"refundamount": "",
|
||||
"transactionid": ""
|
||||
"amount": "Amount",
|
||||
"dateOfPayment": "Date of Payment",
|
||||
"descriptions": "Payment Details",
|
||||
"payer": "Payer",
|
||||
"payername": "Payer Name",
|
||||
"paymentid": "Payment Reference ID",
|
||||
"paymentnum": "Payment Number",
|
||||
"paymenttype": "Payment Type",
|
||||
"refundamount": "Refund Amount",
|
||||
"transactionid": "Transaction ID"
|
||||
}
|
||||
},
|
||||
"joblines": {
|
||||
@@ -1437,6 +1446,7 @@
|
||||
"date_repairstarted": "Repairs Started",
|
||||
"date_scheduled": "Scheduled",
|
||||
"date_towin": "Towed In",
|
||||
"date_void": "Void",
|
||||
"ded_amt": "Deductible",
|
||||
"ded_note": "Deductible Note",
|
||||
"ded_status": "Deductible Status",
|
||||
@@ -1452,6 +1462,7 @@
|
||||
"cost_dms_acctnumber": "Cost DMS Acct #",
|
||||
"dms_make": "DMS Make",
|
||||
"dms_model": "DMS Model",
|
||||
"dms_unsold": "New, Unsold Vehicle",
|
||||
"dms_wip_acctnumber": "Cost WIP DMS Acct #",
|
||||
"id": "DMS ID",
|
||||
"inservicedate": "In Service Date",
|
||||
@@ -1683,6 +1694,7 @@
|
||||
"checklists": "Checklists",
|
||||
"closeconfirm": "Are you sure you want to close this job? This cannot be easily undone.",
|
||||
"closejob": "Close Job {{ro_number}}",
|
||||
"closingperiod": "This Invoice Date is outside of the Closing Period.",
|
||||
"contracts": "CC Contracts",
|
||||
"convertedtolabor": "Lines Converted to Labor",
|
||||
"cost": "Cost",
|
||||
@@ -1918,7 +1930,7 @@
|
||||
"customers": "Customers",
|
||||
"dashboard": "Dashboard",
|
||||
"enterbills": "Enter Bills",
|
||||
"entercardpayment": "",
|
||||
"entercardpayment": "New Card Charge",
|
||||
"enterpayment": "Enter Payments",
|
||||
"entertimeticket": "Enter Time Tickets",
|
||||
"export": "Export",
|
||||
@@ -1930,7 +1942,6 @@
|
||||
"newjob": "Create New Job",
|
||||
"owners": "Owners",
|
||||
"parts-queue": "Parts Queue",
|
||||
"paymentremindersms": "",
|
||||
"phonebook": "Phonebook",
|
||||
"productionboard": "Production Board - Visual",
|
||||
"productionlist": "Production Board - List",
|
||||
@@ -2192,9 +2203,13 @@
|
||||
}
|
||||
},
|
||||
"payments": {
|
||||
"actions": {
|
||||
"generatepaymentlink": "Generate Payment Link"
|
||||
},
|
||||
"errors": {
|
||||
"exporting": "Error exporting payment(s). {{error}}",
|
||||
"exporting-partner": "Error exporting to partner. Please check the partner interaction log for more errors."
|
||||
"exporting-partner": "Error exporting to partner. Please check the partner interaction log for more errors.",
|
||||
"inserting": "Error inserting payment. {{error}}"
|
||||
},
|
||||
"fields": {
|
||||
"amount": "Amount",
|
||||
@@ -2217,17 +2232,18 @@
|
||||
"external": "External",
|
||||
"findermodal": "ICBC Payment Finder",
|
||||
"insurance": "Insurance",
|
||||
"markexported": "Mark Exported",
|
||||
"markforreexport": "Mark for Re-export",
|
||||
"new": "New Payment",
|
||||
"signup": "Please contact support to sign up for electronic payments.",
|
||||
"smspaymentreminder": "This is {{shopname}} reminding you about your balance of {{amount}}. To pay, click the following link {{payment_link}}.",
|
||||
"title": "Payments",
|
||||
"totalpayments": "Total Payments",
|
||||
"markexported": "Mark Exported",
|
||||
"markforreexport": "Mark for Re-export"
|
||||
"totalpayments": "Total Payments"
|
||||
},
|
||||
"successes": {
|
||||
"exported": "Payment(s) exported successfully.",
|
||||
"markreexported": "Payment marked for re-export successfully",
|
||||
"markexported": "Payment(s) marked exported.",
|
||||
"markreexported": "Payment marked for re-export successfully",
|
||||
"payment": "Payment created successfully. ",
|
||||
"stripe": "Credit card transaction charged successfully."
|
||||
}
|
||||
@@ -2403,6 +2419,7 @@
|
||||
"jobs": {
|
||||
"individual_job_note": "Job Note RO: {{ro_number}}",
|
||||
"parts_order": "Parts Order PO: {{ro_number}} - {{name}}",
|
||||
"parts_return_slip": "Parts Return PO: {{ro_number}} - {{name}}",
|
||||
"sublet_order": "Sublet Order PO: {{ro_number}} - {{name}}"
|
||||
}
|
||||
},
|
||||
@@ -2570,6 +2587,7 @@
|
||||
"jobs_completed_not_invoiced": "Jobs Completed not Invoiced",
|
||||
"jobs_invoiced_not_exported": "Jobs Invoiced not Exported",
|
||||
"jobs_reconcile": "Parts/Sublet/Labor Reconciliation",
|
||||
"jobs_scheduled_completion": "Jobs Scheduled Completion",
|
||||
"lag_time": "Lag Time",
|
||||
"open_orders": "Open Orders by Date",
|
||||
"open_orders_csr": "Open Orders by CSR",
|
||||
@@ -2618,6 +2636,7 @@
|
||||
"timetickets_summary": "Time Tickets Summary",
|
||||
"unclaimed_hrs": "Unclaimed Hours",
|
||||
"void_ros": "Void ROs",
|
||||
"work_in_progress_jobs": "Work in Progress - Jobs",
|
||||
"work_in_progress_labour": "Work in Progress - Labor",
|
||||
"work_in_progress_payables": "Work in Progress - Payables"
|
||||
}
|
||||
@@ -2626,8 +2645,8 @@
|
||||
"labels": {
|
||||
"atssummary": "ATS Summary",
|
||||
"employeevacation": "Employee Vacations",
|
||||
"ins_co_nm_filter": "Filter by Insurance Company",
|
||||
"estimators": "Filter by Writer/Customer Rep.",
|
||||
"ins_co_nm_filter": "Filter by Insurance Company",
|
||||
"intake": "Intake Events",
|
||||
"manual": "Manual Events",
|
||||
"manualevent": "Add Manual Event"
|
||||
@@ -2719,6 +2738,7 @@
|
||||
"clockon": "Clocked In",
|
||||
"committed": "",
|
||||
"cost_center": "Cost Center",
|
||||
"created_by": "Created By",
|
||||
"date": "Ticket Date",
|
||||
"efficiency": "Efficiency",
|
||||
"employee": "Employee",
|
||||
|
||||
@@ -63,7 +63,6 @@
|
||||
"scheduledfor": "Cita programada para:",
|
||||
"severalerrorsfound": "",
|
||||
"smartscheduling": "",
|
||||
"smspaymentreminder": "",
|
||||
"suggesteddates": ""
|
||||
},
|
||||
"successes": {
|
||||
@@ -101,6 +100,7 @@
|
||||
"messages": {
|
||||
"admin_jobmarkexported": "",
|
||||
"admin_jobmarkforreexport": "",
|
||||
"admin_jobuninvoice": "",
|
||||
"admin_jobunvoid": "",
|
||||
"billposted": "",
|
||||
"billupdated": "",
|
||||
@@ -222,6 +222,7 @@
|
||||
"reexport": ""
|
||||
},
|
||||
"validation": {
|
||||
"closingperiod": "",
|
||||
"inventoryquantity": "",
|
||||
"manualinhouse": "",
|
||||
"unique_invoice_number": ""
|
||||
@@ -261,6 +262,7 @@
|
||||
"bill_local_tax_rate": "",
|
||||
"bill_state_tax_rate": "",
|
||||
"city": "",
|
||||
"closingperiod": "",
|
||||
"country": "",
|
||||
"dailybodytarget": "",
|
||||
"dailypainttarget": "",
|
||||
@@ -269,6 +271,8 @@
|
||||
"templates": ""
|
||||
},
|
||||
"dms": {
|
||||
"apcontrol": "",
|
||||
"appostingaccount": "",
|
||||
"cashierid": "",
|
||||
"default_journal": "",
|
||||
"disablebillwip": "",
|
||||
@@ -1010,6 +1014,7 @@
|
||||
"cancel": "",
|
||||
"clear": "",
|
||||
"close": "",
|
||||
"copied": "",
|
||||
"copylink": "",
|
||||
"create": "",
|
||||
"delete": "Borrar",
|
||||
@@ -1026,6 +1031,7 @@
|
||||
"saveandnew": "",
|
||||
"selectall": "",
|
||||
"send": "",
|
||||
"sendbysms": "",
|
||||
"senderrortosupport": "",
|
||||
"submit": "",
|
||||
"tryagain": "",
|
||||
@@ -1087,6 +1093,7 @@
|
||||
"passwordsdonotmatch": "",
|
||||
"print": "",
|
||||
"refresh": "",
|
||||
"reports": "",
|
||||
"required": "",
|
||||
"saturday": "",
|
||||
"search": "Buscar...",
|
||||
@@ -1193,6 +1200,7 @@
|
||||
"notifications": {
|
||||
"error": {
|
||||
"description": "",
|
||||
"openingip": "",
|
||||
"title": ""
|
||||
}
|
||||
},
|
||||
@@ -1203,6 +1211,7 @@
|
||||
"payer": "",
|
||||
"payername": "",
|
||||
"paymentid": "",
|
||||
"paymentnum": "",
|
||||
"paymenttype": "",
|
||||
"refundamount": "",
|
||||
"transactionid": ""
|
||||
@@ -1437,6 +1446,7 @@
|
||||
"date_repairstarted": "",
|
||||
"date_scheduled": "Programado",
|
||||
"date_towin": "",
|
||||
"date_void": "",
|
||||
"ded_amt": "Deducible",
|
||||
"ded_note": "",
|
||||
"ded_status": "Estado deducible",
|
||||
@@ -1452,6 +1462,7 @@
|
||||
"cost_dms_acctnumber": "",
|
||||
"dms_make": "",
|
||||
"dms_model": "",
|
||||
"dms_unsold": "",
|
||||
"dms_wip_acctnumber": "",
|
||||
"id": "",
|
||||
"inservicedate": "",
|
||||
@@ -1683,6 +1694,7 @@
|
||||
"checklists": "",
|
||||
"closeconfirm": "",
|
||||
"closejob": "",
|
||||
"closingperiod": "",
|
||||
"contracts": "",
|
||||
"convertedtolabor": "",
|
||||
"cost": "",
|
||||
@@ -1930,7 +1942,6 @@
|
||||
"newjob": "",
|
||||
"owners": "propietarios",
|
||||
"parts-queue": "",
|
||||
"paymentremindersms": "",
|
||||
"phonebook": "",
|
||||
"productionboard": "",
|
||||
"productionlist": "",
|
||||
@@ -2192,9 +2203,13 @@
|
||||
}
|
||||
},
|
||||
"payments": {
|
||||
"actions": {
|
||||
"generatepaymentlink": ""
|
||||
},
|
||||
"errors": {
|
||||
"exporting": "",
|
||||
"exporting-partner": ""
|
||||
"exporting-partner": "",
|
||||
"inserting": ""
|
||||
},
|
||||
"fields": {
|
||||
"amount": "",
|
||||
@@ -2217,14 +2232,18 @@
|
||||
"external": "",
|
||||
"findermodal": "",
|
||||
"insurance": "",
|
||||
"markexported": "",
|
||||
"markforreexport": "",
|
||||
"new": "",
|
||||
"signup": "",
|
||||
"smspaymentreminder": "",
|
||||
"title": "",
|
||||
"totalpayments": ""
|
||||
},
|
||||
"successes": {
|
||||
"exported": "",
|
||||
"markexported": "",
|
||||
"markreexported": "",
|
||||
"payment": "",
|
||||
"stripe": ""
|
||||
}
|
||||
@@ -2400,6 +2419,7 @@
|
||||
"jobs": {
|
||||
"individual_job_note": "",
|
||||
"parts_order": "",
|
||||
"parts_return_slip": "",
|
||||
"sublet_order": ""
|
||||
}
|
||||
},
|
||||
@@ -2567,6 +2587,7 @@
|
||||
"jobs_completed_not_invoiced": "",
|
||||
"jobs_invoiced_not_exported": "",
|
||||
"jobs_reconcile": "",
|
||||
"jobs_scheduled_completion": "",
|
||||
"lag_time": "",
|
||||
"open_orders": "",
|
||||
"open_orders_csr": "",
|
||||
@@ -2615,6 +2636,7 @@
|
||||
"timetickets_summary": "",
|
||||
"unclaimed_hrs": "",
|
||||
"void_ros": "",
|
||||
"work_in_progress_jobs": "",
|
||||
"work_in_progress_labour": "",
|
||||
"work_in_progress_payables": ""
|
||||
}
|
||||
@@ -2623,6 +2645,7 @@
|
||||
"labels": {
|
||||
"atssummary": "",
|
||||
"employeevacation": "",
|
||||
"estimators": "",
|
||||
"ins_co_nm_filter": "",
|
||||
"intake": "",
|
||||
"manual": "",
|
||||
@@ -2715,6 +2738,7 @@
|
||||
"clockon": "",
|
||||
"committed": "",
|
||||
"cost_center": "",
|
||||
"created_by": "",
|
||||
"date": "",
|
||||
"efficiency": "",
|
||||
"employee": "",
|
||||
|
||||
@@ -63,7 +63,6 @@
|
||||
"scheduledfor": "Rendez-vous prévu pour:",
|
||||
"severalerrorsfound": "",
|
||||
"smartscheduling": "",
|
||||
"smspaymentreminder": "",
|
||||
"suggesteddates": ""
|
||||
},
|
||||
"successes": {
|
||||
@@ -101,6 +100,7 @@
|
||||
"messages": {
|
||||
"admin_jobmarkexported": "",
|
||||
"admin_jobmarkforreexport": "",
|
||||
"admin_jobuninvoice": "",
|
||||
"admin_jobunvoid": "",
|
||||
"billposted": "",
|
||||
"billupdated": "",
|
||||
@@ -222,6 +222,7 @@
|
||||
"reexport": ""
|
||||
},
|
||||
"validation": {
|
||||
"closingperiod": "",
|
||||
"inventoryquantity": "",
|
||||
"manualinhouse": "",
|
||||
"unique_invoice_number": ""
|
||||
@@ -261,6 +262,7 @@
|
||||
"bill_local_tax_rate": "",
|
||||
"bill_state_tax_rate": "",
|
||||
"city": "",
|
||||
"closingperiod": "",
|
||||
"country": "",
|
||||
"dailybodytarget": "",
|
||||
"dailypainttarget": "",
|
||||
@@ -269,6 +271,8 @@
|
||||
"templates": ""
|
||||
},
|
||||
"dms": {
|
||||
"apcontrol": "",
|
||||
"appostingaccount": "",
|
||||
"cashierid": "",
|
||||
"default_journal": "",
|
||||
"disablebillwip": "",
|
||||
@@ -1010,6 +1014,7 @@
|
||||
"cancel": "",
|
||||
"clear": "",
|
||||
"close": "",
|
||||
"copied": "",
|
||||
"copylink": "",
|
||||
"create": "",
|
||||
"delete": "Effacer",
|
||||
@@ -1026,6 +1031,7 @@
|
||||
"saveandnew": "",
|
||||
"selectall": "",
|
||||
"send": "",
|
||||
"sendbysms": "",
|
||||
"senderrortosupport": "",
|
||||
"submit": "",
|
||||
"tryagain": "",
|
||||
@@ -1087,6 +1093,7 @@
|
||||
"passwordsdonotmatch": "",
|
||||
"print": "",
|
||||
"refresh": "",
|
||||
"reports": "",
|
||||
"required": "",
|
||||
"saturday": "",
|
||||
"search": "Chercher...",
|
||||
@@ -1193,6 +1200,7 @@
|
||||
"notifications": {
|
||||
"error": {
|
||||
"description": "",
|
||||
"openingip": "",
|
||||
"title": ""
|
||||
}
|
||||
},
|
||||
@@ -1203,6 +1211,7 @@
|
||||
"payer": "",
|
||||
"payername": "",
|
||||
"paymentid": "",
|
||||
"paymentnum": "",
|
||||
"paymenttype": "",
|
||||
"refundamount": "",
|
||||
"transactionid": ""
|
||||
@@ -1437,6 +1446,7 @@
|
||||
"date_repairstarted": "",
|
||||
"date_scheduled": "Prévu",
|
||||
"date_towin": "",
|
||||
"date_void": "",
|
||||
"ded_amt": "Déductible",
|
||||
"ded_note": "",
|
||||
"ded_status": "Statut de franchise",
|
||||
@@ -1452,6 +1462,7 @@
|
||||
"cost_dms_acctnumber": "",
|
||||
"dms_make": "",
|
||||
"dms_model": "",
|
||||
"dms_unsold": "",
|
||||
"dms_wip_acctnumber": "",
|
||||
"id": "",
|
||||
"inservicedate": "",
|
||||
@@ -1683,6 +1694,7 @@
|
||||
"checklists": "",
|
||||
"closeconfirm": "",
|
||||
"closejob": "",
|
||||
"closingperiod": "",
|
||||
"contracts": "",
|
||||
"convertedtolabor": "",
|
||||
"cost": "",
|
||||
@@ -1930,7 +1942,6 @@
|
||||
"newjob": "",
|
||||
"owners": "Propriétaires",
|
||||
"parts-queue": "",
|
||||
"paymentremindersms": "",
|
||||
"phonebook": "",
|
||||
"productionboard": "",
|
||||
"productionlist": "",
|
||||
@@ -2192,9 +2203,13 @@
|
||||
}
|
||||
},
|
||||
"payments": {
|
||||
"actions": {
|
||||
"generatepaymentlink": ""
|
||||
},
|
||||
"errors": {
|
||||
"exporting": "",
|
||||
"exporting-partner": ""
|
||||
"exporting-partner": "",
|
||||
"inserting": ""
|
||||
},
|
||||
"fields": {
|
||||
"amount": "",
|
||||
@@ -2217,14 +2232,18 @@
|
||||
"external": "",
|
||||
"findermodal": "",
|
||||
"insurance": "",
|
||||
"markexported": "",
|
||||
"markforreexport": "",
|
||||
"new": "",
|
||||
"signup": "",
|
||||
"smspaymentreminder": "",
|
||||
"title": "",
|
||||
"totalpayments": ""
|
||||
},
|
||||
"successes": {
|
||||
"exported": "",
|
||||
"markexported": "",
|
||||
"markreexported": "",
|
||||
"payment": "",
|
||||
"stripe": ""
|
||||
}
|
||||
@@ -2400,6 +2419,7 @@
|
||||
"jobs": {
|
||||
"individual_job_note": "",
|
||||
"parts_order": "",
|
||||
"parts_return_slip": "",
|
||||
"sublet_order": ""
|
||||
}
|
||||
},
|
||||
@@ -2567,6 +2587,7 @@
|
||||
"jobs_completed_not_invoiced": "",
|
||||
"jobs_invoiced_not_exported": "",
|
||||
"jobs_reconcile": "",
|
||||
"jobs_scheduled_completion": "",
|
||||
"lag_time": "",
|
||||
"open_orders": "",
|
||||
"open_orders_csr": "",
|
||||
@@ -2615,6 +2636,7 @@
|
||||
"timetickets_summary": "",
|
||||
"unclaimed_hrs": "",
|
||||
"void_ros": "",
|
||||
"work_in_progress_jobs": "",
|
||||
"work_in_progress_labour": "",
|
||||
"work_in_progress_payables": ""
|
||||
}
|
||||
@@ -2623,6 +2645,7 @@
|
||||
"labels": {
|
||||
"atssummary": "",
|
||||
"employeevacation": "",
|
||||
"estimators": "",
|
||||
"ins_co_nm_filter": "",
|
||||
"intake": "",
|
||||
"manual": "",
|
||||
@@ -2715,6 +2738,7 @@
|
||||
"clockon": "",
|
||||
"committed": "",
|
||||
"cost_center": "",
|
||||
"created_by": "",
|
||||
"date": "",
|
||||
"efficiency": "",
|
||||
"employee": "",
|
||||
|
||||
@@ -36,11 +36,12 @@ const AuditTrailMapping = {
|
||||
jobnoteupdated: () => i18n.t("audit_trail.messages.jobnoteupdated"),
|
||||
jobnotedeleted: () => i18n.t("audit_trail.messages.jobnotedeleted"),
|
||||
admin_jobunvoid: () => i18n.t("audit_trail.messages.admin_jobunvoid"),
|
||||
admin_jobuninvoice: () => i18n.t("audit_trail.messages.admin_jobuninvoice"),
|
||||
admin_jobmarkforreexport: () =>
|
||||
i18n.t("audit_trail.messages.admin_jobmarkforreexport"),
|
||||
admin_jobmarkexported: () =>
|
||||
i18n.t("audit_trail.messages.admin_jobmarkexported"),
|
||||
|
||||
failedpayment: () => i18n.t("audit_trail.messages.failedpayment"),
|
||||
};
|
||||
|
||||
export default AuditTrailMapping;
|
||||
|
||||
@@ -606,7 +606,14 @@ export const TemplateList = (type, context) => {
|
||||
},
|
||||
parts_return_slip: {
|
||||
title: i18n.t("printcenter.jobs.parts_return_slip"),
|
||||
subject: i18n.t("printcenter.jobs.parts_return_slip"),
|
||||
subject: i18n.t("printcenter.subjects.jobs.parts_return_slip", {
|
||||
ro_number: context && context.job && context.job.ro_number,
|
||||
name: (
|
||||
(context && context.job && context.job.ownr_ln) ||
|
||||
(context && context.job && context.job.ownr_co_nm) ||
|
||||
""
|
||||
).trim(),
|
||||
}),
|
||||
description: "",
|
||||
key: "parts_return_slip",
|
||||
disabled: false,
|
||||
@@ -1033,7 +1040,7 @@ export const TemplateList = (type, context) => {
|
||||
disabled: false,
|
||||
rangeFilter: {
|
||||
object: i18n.t("reportcenter.labels.objects.jobs"),
|
||||
field: i18n.t("jobs.fields.date_open"),
|
||||
field: i18n.t("jobs.fields.date_invoiced"),
|
||||
},
|
||||
group: "jobs",
|
||||
},
|
||||
@@ -1128,6 +1135,10 @@ export const TemplateList = (type, context) => {
|
||||
key: "timetickets_employee",
|
||||
idtype: "employee",
|
||||
disabled: false,
|
||||
rangeFilter: {
|
||||
object: i18n.t("reportcenter.labels.objects.timetickets"),
|
||||
field: i18n.t("timetickets.fields.date"),
|
||||
},
|
||||
group: "payroll",
|
||||
},
|
||||
attendance_detail: {
|
||||
@@ -1237,7 +1248,7 @@ export const TemplateList = (type, context) => {
|
||||
disabled: false,
|
||||
rangeFilter: {
|
||||
object: i18n.t("reportcenter.labels.objects.jobs"),
|
||||
field: i18n.t("jobs.fields.date_open"),
|
||||
field: i18n.t("jobs.fields.date_void"),
|
||||
},
|
||||
group: "sales",
|
||||
},
|
||||
@@ -1540,6 +1551,19 @@ export const TemplateList = (type, context) => {
|
||||
},
|
||||
group: "payroll",
|
||||
},
|
||||
work_in_progress_jobs_excel: {
|
||||
title: i18n.t("reportcenter.templates.work_in_progress_jobs"),
|
||||
subject: i18n.t("reportcenter.templates.work_in_progress_jobs"),
|
||||
key: "work_in_progress_jobs_excel",
|
||||
//idtype: "vendor",
|
||||
reporttype: "excel",
|
||||
disabled: false,
|
||||
rangeFilter: {
|
||||
object: i18n.t("reportcenter.labels.objects.jobs"),
|
||||
field: i18n.t("jobs.fields.date_open"),
|
||||
},
|
||||
group: "jobs",
|
||||
},
|
||||
work_in_progress_labour: {
|
||||
title: i18n.t("reportcenter.templates.work_in_progress_labour"),
|
||||
description: "",
|
||||
@@ -1897,6 +1921,18 @@ export const TemplateList = (type, context) => {
|
||||
},
|
||||
group: "sales",
|
||||
},
|
||||
jobs_scheduled_completion: {
|
||||
title: i18n.t("reportcenter.templates.jobs_scheduled_completion"),
|
||||
subject: i18n.t("reportcenter.templates.jobs_scheduled_completion"),
|
||||
key: "jobs_scheduled_completion",
|
||||
//idtype: "vendor",
|
||||
disabled: false,
|
||||
rangeFilter: {
|
||||
object: i18n.t("reportcenter.labels.objects.jobs"),
|
||||
field: i18n.t("jobs.fields.scheduled_completion"),
|
||||
},
|
||||
group: "jobs",
|
||||
},
|
||||
}
|
||||
: {}),
|
||||
...(!type || type === "courtesycarcontract"
|
||||
|
||||
@@ -2092,6 +2092,13 @@
|
||||
table:
|
||||
name: employee_team_members
|
||||
schema: public
|
||||
- name: joblines
|
||||
using:
|
||||
foreign_key_constraint_on:
|
||||
column: assigned_team
|
||||
table:
|
||||
name: joblines
|
||||
schema: public
|
||||
insert_permissions:
|
||||
- role: user
|
||||
permission:
|
||||
@@ -2665,6 +2672,9 @@
|
||||
name: joblines
|
||||
schema: public
|
||||
object_relationships:
|
||||
- name: employee_team
|
||||
using:
|
||||
foreign_key_constraint_on: assigned_team
|
||||
- name: job
|
||||
using:
|
||||
foreign_key_constraint_on: jobid
|
||||
@@ -2726,6 +2736,7 @@
|
||||
- alt_part_i
|
||||
- alt_partm
|
||||
- alt_partno
|
||||
- assigned_team
|
||||
- bett_amt
|
||||
- bett_pctg
|
||||
- bett_tax
|
||||
@@ -2734,6 +2745,7 @@
|
||||
- convertedtolbr
|
||||
- convertedtolbr_data
|
||||
- created_at
|
||||
- critical
|
||||
- db_hrs
|
||||
- db_price
|
||||
- db_ref
|
||||
@@ -2793,6 +2805,7 @@
|
||||
- alt_part_i
|
||||
- alt_partm
|
||||
- alt_partno
|
||||
- assigned_team
|
||||
- bett_amt
|
||||
- bett_pctg
|
||||
- bett_tax
|
||||
@@ -2872,6 +2885,7 @@
|
||||
- alt_part_i
|
||||
- alt_partm
|
||||
- alt_partno
|
||||
- assigned_team
|
||||
- bett_amt
|
||||
- bett_pctg
|
||||
- bett_tax
|
||||
@@ -3259,6 +3273,8 @@
|
||||
- ca_gst_registrant
|
||||
- cat_no
|
||||
- category
|
||||
- cieca_pfl
|
||||
- cieca_pft
|
||||
- cieca_stl
|
||||
- cieca_ttl
|
||||
- ciecaid
|
||||
@@ -3286,6 +3302,7 @@
|
||||
- clm_total
|
||||
- clm_zip
|
||||
- comment
|
||||
- completed_tasks
|
||||
- converted
|
||||
- created_at
|
||||
- cust_pr
|
||||
@@ -3299,6 +3316,7 @@
|
||||
- date_repairstarted
|
||||
- date_scheduled
|
||||
- date_towin
|
||||
- date_void
|
||||
- ded_amt
|
||||
- ded_note
|
||||
- ded_status
|
||||
@@ -3523,6 +3541,8 @@
|
||||
- ca_gst_registrant
|
||||
- cat_no
|
||||
- category
|
||||
- cieca_pfl
|
||||
- cieca_pft
|
||||
- cieca_stl
|
||||
- cieca_ttl
|
||||
- ciecaid
|
||||
@@ -3550,6 +3570,7 @@
|
||||
- clm_total
|
||||
- clm_zip
|
||||
- comment
|
||||
- completed_tasks
|
||||
- converted
|
||||
- created_at
|
||||
- cust_pr
|
||||
@@ -3563,6 +3584,7 @@
|
||||
- date_repairstarted
|
||||
- date_scheduled
|
||||
- date_towin
|
||||
- date_void
|
||||
- ded_amt
|
||||
- ded_note
|
||||
- ded_status
|
||||
@@ -3798,6 +3820,8 @@
|
||||
- ca_gst_registrant
|
||||
- cat_no
|
||||
- category
|
||||
- cieca_pfl
|
||||
- cieca_pft
|
||||
- cieca_stl
|
||||
- cieca_ttl
|
||||
- ciecaid
|
||||
@@ -3825,6 +3849,7 @@
|
||||
- clm_total
|
||||
- clm_zip
|
||||
- comment
|
||||
- completed_tasks
|
||||
- converted
|
||||
- created_at
|
||||
- cust_pr
|
||||
@@ -3838,6 +3863,7 @@
|
||||
- date_repairstarted
|
||||
- date_scheduled
|
||||
- date_towin
|
||||
- date_void
|
||||
- ded_amt
|
||||
- ded_note
|
||||
- ded_status
|
||||
@@ -4629,6 +4655,7 @@
|
||||
_eq: X-Hasura-User-Id
|
||||
- active:
|
||||
_eq: true
|
||||
allow_aggregations: true
|
||||
update_permissions:
|
||||
- role: user
|
||||
permission:
|
||||
@@ -4706,6 +4733,7 @@
|
||||
_eq: X-Hasura-User-Id
|
||||
- active:
|
||||
_eq: true
|
||||
allow_aggregations: true
|
||||
update_permissions:
|
||||
- role: user
|
||||
permission:
|
||||
@@ -5534,6 +5562,7 @@
|
||||
- committed_at
|
||||
- cost_center
|
||||
- created_at
|
||||
- created_by
|
||||
- date
|
||||
- employeeid
|
||||
- flat_rate
|
||||
@@ -5542,6 +5571,7 @@
|
||||
- memo
|
||||
- productivehrs
|
||||
- rate
|
||||
- task_name
|
||||
- ttapprovalqueueid
|
||||
- updated_at
|
||||
select_permissions:
|
||||
@@ -5557,6 +5587,7 @@
|
||||
- committed_at
|
||||
- cost_center
|
||||
- created_at
|
||||
- created_by
|
||||
- date
|
||||
- employeeid
|
||||
- flat_rate
|
||||
@@ -5565,6 +5596,7 @@
|
||||
- memo
|
||||
- productivehrs
|
||||
- rate
|
||||
- task_name
|
||||
- ttapprovalqueueid
|
||||
- updated_at
|
||||
filter:
|
||||
@@ -5589,6 +5621,7 @@
|
||||
- committed_at
|
||||
- cost_center
|
||||
- created_at
|
||||
- created_by
|
||||
- date
|
||||
- employeeid
|
||||
- flat_rate
|
||||
@@ -5597,6 +5630,7 @@
|
||||
- memo
|
||||
- productivehrs
|
||||
- rate
|
||||
- task_name
|
||||
- ttapprovalqueueid
|
||||
- updated_at
|
||||
filter:
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
-- Could not auto-generate a down migration.
|
||||
-- Please write an appropriate down migration for the SQL below:
|
||||
-- alter table "public"."joblines" add column "assigned_team" uuid
|
||||
-- null;
|
||||
@@ -0,0 +1,2 @@
|
||||
alter table "public"."joblines" add column "assigned_team" uuid
|
||||
null;
|
||||
@@ -0,0 +1 @@
|
||||
alter table "public"."joblines" drop constraint "joblines_assigned_team_fkey";
|
||||
@@ -0,0 +1,5 @@
|
||||
alter table "public"."joblines"
|
||||
add constraint "joblines_assigned_team_fkey"
|
||||
foreign key ("assigned_team")
|
||||
references "public"."employee_teams"
|
||||
("id") on update restrict on delete restrict;
|
||||
@@ -0,0 +1,4 @@
|
||||
-- Could not auto-generate a down migration.
|
||||
-- Please write an appropriate down migration for the SQL below:
|
||||
-- alter table "public"."jobs" add column "completed_tasks" jsonb
|
||||
-- null default jsonb_build_array();
|
||||
@@ -0,0 +1,2 @@
|
||||
alter table "public"."jobs" add column "completed_tasks" jsonb
|
||||
null default jsonb_build_array();
|
||||
@@ -0,0 +1,4 @@
|
||||
-- Could not auto-generate a down migration.
|
||||
-- Please write an appropriate down migration for the SQL below:
|
||||
-- alter table "public"."jobs" add column "void_date" Timestamp
|
||||
-- null;
|
||||
@@ -0,0 +1,2 @@
|
||||
alter table "public"."jobs" add column "void_date" Timestamp
|
||||
null;
|
||||
@@ -0,0 +1 @@
|
||||
ALTER TABLE "public"."jobs" ALTER COLUMN "void_date" TYPE timestamp without time zone;
|
||||
@@ -0,0 +1 @@
|
||||
ALTER TABLE "public"."jobs" ALTER COLUMN "void_date" TYPE timestamptz;
|
||||
@@ -0,0 +1 @@
|
||||
alter table "public"."jobs" rename column "date_void" to "void_date";
|
||||
@@ -0,0 +1 @@
|
||||
alter table "public"."jobs" rename column "void_date" to "date_void";
|
||||
@@ -0,0 +1,4 @@
|
||||
-- Could not auto-generate a down migration.
|
||||
-- Please write an appropriate down migration for the SQL below:
|
||||
-- alter table "public"."timetickets" add column "task_name" text
|
||||
-- null;
|
||||
@@ -0,0 +1,2 @@
|
||||
alter table "public"."timetickets" add column "task_name" text
|
||||
null;
|
||||
@@ -0,0 +1 @@
|
||||
DROP INDEX IF EXISTS "public"."parts_dispatch_employeeid";
|
||||
@@ -0,0 +1,2 @@
|
||||
CREATE INDEX "parts_dispatch_employeeid" on
|
||||
"public"."parts_dispatch" using btree ("employeeid");
|
||||
@@ -0,0 +1 @@
|
||||
DROP INDEX IF EXISTS "public"."parts_dispatch_dispatchid";
|
||||
@@ -0,0 +1,2 @@
|
||||
CREATE INDEX "parts_dispatch_dispatchid" on
|
||||
"public"."parts_dispatch_lines" using btree ("partsdispatchid");
|
||||
@@ -0,0 +1 @@
|
||||
DROP INDEX IF EXISTS "public"."parts_dispatch_line_accepted_at";
|
||||
@@ -0,0 +1,2 @@
|
||||
CREATE INDEX "parts_dispatch_line_accepted_at" on
|
||||
"public"."parts_dispatch_lines" using btree ("accepted_at");
|
||||
@@ -0,0 +1,4 @@
|
||||
-- Could not auto-generate a down migration.
|
||||
-- Please write an appropriate down migration for the SQL below:
|
||||
-- alter table "public"."timetickets" add column "created_by" text
|
||||
-- null;
|
||||
@@ -0,0 +1,2 @@
|
||||
alter table "public"."timetickets" add column "created_by" text
|
||||
null;
|
||||
@@ -0,0 +1,4 @@
|
||||
-- Could not auto-generate a down migration.
|
||||
-- Please write an appropriate down migration for the SQL below:
|
||||
-- alter table "public"."jobs" add column "cieca_pfl" jsonb
|
||||
-- null default jsonb_build_object();
|
||||
@@ -0,0 +1,2 @@
|
||||
alter table "public"."jobs" add column "cieca_pfl" jsonb
|
||||
null default jsonb_build_object();
|
||||
@@ -0,0 +1,4 @@
|
||||
-- Could not auto-generate a down migration.
|
||||
-- Please write an appropriate down migration for the SQL below:
|
||||
-- alter table "public"."bodyshops" add column "claimscorpid" text
|
||||
-- null;
|
||||
@@ -0,0 +1,2 @@
|
||||
alter table "public"."bodyshops" add column "claimscorpid" text
|
||||
null;
|
||||
@@ -0,0 +1,4 @@
|
||||
-- Could not auto-generate a down migration.
|
||||
-- Please write an appropriate down migration for the SQL below:
|
||||
-- alter table "public"."jobs" add column "cieca_pft" jsonb
|
||||
-- null default jsonb_build_object();
|
||||
@@ -0,0 +1,2 @@
|
||||
alter table "public"."jobs" add column "cieca_pft" jsonb
|
||||
null default jsonb_build_object();
|
||||
11095
package-lock.json
generated
11095
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user