Compare commits
174 Commits
release/20
...
release/20
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ef18cf0718 | ||
|
|
e98f9763fd | ||
|
|
46abf01366 | ||
|
|
45ab7543d5 | ||
|
|
fed16efd10 | ||
|
|
49bf461c36 | ||
|
|
696781c857 | ||
|
|
5540872f62 | ||
|
|
69690f0184 | ||
|
|
f11a4c93ac | ||
|
|
9eed33b5f2 | ||
|
|
55b7715e1c | ||
|
|
c129b5ba8c | ||
|
|
efc2844d99 | ||
|
|
ab38f85d38 | ||
|
|
81eccba393 | ||
|
|
b3b49fd4ca | ||
|
|
b7a08db4e7 | ||
|
|
2d63c3d576 | ||
|
|
5f4a2392af | ||
|
|
f6fe8be7c4 | ||
|
|
89af6d23e8 | ||
|
|
7e1431d65e | ||
|
|
d50e845ba0 | ||
|
|
6796c35e5b | ||
|
|
4a68a10005 | ||
|
|
17088b3025 | ||
|
|
4e3c659b6d | ||
|
|
6afcf82cc4 | ||
|
|
f4ddb40bde | ||
|
|
6017e2172e | ||
|
|
bef9e64bf8 | ||
|
|
e8d95bdb68 | ||
|
|
10fb7d9d96 | ||
|
|
2ec196e664 | ||
|
|
1f38b98bfd | ||
|
|
a7c76386bc | ||
|
|
042b67c531 | ||
|
|
2e72ed3698 | ||
|
|
bc01f46388 | ||
|
|
74f791541f | ||
|
|
84c7fdba5a | ||
|
|
630fa23f6c | ||
|
|
8f58f09c8c | ||
|
|
a0922f2944 | ||
|
|
b8f001625b | ||
|
|
53394efebf | ||
|
|
d8296e1d01 | ||
|
|
2c00e5ee79 | ||
|
|
34e220dcad | ||
|
|
fa1fffd8b9 | ||
|
|
67eb430ff9 | ||
|
|
ff53355b4a | ||
|
|
331b2c517b | ||
|
|
05d9f20a66 | ||
|
|
9e3a2e920d | ||
|
|
1511b87959 | ||
|
|
9c3e4b7b83 | ||
|
|
b718f49071 | ||
|
|
e7157119ae | ||
|
|
09187bdb7f | ||
|
|
2aed392fbe | ||
|
|
1a0054a911 | ||
|
|
6e6f3d3d3e | ||
|
|
29df140680 | ||
|
|
f37c67a122 | ||
|
|
e53c9aab72 | ||
|
|
385ad06adc | ||
|
|
d1e2d943a9 | ||
|
|
90aa3557b7 | ||
|
|
8891167183 | ||
|
|
6631e645df | ||
|
|
b7f202969b | ||
|
|
1dc3353ecc | ||
|
|
b1a3f1a7b8 | ||
|
|
89f3a26635 | ||
|
|
506fe9b1af | ||
|
|
37c898d3ce | ||
|
|
e4eac5714a | ||
|
|
9d4a59ca16 | ||
|
|
8ec524061a | ||
|
|
434ed46b5a | ||
|
|
3ece5e0ba2 | ||
|
|
52a383ffb7 | ||
|
|
8ad1d5929a | ||
|
|
445c01499b | ||
|
|
0b05be841d | ||
|
|
6bcb5f2af5 | ||
|
|
7482751c5b | ||
|
|
7a025fff42 | ||
|
|
602fe36638 | ||
|
|
da08fc74f1 | ||
|
|
14af45baf0 | ||
|
|
420a88c505 | ||
|
|
f3c44f8dd1 | ||
|
|
c72111e18b | ||
|
|
1ca2870912 | ||
|
|
db9744e1e5 | ||
|
|
e700095551 | ||
|
|
99196a77ed | ||
|
|
289a8222a0 | ||
|
|
dc10f8d35b | ||
|
|
f448232fe7 | ||
|
|
4baf4b4afa | ||
|
|
1785093023 | ||
|
|
0d65f8d894 | ||
|
|
3d8c390291 | ||
|
|
f0a13856bc | ||
|
|
ad6394783d | ||
|
|
16e9843298 | ||
|
|
a4c949c376 | ||
|
|
0e0d5316b7 | ||
|
|
60cb6ee8fb | ||
|
|
7d3279d21a | ||
|
|
402d13ad99 | ||
|
|
e803f5a2d4 | ||
|
|
3989d0f1e2 | ||
|
|
161d476ab3 | ||
|
|
ce84a89cf3 | ||
|
|
1d210a9e52 | ||
|
|
660f463aea | ||
|
|
ad9f01111c | ||
|
|
1b885e4114 | ||
|
|
b56742bcb2 | ||
|
|
40d4d69a9a | ||
|
|
b54d5beb76 | ||
|
|
5cbcd440f5 | ||
|
|
1cdc34249a | ||
|
|
8bbb218777 | ||
|
|
755ac7f657 | ||
|
|
d7b884ff86 | ||
|
|
fda3620ed0 | ||
|
|
61ad9f0d58 | ||
|
|
804c8ad40a | ||
|
|
0ddf009f8f | ||
|
|
14309b5c96 | ||
|
|
57d9de469a | ||
|
|
404ade396c | ||
|
|
bdad6da6d9 | ||
|
|
e7ef3b94c1 | ||
|
|
c19c92ab7e | ||
|
|
944229bae3 | ||
|
|
661b05d9e3 | ||
|
|
4d1d471a66 | ||
|
|
3e84fbbaf4 | ||
|
|
c4e59c1a5e | ||
|
|
9ee8e9007a | ||
|
|
4d52a5c44a | ||
|
|
fff9073f9d | ||
|
|
71f0b8a005 | ||
|
|
d2b965f79e | ||
|
|
a02aa71a95 | ||
|
|
7562bf5c95 | ||
|
|
f9521483e2 | ||
|
|
ca85858885 | ||
|
|
c7b3a94533 | ||
|
|
b010c9ecb0 | ||
|
|
0b98d04bac | ||
|
|
e25e388e59 | ||
|
|
10654b7916 | ||
|
|
ff049ad3e8 | ||
|
|
3572fff2c1 | ||
|
|
851f1c265f | ||
|
|
c104ee4fd9 | ||
|
|
e550baf59d | ||
|
|
ca2ded047b | ||
|
|
fd43d7d56d | ||
|
|
e899e4545f | ||
|
|
2e28a4a790 | ||
|
|
891aa649e8 | ||
|
|
a8a0167123 | ||
|
|
4b55719f86 | ||
|
|
5387ff207c | ||
|
|
e3804b103b |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -112,3 +112,4 @@ firebase/.env
|
||||
.elasticbeanstalk/*
|
||||
!.elasticbeanstalk/*.cfg.yml
|
||||
!.elasticbeanstalk/*.global.yml
|
||||
logs/oAuthClient-log.log
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<babeledit_project be_version="2.7.1" version="1.2">
|
||||
<babeledit_project version="1.2" be_version="2.7.1">
|
||||
<!--
|
||||
|
||||
BabelEdit project file
|
||||
@@ -4283,6 +4283,27 @@
|
||||
</translation>
|
||||
</translations>
|
||||
</concept_node>
|
||||
<concept_node>
|
||||
<name>md_ded_notes</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>
|
||||
<folder_node>
|
||||
<name>md_hour_split</name>
|
||||
<children>
|
||||
@@ -7120,6 +7141,27 @@
|
||||
</translation>
|
||||
</translations>
|
||||
</concept_node>
|
||||
<concept_node>
|
||||
<name>color</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>default_arrived</name>
|
||||
<definition_loaded>false</definition_loaded>
|
||||
@@ -7456,6 +7498,27 @@
|
||||
</translation>
|
||||
</translations>
|
||||
</concept_node>
|
||||
<concept_node>
|
||||
<name>production_colors</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>production_statuses</name>
|
||||
<definition_loaded>false</definition_loaded>
|
||||
@@ -8019,6 +8082,48 @@
|
||||
</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>filehandlers</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>insurancecos</name>
|
||||
<definition_loaded>false</definition_loaded>
|
||||
@@ -8580,6 +8685,27 @@
|
||||
<folder_node>
|
||||
<name>validation</name>
|
||||
<children>
|
||||
<concept_node>
|
||||
<name>centermustexist</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>larsplit</name>
|
||||
<definition_loaded>false</definition_loaded>
|
||||
@@ -16878,6 +17004,27 @@
|
||||
</translation>
|
||||
</translations>
|
||||
</concept_node>
|
||||
<concept_node>
|
||||
<name>prt_dsmk_m</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>prt_dsmk_p</name>
|
||||
<definition_loaded>false</definition_loaded>
|
||||
@@ -17386,6 +17533,27 @@
|
||||
</translation>
|
||||
</translations>
|
||||
</concept_node>
|
||||
<concept_node>
|
||||
<name>changefilehandler</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>changelaborrate</name>
|
||||
<definition_loaded>false</definition_loaded>
|
||||
@@ -17428,6 +17596,27 @@
|
||||
</translation>
|
||||
</translations>
|
||||
</concept_node>
|
||||
<concept_node>
|
||||
<name>changestimator</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>convert</name>
|
||||
<definition_loaded>false</definition_loaded>
|
||||
@@ -19611,6 +19800,27 @@
|
||||
</translation>
|
||||
</translations>
|
||||
</concept_node>
|
||||
<concept_node>
|
||||
<name>date_next_contact</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>date_open</name>
|
||||
<definition_loaded>false</definition_loaded>
|
||||
@@ -19674,6 +19884,27 @@
|
||||
</translation>
|
||||
</translations>
|
||||
</concept_node>
|
||||
<concept_node>
|
||||
<name>ded_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>ded_status</name>
|
||||
<definition_loaded>false</definition_loaded>
|
||||
@@ -21138,6 +21369,27 @@
|
||||
</translation>
|
||||
</translations>
|
||||
</concept_node>
|
||||
<concept_node>
|
||||
<name>loss_of_use</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>ma2s</name>
|
||||
<definition_loaded>false</definition_loaded>
|
||||
@@ -25960,6 +26212,27 @@
|
||||
</translation>
|
||||
</translations>
|
||||
</concept_node>
|
||||
<concept_node>
|
||||
<name>savebeforeconversion</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>scheduledinchange</name>
|
||||
<definition_loaded>false</definition_loaded>
|
||||
@@ -29083,6 +29356,27 @@
|
||||
</translation>
|
||||
</translations>
|
||||
</concept_node>
|
||||
<concept_node>
|
||||
<name>noattachedjobs</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>
|
||||
@@ -29235,6 +29529,27 @@
|
||||
</translation>
|
||||
</translations>
|
||||
</concept_node>
|
||||
<concept_node>
|
||||
<name>recentonly</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>selectmedia</name>
|
||||
<definition_loaded>false</definition_loaded>
|
||||
@@ -30438,6 +30753,27 @@
|
||||
<folder_node>
|
||||
<name>errors</name>
|
||||
<children>
|
||||
<concept_node>
|
||||
<name>associatedbills</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>backordering</name>
|
||||
<definition_loaded>false</definition_loaded>
|
||||
@@ -32685,6 +33021,27 @@
|
||||
</translation>
|
||||
</translations>
|
||||
</concept_node>
|
||||
<concept_node>
|
||||
<name>ab_proof_of_loss</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>appointment_confirmation</name>
|
||||
<definition_loaded>false</definition_loaded>
|
||||
@@ -33042,6 +33399,27 @@
|
||||
</translation>
|
||||
</translations>
|
||||
</concept_node>
|
||||
<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>invoice_customer_payable</name>
|
||||
<definition_loaded>false</definition_loaded>
|
||||
@@ -33168,6 +33546,69 @@
|
||||
</translation>
|
||||
</translations>
|
||||
</concept_node>
|
||||
<concept_node>
|
||||
<name>mpi_animal_checklist</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>mpi_eglass_auth</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>mpi_final_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>
|
||||
@@ -33798,6 +34239,27 @@
|
||||
</translation>
|
||||
</translations>
|
||||
</concept_node>
|
||||
<concept_node>
|
||||
<name>worksheet_sorted_by_operation_type</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>
|
||||
@@ -34410,6 +34872,27 @@
|
||||
</translation>
|
||||
</translations>
|
||||
</concept_node>
|
||||
<concept_node>
|
||||
<name>settings</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>
|
||||
@@ -34478,6 +34961,27 @@
|
||||
</translation>
|
||||
</translations>
|
||||
</concept_node>
|
||||
<concept_node>
|
||||
<name>ats</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>bodyhours</name>
|
||||
<definition_loaded>false</definition_loaded>
|
||||
@@ -34520,6 +35024,69 @@
|
||||
</translation>
|
||||
</translations>
|
||||
</concept_node>
|
||||
<concept_node>
|
||||
<name>cardsettings</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>clm_no</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>compact</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>detailpriority</name>
|
||||
<definition_loaded>false</definition_loaded>
|
||||
@@ -34541,6 +35108,27 @@
|
||||
</translation>
|
||||
</translations>
|
||||
</concept_node>
|
||||
<concept_node>
|
||||
<name>employeeassignments</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>employeesearch</name>
|
||||
<definition_loaded>false</definition_loaded>
|
||||
@@ -34562,6 +35150,27 @@
|
||||
</translation>
|
||||
</translations>
|
||||
</concept_node>
|
||||
<concept_node>
|
||||
<name>ins_co_nm</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>jobdetail</name>
|
||||
<definition_loaded>false</definition_loaded>
|
||||
@@ -34583,6 +35192,27 @@
|
||||
</translation>
|
||||
</translations>
|
||||
</concept_node>
|
||||
<concept_node>
|
||||
<name>laborhrs</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>note</name>
|
||||
<definition_loaded>false</definition_loaded>
|
||||
@@ -34604,6 +35234,27 @@
|
||||
</translation>
|
||||
</translations>
|
||||
</concept_node>
|
||||
<concept_node>
|
||||
<name>ownr_nm</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>paintpriority</name>
|
||||
<definition_loaded>false</definition_loaded>
|
||||
@@ -34625,6 +35276,27 @@
|
||||
</translation>
|
||||
</translations>
|
||||
</concept_node>
|
||||
<concept_node>
|
||||
<name>production_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>refinishhours</name>
|
||||
<definition_loaded>false</definition_loaded>
|
||||
@@ -34646,6 +35318,27 @@
|
||||
</translation>
|
||||
</translations>
|
||||
</concept_node>
|
||||
<concept_node>
|
||||
<name>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>selectview</name>
|
||||
<definition_loaded>false</definition_loaded>
|
||||
@@ -36213,6 +36906,27 @@
|
||||
</translation>
|
||||
</translations>
|
||||
</concept_node>
|
||||
<concept_node>
|
||||
<name>open_orders_status</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_backorder</name>
|
||||
<definition_loaded>false</definition_loaded>
|
||||
@@ -36826,6 +37540,37 @@
|
||||
</folder_node>
|
||||
</children>
|
||||
</folder_node>
|
||||
<folder_node>
|
||||
<name>schedule</name>
|
||||
<children>
|
||||
<folder_node>
|
||||
<name>labels</name>
|
||||
<children>
|
||||
<concept_node>
|
||||
<name>manualevent</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>
|
||||
</folder_node>
|
||||
<folder_node>
|
||||
<name>scoreboard</name>
|
||||
<children>
|
||||
|
||||
@@ -2,74 +2,75 @@
|
||||
"name": "bodyshop",
|
||||
"version": "0.1.1",
|
||||
"private": true,
|
||||
"proxy": "http://localhost:5000",
|
||||
"proxy": "http://localhost:4000",
|
||||
"dependencies": {
|
||||
"@apollo/client": "^3.4.16",
|
||||
"@craco/craco": "^6.3.0",
|
||||
"@apollo/client": "^3.4.17",
|
||||
"@asseinfo/react-kanban": "^2.2.0",
|
||||
"@craco/craco": "^6.4.0",
|
||||
"@fingerprintjs/fingerprintjs": "^3.3.0",
|
||||
"@lourenci/react-kanban": "^2.1.0",
|
||||
"@openreplay/tracker": "^3.4.4",
|
||||
"@openreplay/tracker-assist": "^3.4.3",
|
||||
"@openreplay/tracker": "^3.4.7",
|
||||
"@openreplay/tracker-assist": "^3.4.4",
|
||||
"@openreplay/tracker-graphql": "^3.0.0",
|
||||
"@openreplay/tracker-redux": "^3.0.0",
|
||||
"@sentry/react": "^6.13.2",
|
||||
"@sentry/tracing": "^6.13.2",
|
||||
"@stripe/react-stripe-js": "^1.5.0",
|
||||
"@stripe/stripe-js": "^1.19.1",
|
||||
"@tanem/react-nprogress": "^3.0.79",
|
||||
"@sentry/react": "^6.14.3",
|
||||
"@sentry/tracing": "^6.14.3",
|
||||
"@splitsoftware/splitio-react": "^1.3.0",
|
||||
"@stripe/react-stripe-js": "^1.6.0",
|
||||
"@stripe/stripe-js": "^1.21.1",
|
||||
"@tanem/react-nprogress": "^3.0.82",
|
||||
"antd": "^4.16.13",
|
||||
"apollo-link-logger": "^2.0.0",
|
||||
"axios": "^0.22.0",
|
||||
"axios": "^0.24.0",
|
||||
"craco-less": "^1.20.0",
|
||||
"dinero.js": "^1.9.0",
|
||||
"dinero.js": "^1.9.1",
|
||||
"dotenv": "^10.0.0",
|
||||
"enquire-js": "^0.2.1",
|
||||
"env-cmd": "^10.1.0",
|
||||
"exifr": "^7.1.3",
|
||||
"firebase": "^9.1.1",
|
||||
"graphql": "^15.6.0",
|
||||
"i18next": "^21.2.4",
|
||||
"firebase": "^9.4.1",
|
||||
"graphql": "^16.0.1",
|
||||
"i18next": "^21.4.2",
|
||||
"i18next-browser-languagedetector": "^6.1.2",
|
||||
"jsoneditor": "^9.5.6",
|
||||
"jsoneditor": "^9.5.7",
|
||||
"jsreport-browser-client-dist": "^1.3.0",
|
||||
"libphonenumber-js": "^1.9.36",
|
||||
"logrocket": "^2.0.0",
|
||||
"markerjs2": "^2.13.0",
|
||||
"libphonenumber-js": "^1.9.42",
|
||||
"logrocket": "^2.1.1",
|
||||
"markerjs2": "^2.17.0",
|
||||
"moment-business-days": "^1.2.0",
|
||||
"phone": "^3.1.8",
|
||||
"phone": "^3.1.9",
|
||||
"preval.macro": "^5.0.0",
|
||||
"prop-types": "^15.7.2",
|
||||
"query-string": "^7.0.1",
|
||||
"rc-queue-anim": "^2.0.0",
|
||||
"rc-scroll-anim": "^2.7.6",
|
||||
"react": "^17.0.1",
|
||||
"react-big-calendar": "^0.36.1",
|
||||
"react-big-calendar": "^0.38.1",
|
||||
"react-color": "^2.19.3",
|
||||
"react-cookie": "^4.1.1",
|
||||
"react-dom": "^17.0.1",
|
||||
"react-drag-listview": "^0.1.8",
|
||||
"react-grid-gallery": "^0.5.5",
|
||||
"react-grid-layout": "^1.3.0",
|
||||
"react-i18next": "^11.12.0",
|
||||
"react-i18next": "^11.14.2",
|
||||
"react-icons": "^4.3.1",
|
||||
"react-number-format": "^4.7.3",
|
||||
"react-redux": "^7.2.5",
|
||||
"react-number-format": "^4.8.0",
|
||||
"react-redux": "^7.2.6",
|
||||
"react-resizable": "^3.0.4",
|
||||
"react-router-dom": "^5.3.0",
|
||||
"react-scripts": "^4.0.3",
|
||||
"react-sublime-video": "^0.2.5",
|
||||
"react-virtualized": "^9.22.3",
|
||||
"recharts": "^2.1.4",
|
||||
"redux": "^4.1.1",
|
||||
"recharts": "^2.1.6",
|
||||
"redux": "^4.1.2",
|
||||
"redux-persist": "^6.0.0",
|
||||
"redux-saga": "^1.1.3",
|
||||
"redux-state-sync": "^3.1.2",
|
||||
"reselect": "^4.0.0",
|
||||
"sass": "^1.42.1",
|
||||
"socket.io-client": "^4.2.0",
|
||||
"styled-components": "^5.3.1",
|
||||
"subscriptions-transport-ws": "^0.9.18",
|
||||
"web-vitals": "^2.1.0",
|
||||
"reselect": "^4.1.2",
|
||||
"sass": "^1.43.4",
|
||||
"socket.io-client": "^4.3.2",
|
||||
"styled-components": "^5.3.3",
|
||||
"subscriptions-transport-ws": "^0.11.0",
|
||||
"web-vitals": "^2.1.2",
|
||||
"workbox-background-sync": "^6.3.0",
|
||||
"workbox-broadcast-update": "^6.3.0",
|
||||
"workbox-cacheable-response": "^6.3.0",
|
||||
@@ -114,7 +115,7 @@
|
||||
]
|
||||
},
|
||||
"devDependencies": {
|
||||
"@sentry/webpack-plugin": "^1.17.2",
|
||||
"@sentry/webpack-plugin": "^1.18.3",
|
||||
"patch-package": "^6.4.7",
|
||||
"redux-logger": "^3.0.6",
|
||||
"source-map-explorer": "^2.5.2"
|
||||
|
||||
@@ -1,4 +1,8 @@
|
||||
import { ApolloProvider } from "@apollo/client";
|
||||
//import trackerRedux from "@openreplay/tracker-redux";
|
||||
import Tracker from "@openreplay/tracker";
|
||||
import trackerGraphQL from "@openreplay/tracker-graphql";
|
||||
import { SplitFactory, SplitSdk } from "@splitsoftware/splitio-react";
|
||||
import { ConfigProvider } from "antd";
|
||||
import enLocale from "antd/es/locale/en_US";
|
||||
import LogRocket from "logrocket";
|
||||
@@ -6,13 +10,11 @@ import moment from "moment";
|
||||
import React from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import GlobalLoadingBar from "../components/global-loading-bar/global-loading-bar.component";
|
||||
import client from "../utils/GraphQLClient";
|
||||
import App from "./App";
|
||||
import trackerGraphQL from "@openreplay/tracker-graphql";
|
||||
//import trackerRedux from "@openreplay/tracker-redux";
|
||||
import Tracker from "@openreplay/tracker";
|
||||
//import trackerAssist from "@openreplay/tracker-assist";
|
||||
import { getCurrentUser } from "../firebase/firebase.utils";
|
||||
import client from "../utils/GraphQLClient";
|
||||
import App from "./App";
|
||||
|
||||
moment.locale("en-US");
|
||||
|
||||
export const tracker = new Tracker({
|
||||
@@ -36,6 +38,13 @@ export const recordGraphQL = tracker.use(trackerGraphQL());
|
||||
tracker.start();
|
||||
if (process.env.NODE_ENV === "production") LogRocket.init("gvfvfw/bodyshopapp");
|
||||
|
||||
export const factory = SplitSdk({
|
||||
core: {
|
||||
authorizationKey: process.env.REACT_APP_SPLIT_API,
|
||||
key: "anon",
|
||||
},
|
||||
});
|
||||
|
||||
export default function AppContainer() {
|
||||
const { t } = useTranslation();
|
||||
|
||||
@@ -53,7 +62,9 @@ export default function AppContainer() {
|
||||
}}
|
||||
>
|
||||
<GlobalLoadingBar />
|
||||
<App />
|
||||
<SplitFactory factory={factory}>
|
||||
<App />
|
||||
</SplitFactory>
|
||||
</ConfigProvider>
|
||||
</ApolloProvider>
|
||||
);
|
||||
|
||||
@@ -128,3 +128,9 @@
|
||||
.react-kanban-column {
|
||||
background-color: #ddd !important;
|
||||
}
|
||||
|
||||
.production-list-table {
|
||||
td.ant-table-column-sort {
|
||||
background: unset;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,8 +8,26 @@ import { DateFormatter, DateTimeFormatter } from "../../utils/DateFormatter";
|
||||
import { alphaSort, dateSort } from "../../utils/sorters";
|
||||
import PaymentExportButton from "../payment-export-button/payment-export-button.component";
|
||||
import PaymentsExportAllButton from "../payments-export-all-button/payments-export-all-button.component";
|
||||
import QboAuthorizeComponent from "../qbo-authorize/qbo-authorize.component";
|
||||
import { connect } from "react-redux";
|
||||
import { createStructuredSelector } from "reselect";
|
||||
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||
|
||||
export default function AccountingPayablesTableComponent({
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
bodyshop: selectBodyshop,
|
||||
});
|
||||
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
//setUserLanguage: language => dispatch(setUserLanguage(language))
|
||||
});
|
||||
|
||||
export default connect(
|
||||
mapStateToProps,
|
||||
mapDispatchToProps
|
||||
)(AccountingPayablesTableComponent);
|
||||
|
||||
export function AccountingPayablesTableComponent({
|
||||
bodyshop,
|
||||
loading,
|
||||
payments,
|
||||
}) {
|
||||
@@ -163,6 +181,9 @@ export default function AccountingPayablesTableComponent({
|
||||
loadingCallback={setTransInProgress}
|
||||
completedCallback={setSelectedPayments}
|
||||
/>
|
||||
{bodyshop.accountingconfig && bodyshop.accountingconfig.qbo && (
|
||||
<QboAuthorizeComponent />
|
||||
)}
|
||||
<Input
|
||||
value={state.search}
|
||||
onChange={handleSearch}
|
||||
|
||||
@@ -199,7 +199,7 @@ export function AccountingReceivablesTableComponent({
|
||||
<Card
|
||||
extra={
|
||||
<Space wrap>
|
||||
{!bodyshop.cdk_dealerid && (
|
||||
{!bodyshop.cdk_dealerid && !bodyshop.pbs_serialnumber && (
|
||||
<JobsExportAllButton
|
||||
jobIds={selectedJobs}
|
||||
disabled={transInProgress || selectedJobs.length === 0}
|
||||
|
||||
@@ -216,7 +216,7 @@ function BillEnterModalContainer({
|
||||
|
||||
if (enterAgain) {
|
||||
form.resetFields();
|
||||
form.setFieldsValue(formValues);
|
||||
form.setFieldsValue({ ...form.getFieldsValue(), billlines: [] });
|
||||
} else {
|
||||
toggleModalVisible();
|
||||
}
|
||||
|
||||
@@ -156,7 +156,6 @@ export function BillEnterModalLinesComponent({
|
||||
setFieldsValue({
|
||||
billlines: getFieldsValue("billlines").billlines.map(
|
||||
(item, idx) => {
|
||||
console.log("Checking", index, idx);
|
||||
if (idx === index) {
|
||||
console.log(
|
||||
"Found and setting.",
|
||||
@@ -502,9 +501,9 @@ const EditableCell = ({
|
||||
labelCol={{ span: 0 }}
|
||||
{...(formItemProps && formItemProps(record))}
|
||||
>
|
||||
{(formInput && formInput(record, record.key)) || children}
|
||||
{(formInput && formInput(record, record.name)) || children}
|
||||
</Form.Item>
|
||||
{additional && additional(record, record.key)}
|
||||
{additional && additional(record, record.name)}
|
||||
</Space>
|
||||
</td>
|
||||
);
|
||||
@@ -516,7 +515,7 @@ const EditableCell = ({
|
||||
name={dataIndex}
|
||||
{...(formItemProps && formItemProps(record))}
|
||||
>
|
||||
{(formInput && formInput(record, record.key)) || children}
|
||||
{(formInput && formInput(record, record.name)) || children}
|
||||
</Form.Item>
|
||||
</td>
|
||||
);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { ShrinkOutlined } from "@ant-design/icons";
|
||||
import { Col, Row, Typography } from "antd";
|
||||
import { ShrinkOutlined, InfoCircleOutlined } from "@ant-design/icons";
|
||||
import { Col, Row, Tooltip, Typography } from "antd";
|
||||
import React from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { connect } from "react-redux";
|
||||
@@ -31,6 +31,9 @@ export function ChatPopupComponent({
|
||||
{t("messaging.labels.messaging")}
|
||||
</Typography.Title>
|
||||
<ChatNewConversation />
|
||||
<Tooltip title={t("messaging.labels.recentonly")}>
|
||||
<InfoCircleOutlined />
|
||||
</Tooltip>
|
||||
</div>
|
||||
<ShrinkOutlined
|
||||
onClick={() => toggleChatVisible()}
|
||||
|
||||
@@ -53,6 +53,7 @@ export function ContractConvertToRo({
|
||||
const billingLines = [];
|
||||
if (contractLength > 0)
|
||||
billingLines.push({
|
||||
manual_line:true,
|
||||
unq_seq: 1,
|
||||
line_no: 1,
|
||||
line_ref: 1,
|
||||
@@ -70,6 +71,7 @@ export function ContractConvertToRo({
|
||||
contract.kmend - contract.kmstart - contract.dailyfreekm * contractLength;
|
||||
if (mileageDiff > 0) {
|
||||
billingLines.push({
|
||||
manual_line:true,
|
||||
unq_seq: 2,
|
||||
line_no: 2,
|
||||
line_ref: 2,
|
||||
@@ -86,6 +88,7 @@ export function ContractConvertToRo({
|
||||
|
||||
if (values.refuelqty > 0) {
|
||||
billingLines.push({
|
||||
manual_line:true,
|
||||
unq_seq: 3,
|
||||
line_no: 3,
|
||||
line_ref: 3,
|
||||
@@ -101,6 +104,7 @@ export function ContractConvertToRo({
|
||||
}
|
||||
if (values.applyCleanupCharge) {
|
||||
billingLines.push({
|
||||
manual_line:true,
|
||||
unq_seq: 4,
|
||||
line_no: 4,
|
||||
line_ref: 4,
|
||||
@@ -117,6 +121,7 @@ export function ContractConvertToRo({
|
||||
if (contract.damagewaiver) {
|
||||
//Add for cleanup fee.
|
||||
billingLines.push({
|
||||
manual_line:true,
|
||||
unq_seq: 5,
|
||||
line_no: 5,
|
||||
line_ref: 5,
|
||||
|
||||
@@ -42,7 +42,6 @@ export default function ContractFormComponent({
|
||||
<ContractStatusSelector />
|
||||
</Form.Item>
|
||||
)}
|
||||
|
||||
<Form.Item
|
||||
label={t("contracts.fields.start")}
|
||||
name="start"
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Button, Table, Col , Checkbox} from "antd";
|
||||
import { Button, Table, Col, Checkbox } from "antd";
|
||||
import React, { useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { connect } from "react-redux";
|
||||
@@ -23,15 +23,26 @@ export function DmsCustomerSelector({ bodyshop }) {
|
||||
const [customerList, setcustomerList] = useState([]);
|
||||
const [visible, setVisible] = useState(false);
|
||||
const [selectedCustomer, setSelectedCustomer] = useState(null);
|
||||
const [dmsType, setDmsType] = useState("cdk");
|
||||
|
||||
socket.on("cdk-select-customer", (customerList, callback) => {
|
||||
setVisible(true);
|
||||
setDmsType("cdk");
|
||||
setcustomerList(customerList);
|
||||
});
|
||||
socket.on("pbs-select-customer", (customerList, callback) => {
|
||||
setVisible(true);
|
||||
setDmsType("pbs");
|
||||
setcustomerList(customerList);
|
||||
console.log(
|
||||
"🚀 ~ file: dms-customer-selector.component.jsx ~ line 37 ~ socket.on ~ customerList",
|
||||
customerList
|
||||
);
|
||||
});
|
||||
|
||||
const onUseSelected = () => {
|
||||
setVisible(false);
|
||||
socket.emit("cdk-selected-customer", selectedCustomer);
|
||||
socket.emit(`${dmsType}-selected-customer`, selectedCustomer);
|
||||
setSelectedCustomer(null);
|
||||
};
|
||||
|
||||
@@ -50,7 +61,7 @@ export function DmsCustomerSelector({ bodyshop }) {
|
||||
setSelectedCustomer(null);
|
||||
};
|
||||
|
||||
const columns = [
|
||||
const cdkColumns = [
|
||||
{
|
||||
title: t("jobs.fields.dms.id"),
|
||||
dataIndex: ["id", "value"],
|
||||
@@ -60,13 +71,14 @@ export function DmsCustomerSelector({ bodyshop }) {
|
||||
title: t("jobs.fields.dms.vinowner"),
|
||||
dataIndex: "vinOwner",
|
||||
key: "vinOwner",
|
||||
render: (text, record) => <Checkbox disabled checked={record.vinOwner}/>
|
||||
render: (text, record) => <Checkbox disabled checked={record.vinOwner} />,
|
||||
},
|
||||
{
|
||||
title: t("jobs.fields.dms.name1"),
|
||||
dataIndex: ["name1", "fullName"],
|
||||
key: "name1",
|
||||
sorter: (a, b) => alphaSort(a.name1?.fullName, b.name1?.fullName),
|
||||
sorter: (a, b) =>
|
||||
alphaSort(a.name1 && a.name1.fullName, b.name1 && b.name1.fullName),
|
||||
},
|
||||
|
||||
{
|
||||
@@ -74,11 +86,43 @@ export function DmsCustomerSelector({ bodyshop }) {
|
||||
//dataIndex: ["name2", "fullName"],
|
||||
key: "address",
|
||||
render: (record, value) =>
|
||||
`${record?.address?.addressLine[0]}, ${record.address?.city} ${record.address?.stateOrProvince} ${record.address?.postalCode}`,
|
||||
`${record.address && record.address.addressLine[0]}, ${
|
||||
record.address && record.address.city
|
||||
} ${record.address && record.address.stateOrProvince} ${
|
||||
record.address && record.address.postalCode
|
||||
}`,
|
||||
},
|
||||
];
|
||||
|
||||
if (!visible) return <></>;
|
||||
const pbsColumns = [
|
||||
{
|
||||
title: t("jobs.fields.dms.id"),
|
||||
dataIndex: "ContactId",
|
||||
key: "ContactId",
|
||||
},
|
||||
{
|
||||
title: t("jobs.fields.dms.vinowner"),
|
||||
dataIndex: "vinOwner",
|
||||
key: "vinOwner",
|
||||
render: (text, record) => <Checkbox disabled checked={record.vinOwner} />,
|
||||
},
|
||||
{
|
||||
title: t("jobs.fields.dms.name1"),
|
||||
key: "name1",
|
||||
sorter: (a, b) => alphaSort(a.LastName, b.LastName),
|
||||
render: (text, record) =>
|
||||
`${record.FirstName || ""} ${record.LastName || ""}`,
|
||||
},
|
||||
|
||||
{
|
||||
title: t("jobs.fields.dms.address"),
|
||||
key: "address",
|
||||
render: (record, value) =>
|
||||
`${record.Address}, ${record.City} ${record.State} ${record.ZipCode}`,
|
||||
},
|
||||
];
|
||||
|
||||
if (!visible) return null;
|
||||
return (
|
||||
<Col span={24}>
|
||||
<Table
|
||||
@@ -104,13 +148,17 @@ export function DmsCustomerSelector({ bodyshop }) {
|
||||
</div>
|
||||
)}
|
||||
pagination={{ position: "top" }}
|
||||
columns={columns}
|
||||
rowKey={(record) => record.id.value}
|
||||
columns={dmsType === "cdk" ? cdkColumns : pbsColumns}
|
||||
rowKey={(record) =>
|
||||
dmsType === "cdk" ? record.id.value : record.ContactId
|
||||
}
|
||||
dataSource={customerList}
|
||||
//onChange={handleTableChange}
|
||||
rowSelection={{
|
||||
onSelect: (props) => {
|
||||
setSelectedCustomer(props.id.value);
|
||||
onSelect: (record) => {
|
||||
setSelectedCustomer(
|
||||
dmsType === "cdk" ? record.id.value : record.ContactId
|
||||
);
|
||||
},
|
||||
type: "radio",
|
||||
selectedRowKeys: [selectedCustomer],
|
||||
|
||||
@@ -119,33 +119,35 @@ export function DmsPostForm({ bodyshop, socket, job }) {
|
||||
</Form.Item>
|
||||
</LayoutFormRow>
|
||||
|
||||
<LayoutFormRow style={{ justifyContent: "center" }} grow>
|
||||
<Form.Item
|
||||
name="dms_make"
|
||||
label={t("jobs.fields.dms.dms_make")}
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
},
|
||||
]}
|
||||
>
|
||||
<Input disabled />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
name="dms_model"
|
||||
label={t("jobs.fields.dms.dms_model")}
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
},
|
||||
]}
|
||||
>
|
||||
<Input disabled />
|
||||
</Form.Item>
|
||||
{bodyshop.cdk_dealerid && (
|
||||
<LayoutFormRow style={{ justifyContent: "center" }} grow>
|
||||
<Form.Item
|
||||
name="dms_make"
|
||||
label={t("jobs.fields.dms.dms_make")}
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
},
|
||||
]}
|
||||
>
|
||||
<Input disabled />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
name="dms_model"
|
||||
label={t("jobs.fields.dms.dms_model")}
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
},
|
||||
]}
|
||||
>
|
||||
<Input disabled />
|
||||
</Form.Item>
|
||||
|
||||
<DmsCdkMakes form={form} socket={socket} job={job} />
|
||||
<DmsCdkMakesRefetch />
|
||||
</LayoutFormRow>
|
||||
<DmsCdkMakes form={form} socket={socket} job={job} />
|
||||
<DmsCdkMakesRefetch />
|
||||
</LayoutFormRow>
|
||||
)}
|
||||
<Form.Item
|
||||
name="story"
|
||||
label={t("jobs.fields.dms.story")}
|
||||
@@ -157,6 +159,7 @@ export function DmsPostForm({ bodyshop, socket, job }) {
|
||||
>
|
||||
<Input.TextArea maxLength={240} />
|
||||
</Form.Item>
|
||||
|
||||
<Divider />
|
||||
<Form.List name={["payers"]}>
|
||||
{(fields, { add, remove }) => {
|
||||
@@ -259,7 +262,6 @@ export function DmsPostForm({ bodyshop, socket, job }) {
|
||||
))}
|
||||
<Form.Item>
|
||||
<Button
|
||||
type="dashed"
|
||||
disabled={!(fields.length < 3)}
|
||||
onClick={() => {
|
||||
if (fields.length < 3) add();
|
||||
|
||||
@@ -1,15 +1,71 @@
|
||||
import { UploadOutlined } from "@ant-design/icons";
|
||||
import { Divider, Form, Input, Select, Tabs, Upload } from "antd";
|
||||
import { UploadOutlined, UserAddOutlined } from "@ant-design/icons";
|
||||
import {
|
||||
Divider,
|
||||
Form,
|
||||
Input,
|
||||
Select,
|
||||
Tabs,
|
||||
Upload,
|
||||
Space,
|
||||
Menu,
|
||||
Dropdown,
|
||||
} from "antd";
|
||||
import React from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import EmailDocumentsComponent from "../email-documents/email-documents.component";
|
||||
import _ from "lodash";
|
||||
import { connect } from "react-redux";
|
||||
import { createStructuredSelector } from "reselect";
|
||||
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
bodyshop: selectBodyshop,
|
||||
});
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
//setUserLanguage: language => dispatch(setUserLanguage(language))
|
||||
});
|
||||
export default connect(
|
||||
mapStateToProps,
|
||||
mapDispatchToProps
|
||||
)(EmailOverlayComponent);
|
||||
|
||||
export default function EmailOverlayComponent({ form, selectedMediaState }) {
|
||||
export function EmailOverlayComponent({ form, selectedMediaState, bodyshop }) {
|
||||
const { t } = useTranslation();
|
||||
const handleClick = ({ item, key, keyPath }) => {
|
||||
const email = item.props.value;
|
||||
form.setFieldsValue({ to: _.uniq([...form.getFieldValue("to"), email]) });
|
||||
};
|
||||
|
||||
const menu = (
|
||||
<div>
|
||||
<Menu onClick={handleClick}>
|
||||
{bodyshop.employees
|
||||
.filter((e) => e.user_email)
|
||||
.map((e, idx) => (
|
||||
<Menu.Item value={e.user_email} key={idx}>
|
||||
{`${e.first_name} ${e.last_name}`}
|
||||
</Menu.Item>
|
||||
))}
|
||||
</Menu>
|
||||
</div>
|
||||
);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Form.Item
|
||||
label={t("emails.fields.to")}
|
||||
label={
|
||||
<Space>
|
||||
{t("emails.fields.to")}
|
||||
<Dropdown overlay={menu}>
|
||||
<a
|
||||
className="ant-dropdown-link"
|
||||
href=" #"
|
||||
onClick={(e) => e.preventDefault()}
|
||||
>
|
||||
<UserAddOutlined />
|
||||
</a>
|
||||
</Dropdown>
|
||||
</Space>
|
||||
}
|
||||
name="to"
|
||||
rules={[
|
||||
{
|
||||
|
||||
@@ -51,7 +51,9 @@ export function EmailOverlayContainer({
|
||||
|
||||
const defaultEmailFrom = {
|
||||
from: {
|
||||
name: `${currentUser.displayName} @ ${bodyshop.shopname}`,
|
||||
name: currentUser.displayName
|
||||
? `${currentUser.displayName} @ ${bodyshop.shopname}`
|
||||
: bodyshop.shopname,
|
||||
address: EmailSettings.fromAddress,
|
||||
},
|
||||
ReplyTo: {
|
||||
|
||||
@@ -7,10 +7,12 @@ import { selectCurrentUser } from "../../redux/user/user.selectors";
|
||||
import { GenerateDocument } from "../../utils/RenderTemplate";
|
||||
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";
|
||||
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
currentUser: selectCurrentUser,
|
||||
});
|
||||
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
setEmailOptions: (e) => dispatch(setEmailOptions(e)),
|
||||
});
|
||||
@@ -24,9 +26,20 @@ export function EmailTestComponent({ currentUser, setEmailOptions }) {
|
||||
{
|
||||
name: values.key,
|
||||
variables: {
|
||||
...(values.start
|
||||
? {
|
||||
start: moment(values.start).startOf("day").format("YYYY-MM-DD"),
|
||||
}
|
||||
: {}),
|
||||
...(values.end
|
||||
? { end: moment(values.end).endOf("day").format("YYYY-MM-DD") }
|
||||
: {}),
|
||||
...(values.start
|
||||
? { starttz: moment(values.start).startOf("day") }
|
||||
: {}),
|
||||
...(values.end ? { endtz: moment(values.end).endOf("day") } : {}),
|
||||
|
||||
...(values.id ? { id: values.id } : {}),
|
||||
...(values.start ? { start: values.start } : {}),
|
||||
...(values.end ? { end: values.end } : {}),
|
||||
},
|
||||
},
|
||||
{
|
||||
|
||||
@@ -21,7 +21,7 @@ export const PhoneItemFormatterValidation = (getFieldValue, name) => ({
|
||||
return Promise.resolve();
|
||||
} else {
|
||||
const p = parsePhoneNumber(value, "CA");
|
||||
if (p.isValid()) {
|
||||
if (p && p.isValid()) {
|
||||
return Promise.resolve();
|
||||
} else {
|
||||
return Promise.reject(i18n.t("general.validation.invalidphone"));
|
||||
|
||||
@@ -46,12 +46,16 @@ import {
|
||||
} from "../../redux/application/application.selectors";
|
||||
import { setModalContext } from "../../redux/modals/modals.actions";
|
||||
import { signOutStart } from "../../redux/user/user.actions";
|
||||
import { selectCurrentUser } from "../../redux/user/user.selectors";
|
||||
import {
|
||||
selectBodyshop,
|
||||
selectCurrentUser,
|
||||
} from "../../redux/user/user.selectors";
|
||||
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
currentUser: selectCurrentUser,
|
||||
recentItems: selectRecentItems,
|
||||
selectedHeader: selectSelectedHeader,
|
||||
bodyshop: selectBodyshop,
|
||||
});
|
||||
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
@@ -69,6 +73,7 @@ const mapDispatchToProps = (dispatch) => ({
|
||||
function Header({
|
||||
handleMenuClick,
|
||||
currentUser,
|
||||
bodyshop,
|
||||
selectedHeader,
|
||||
signOutStart,
|
||||
setBillEnterContext,
|
||||
@@ -237,16 +242,26 @@ function Header({
|
||||
{t("menus.header.accounting-receivables")}
|
||||
</Link>
|
||||
</Menu.Item>
|
||||
<Menu.Item key="payables">
|
||||
<Link to="/manage/accounting/payables">
|
||||
{t("menus.header.accounting-payables")}
|
||||
</Link>
|
||||
</Menu.Item>
|
||||
<Menu.Item key="payments">
|
||||
<Link to="/manage/accounting/payments">
|
||||
{t("menus.header.accounting-payments")}
|
||||
</Link>
|
||||
</Menu.Item>
|
||||
{!(
|
||||
(bodyshop && bodyshop.cdk_dealerid) ||
|
||||
(bodyshop && bodyshop.pbs_serialnumber)
|
||||
) && (
|
||||
<Menu.Item key="payables">
|
||||
<Link to="/manage/accounting/payables">
|
||||
{t("menus.header.accounting-payables")}
|
||||
</Link>
|
||||
</Menu.Item>
|
||||
)}
|
||||
{!(
|
||||
(bodyshop && bodyshop.cdk_dealerid) ||
|
||||
(bodyshop && bodyshop.pbs_serialnumber)
|
||||
) && (
|
||||
<Menu.Item key="payments">
|
||||
<Link to="/manage/accounting/payments">
|
||||
{t("menus.header.accounting-payments")}
|
||||
</Link>
|
||||
</Menu.Item>
|
||||
)}
|
||||
<Menu.Item key="export-logs">
|
||||
<Link to="/manage/accounting/exportlogs">
|
||||
{t("menus.header.export-logs")}
|
||||
|
||||
@@ -209,6 +209,9 @@ export function ScheduleEventComponent({
|
||||
jobId: event.job.id,
|
||||
job: event.job,
|
||||
previousEvent: event.id,
|
||||
color: event.color,
|
||||
alt_transport: event.job && event.job.alt_transport,
|
||||
note: event.note,
|
||||
},
|
||||
});
|
||||
}}
|
||||
|
||||
@@ -186,12 +186,14 @@ export function JobChecklistForm({
|
||||
allow_text_message: job.owner && job.owner.allow_text_message,
|
||||
scheduled_completion:
|
||||
(job && job.scheduled_completion) ||
|
||||
moment().businessAdd(
|
||||
(job.labhrs.aggregate.sum.mod_lb_hrs +
|
||||
job.larhrs.aggregate.sum.mod_lb_hrs) /
|
||||
bodyshop.target_touchtime,
|
||||
"days"
|
||||
),
|
||||
(job.labbrs && job.larhrs
|
||||
? moment().businessAdd(
|
||||
(job.labhrs.aggregate.sum.mod_lb_hrs +
|
||||
job.larhrs.aggregate.sum.mod_lb_hrs) /
|
||||
bodyshop.target_touchtime,
|
||||
"days"
|
||||
)
|
||||
: null),
|
||||
scheduled_delivery: job && job.scheduled_delivery,
|
||||
}),
|
||||
...(type === "deliver" && {
|
||||
|
||||
@@ -162,7 +162,11 @@ export function JobLinesComponent({
|
||||
ellipsis: true,
|
||||
render: (text, record) => (
|
||||
<>
|
||||
<CurrencyFormatter>{record.act_price}</CurrencyFormatter>
|
||||
<CurrencyFormatter>
|
||||
{record.db_ref === "900510" || record.db_ref === "900511"
|
||||
? record.prt_dsmk_m
|
||||
: record.act_price}
|
||||
</CurrencyFormatter>
|
||||
{record.prt_dsmk_p && record.prt_dsmk_p !== 0 ? (
|
||||
<span
|
||||
style={{ marginLeft: ".2rem" }}
|
||||
|
||||
@@ -73,7 +73,7 @@ export const reconcileByPrice = (
|
||||
|
||||
jobLines.forEach((jl) => {
|
||||
const matchingBillLineIds = billLines
|
||||
.filter((bl) => bl.actual_price === jl.act_price && !jl.removed)
|
||||
.filter((bl) => bl.actual_price === jl.act_price && bl.quantity === jl.part_qty && !jl.removed)
|
||||
.map((bl) => bl.id);
|
||||
|
||||
if (matchingBillLineIds.length > 1) {
|
||||
|
||||
@@ -498,6 +498,12 @@ async function CheckTaxRates(estData, bodyshop) {
|
||||
) {
|
||||
estData.joblines.data[index].tax_part = jl.lbr_tax;
|
||||
}
|
||||
|
||||
//Set markup lines and tax lines as taxable.
|
||||
//900510 is a mark up. 900510 is a discount.
|
||||
if (jl.db_ref === "900510") {
|
||||
estData.joblines.data[index].tax_part = true;
|
||||
}
|
||||
});
|
||||
//}
|
||||
}
|
||||
|
||||
@@ -61,7 +61,7 @@ export function JobsCloseAutoAllocate({ bodyshop, joblines, form, disabled }) {
|
||||
);
|
||||
};
|
||||
|
||||
const overlay = bodyshop.cdk_dealerid && (
|
||||
const overlay = (bodyshop.cdk_dealerid || bodyshop.pbs_serialnumber) && (
|
||||
<Menu onClick={handleMenuClick}>
|
||||
{bodyshop.md_responsibility_centers.dms_defaults.map((mapping) => (
|
||||
<Menu.Item key={mapping.name}>{mapping.name}</Menu.Item>
|
||||
@@ -69,7 +69,7 @@ export function JobsCloseAutoAllocate({ bodyshop, joblines, form, disabled }) {
|
||||
</Menu>
|
||||
);
|
||||
|
||||
return bodyshop.cdk_dealerid ? (
|
||||
return bodyshop.cdk_dealerid || bodyshop.pbs_serialnumber ? (
|
||||
<Dropdown overlay={overlay}>
|
||||
<Button disabled={disabled}>{t("jobs.actions.dmsautoallocate")}</Button>
|
||||
</Dropdown>
|
||||
|
||||
@@ -15,6 +15,7 @@ import { logImEXEvent } from "../../firebase/firebase.utils";
|
||||
import { INSERT_EXPORT_LOG } from "../../graphql/accounting.queries";
|
||||
import { useHistory } from "react-router-dom";
|
||||
|
||||
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
bodyshop: selectBodyshop,
|
||||
currentUser: selectCurrentUser,
|
||||
@@ -35,7 +36,7 @@ export function JobsCloseExportButton({
|
||||
|
||||
const handleQbxml = async () => {
|
||||
//Check if it's a CDK setup.
|
||||
if (bodyshop.cdk_dealerid) {
|
||||
if (bodyshop.cdk_dealerid || bodyshop.pbs_serialnumber) {
|
||||
history.push(`/manage/dms?jobId=${jobId}`);
|
||||
return;
|
||||
}
|
||||
@@ -45,10 +46,13 @@ export function JobsCloseExportButton({
|
||||
//Check if it's a QBO Setup.
|
||||
let PartnerResponse;
|
||||
if (bodyshop.accountingconfig && bodyshop.accountingconfig.qbo) {
|
||||
PartnerResponse = await axios.post(`/qbo/receivables`, {
|
||||
withCredentials: true,
|
||||
jobIds: [jobId],
|
||||
});
|
||||
PartnerResponse = await axios.post(
|
||||
`/qbo/receivables`,
|
||||
{
|
||||
jobIds: [jobId],
|
||||
},
|
||||
|
||||
);
|
||||
} else {
|
||||
//Default is QBD
|
||||
|
||||
|
||||
@@ -31,6 +31,7 @@ export function JobsCloseLines({ bodyshop, job, jobRO }) {
|
||||
<th>{t("joblines.fields.line_desc")}</th>
|
||||
<th>{t("joblines.fields.part_type")}</th>
|
||||
<th>{t("joblines.fields.act_price")}</th>
|
||||
<th>{t("joblines.fields.prt_dsmk_m")}</th>
|
||||
<th>{t("joblines.fields.op_code_desc")}</th>
|
||||
<th>{t("joblines.fields.mod_lbr_ty")}</th>
|
||||
<th>{t("joblines.fields.mod_lb_hrs")}</th>
|
||||
@@ -70,6 +71,16 @@ export function JobsCloseLines({ bodyshop, job, jobRO }) {
|
||||
<ReadOnlyFormItem type="currency" />
|
||||
</Form.Item>
|
||||
</td>
|
||||
<td>
|
||||
<Form.Item
|
||||
span={2}
|
||||
// label={t("joblines.fields.prt_dsmk_m")}
|
||||
key={`${index}prt_dsmk_m`}
|
||||
name={[field.name, "prt_dsmk_m"]}
|
||||
>
|
||||
<ReadOnlyFormItem type="currency" />
|
||||
</Form.Item>
|
||||
</td>
|
||||
<td>
|
||||
<Form.Item
|
||||
span={2}
|
||||
@@ -108,7 +119,9 @@ export function JobsCloseLines({ bodyshop, job, jobRO }) {
|
||||
labelCol={{ span: 0 }}
|
||||
rules={[
|
||||
{
|
||||
required: !!job.joblines[index].act_price,
|
||||
required:
|
||||
!!job.joblines[index].act_price ||
|
||||
!!job.joblines[index].prt_dsmk_m,
|
||||
//message: t("general.validation.required"),
|
||||
},
|
||||
]}
|
||||
|
||||
@@ -35,6 +35,7 @@ export function JobsConvertButton({
|
||||
refetch,
|
||||
jobRO,
|
||||
insertAuditTrail,
|
||||
parentFormIsFieldsTouched,
|
||||
}) {
|
||||
const [visible, setVisible] = useState(false);
|
||||
const [loading, setLoading] = useState(false);
|
||||
@@ -43,6 +44,10 @@ export function JobsConvertButton({
|
||||
const [form] = Form.useForm();
|
||||
|
||||
const handleConvert = async (values) => {
|
||||
if (parentFormIsFieldsTouched()) {
|
||||
alert(t("jobs.labels.savebeforeconversion"));
|
||||
return;
|
||||
}
|
||||
setLoading(true);
|
||||
const res = await mutationConvertJob({
|
||||
variables: { jobId: job.id, ...values },
|
||||
|
||||
@@ -150,6 +150,9 @@ export function JobsCreateJobsInfo({ bodyshop, form, selected }) {
|
||||
<Form.Item label={t("jobs.fields.loss_desc")} name="loss_desc">
|
||||
<Input />
|
||||
</Form.Item>
|
||||
<Form.Item label={t("jobs.fields.loss_of_use")} name="loss_of_use">
|
||||
<Input />
|
||||
</Form.Item>
|
||||
<Form.Item label={t("jobs.fields.ponumber")} name="po_number">
|
||||
<Input />
|
||||
</Form.Item>
|
||||
@@ -197,7 +200,7 @@ export function JobsCreateJobsInfo({ bodyshop, form, selected }) {
|
||||
<JobsMarkPstExempt form={form} />
|
||||
<LayoutFormRow>
|
||||
<Form.Item label={t("jobs.fields.ded_amt")} name="ded_amt">
|
||||
<CurrencyInput />
|
||||
<CurrencyInput min={0} />
|
||||
</Form.Item>
|
||||
<Form.Item label={t("jobs.fields.ded_status")} name="ded_status">
|
||||
<Select>
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
import { DownOutlined } from "@ant-design/icons";
|
||||
import { Dropdown, Menu } from "antd";
|
||||
import React from "react";
|
||||
import { connect } from "react-redux";
|
||||
import { createStructuredSelector } from "reselect";
|
||||
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
bodyshop: selectBodyshop,
|
||||
});
|
||||
|
||||
export function JobsDetailChangeEstimator({ disabled, form, bodyshop }) {
|
||||
const handleClick = ({ item, key, keyPath }) => {
|
||||
const est = item.props.value;
|
||||
form.setFieldsValue(est);
|
||||
};
|
||||
|
||||
const menu = (
|
||||
<div>
|
||||
<Menu onClick={handleClick}>
|
||||
{bodyshop.md_estimators.map((est, idx) => (
|
||||
<Menu.Item value={est} key={idx}>
|
||||
{`${est.est_ct_fn} ${est.est_ct_ln}`}
|
||||
</Menu.Item>
|
||||
))}
|
||||
</Menu>
|
||||
</div>
|
||||
);
|
||||
|
||||
return (
|
||||
<Dropdown overlay={menu} disabled={disabled}>
|
||||
<a
|
||||
className="ant-dropdown-link"
|
||||
href=" #"
|
||||
onClick={(e) => e.preventDefault()}
|
||||
>
|
||||
<DownOutlined />
|
||||
</a>
|
||||
</Dropdown>
|
||||
);
|
||||
}
|
||||
|
||||
export default connect(mapStateToProps, null)(JobsDetailChangeEstimator);
|
||||
@@ -0,0 +1,43 @@
|
||||
import { DownOutlined } from "@ant-design/icons";
|
||||
import { Dropdown, Menu } from "antd";
|
||||
import React from "react";
|
||||
import { connect } from "react-redux";
|
||||
import { createStructuredSelector } from "reselect";
|
||||
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
bodyshop: selectBodyshop,
|
||||
});
|
||||
|
||||
export function JobsDetailChangeFilehandler({ disabled, form, bodyshop }) {
|
||||
const handleClick = ({ item, key, keyPath }) => {
|
||||
const est = item.props.value;
|
||||
form.setFieldsValue(est);
|
||||
};
|
||||
|
||||
const menu = (
|
||||
<div>
|
||||
<Menu onClick={handleClick}>
|
||||
{bodyshop.md_filehandlers.map((est, idx) => (
|
||||
<Menu.Item value={est} key={idx}>
|
||||
{`${est.ins_ct_fn} ${est.ins_ct_ln}`}
|
||||
</Menu.Item>
|
||||
))}
|
||||
</Menu>
|
||||
</div>
|
||||
);
|
||||
|
||||
return (
|
||||
<Dropdown overlay={menu} disabled={disabled}>
|
||||
<a
|
||||
className="ant-dropdown-link"
|
||||
href=" #"
|
||||
onClick={(e) => e.preventDefault()}
|
||||
>
|
||||
<DownOutlined />
|
||||
</a>
|
||||
</Dropdown>
|
||||
);
|
||||
}
|
||||
|
||||
export default connect(mapStateToProps, null)(JobsDetailChangeFilehandler);
|
||||
@@ -75,6 +75,12 @@ export function JobsDetailDatesComponent({ jobRO, job, bodyshop }) {
|
||||
>
|
||||
<DateTimePicker />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("jobs.fields.date_next_contact")}
|
||||
name="date_next_contact"
|
||||
>
|
||||
<DateTimePicker />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("jobs.fields.scheduled_completion")}
|
||||
name="scheduled_completion"
|
||||
|
||||
@@ -1,4 +1,14 @@
|
||||
import { Col, Form, Input, InputNumber, Row, Select, Switch } from "antd";
|
||||
import {
|
||||
Col,
|
||||
Divider,
|
||||
Form,
|
||||
Input,
|
||||
InputNumber,
|
||||
Row,
|
||||
Select,
|
||||
Space,
|
||||
Switch,
|
||||
} from "antd";
|
||||
import React from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { connect } from "react-redux";
|
||||
@@ -12,8 +22,9 @@ import FormItemPhone, {
|
||||
PhoneItemFormatterValidation,
|
||||
} from "../form-items-formatted/phone-form-item.component";
|
||||
import Car from "../job-damage-visual/job-damage-visual.component";
|
||||
import JobsDetailChangeEstimator from "../jobs-detail-change-estimator/jobs-detail-change-estimator.component";
|
||||
import FormRow from "../layout-form-row/layout-form-row.component";
|
||||
|
||||
import JobsDetailChangeFileHandler from "../jobs-detail-change-filehandler/jobs-detail-change-filehandler.component";
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
jobRO: selectJobReadOnly,
|
||||
bodyshop: selectBodyshop,
|
||||
@@ -44,7 +55,14 @@ export function JobsDetailGeneral({ bodyshop, jobRO, job, form }) {
|
||||
</Select>
|
||||
</Form.Item>
|
||||
<Form.Item label={t("jobs.fields.ded_amt")} name="ded_amt">
|
||||
<CurrencyInput disabled={jobRO} />
|
||||
<CurrencyInput disabled={jobRO} min={0} />
|
||||
</Form.Item>
|
||||
<Form.Item label={t("jobs.fields.ded_note")} name="ded_note">
|
||||
<Select disabled={jobRO}>
|
||||
{bodyshop.md_ded_notes.map((n, index) => (
|
||||
<Select.Option key={index}>{n}</Select.Option>
|
||||
))}
|
||||
</Select>
|
||||
</Form.Item>
|
||||
<Form.Item label={t("jobs.fields.policy_no")} name="policy_no">
|
||||
<Input disabled={jobRO} />
|
||||
@@ -68,7 +86,15 @@ export function JobsDetailGeneral({ bodyshop, jobRO, job, form }) {
|
||||
<Form.Item label={t("jobs.fields.ins_city")} name="ins_city">
|
||||
<Input disabled={jobRO} />
|
||||
</Form.Item>
|
||||
<Form.Item label={t("jobs.fields.ins_ct_ln")} name="ins_ct_ln">
|
||||
<Form.Item
|
||||
label={
|
||||
<Space>
|
||||
{t("jobs.fields.ins_ct_ln")}
|
||||
<JobsDetailChangeFileHandler form={form} disabled={jobRO} />
|
||||
</Space>
|
||||
}
|
||||
name="ins_ct_ln"
|
||||
>
|
||||
<Input disabled={jobRO} />
|
||||
</Form.Item>
|
||||
<Form.Item label={t("jobs.fields.ins_ct_fn")} name="ins_ct_fn">
|
||||
@@ -118,7 +144,7 @@ export function JobsDetailGeneral({ bodyshop, jobRO, job, form }) {
|
||||
label={t("jobs.fields.referral_source_extra")}
|
||||
name="referral_source_extra"
|
||||
>
|
||||
<Input />
|
||||
<Input disabled={jobRO} />
|
||||
</Form.Item>
|
||||
<Form.Item label={t("jobs.fields.alt_transport")} name="alt_transport">
|
||||
<Select disabled={jobRO} allowClear>
|
||||
@@ -139,6 +165,9 @@ export function JobsDetailGeneral({ bodyshop, jobRO, job, form }) {
|
||||
<Form.Item label={t("jobs.fields.loss_date")} name="loss_date">
|
||||
<FormDatePicker disabled={jobRO} />
|
||||
</Form.Item>
|
||||
<Form.Item label={t("jobs.fields.loss_of_use")} name="loss_of_use">
|
||||
<Input disabled={jobRO} />
|
||||
</Form.Item>
|
||||
<Form.Item label={t("jobs.fields.kmin")} name="kmin">
|
||||
<InputNumber precision={0} min={0} disabled={jobRO} />
|
||||
</Form.Item>
|
||||
@@ -191,12 +220,27 @@ export function JobsDetailGeneral({ bodyshop, jobRO, job, form }) {
|
||||
)}
|
||||
</Col>
|
||||
</Row>
|
||||
<Divider
|
||||
orientation="left"
|
||||
type="horizontal"
|
||||
style={{ marginTop: ".8rem", float: "right" }}
|
||||
>
|
||||
{t("jobs.forms.appraiserinfo")}
|
||||
</Divider>
|
||||
|
||||
<FormRow header={t("jobs.forms.appraiserinfo")}>
|
||||
<FormRow noDivider>
|
||||
<Form.Item label={t("jobs.fields.est_co_nm")} name="est_co_nm">
|
||||
<Input disabled={jobRO} />
|
||||
</Form.Item>
|
||||
<Form.Item label={t("jobs.fields.est_ct_fn")} name="est_ct_fn">
|
||||
<Form.Item
|
||||
label={
|
||||
<Space>
|
||||
{t("jobs.fields.est_ct_fn")}
|
||||
<JobsDetailChangeEstimator form={form} disabled={jobRO} />
|
||||
</Space>
|
||||
}
|
||||
name="est_ct_fn"
|
||||
>
|
||||
<Input disabled={jobRO} />
|
||||
</Form.Item>
|
||||
<Form.Item label={t("jobs.fields.est_ct_ln")} name="est_ct_ln">
|
||||
|
||||
@@ -101,6 +101,7 @@ export function JobsDetailHeaderActions({
|
||||
context: {
|
||||
jobId: job.id,
|
||||
job: job,
|
||||
alt_transport: job.alt_transport,
|
||||
},
|
||||
});
|
||||
}}
|
||||
|
||||
@@ -147,7 +147,7 @@ export function JobsDetailHeaderCsi({
|
||||
replyTo: bodyshop.email,
|
||||
},
|
||||
template: {
|
||||
name: TemplateList("job").csi_invitation.key,
|
||||
name: TemplateList("job_special").csi_invitation_action.key,
|
||||
variables: {
|
||||
id: job.csiinvites[0].id,
|
||||
},
|
||||
|
||||
@@ -21,47 +21,57 @@ export function JobsDetailHeaderActionexportCustomerData({
|
||||
|
||||
const handleExportCustData = async (e) => {
|
||||
logImEXEvent("job_export_cust_data");
|
||||
let QbXmlResponse;
|
||||
try {
|
||||
QbXmlResponse = await axios.post(
|
||||
"/accounting/qbxml/receivables",
|
||||
{ jobIds: [job.id], custDataOnly: true },
|
||||
{
|
||||
headers: {
|
||||
Authorization: `Bearer ${await auth.currentUser.getIdToken()}`,
|
||||
},
|
||||
}
|
||||
);
|
||||
console.log("handle -> XML", QbXmlResponse);
|
||||
} catch (error) {
|
||||
console.log("Error getting QBXML from Server.", error);
|
||||
notification["error"]({
|
||||
message: t("jobs.errors.exporting", {
|
||||
error: "Unable to retrieve QBXML. " + JSON.stringify(error.message),
|
||||
}),
|
||||
});
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
let PartnerResponse;
|
||||
try {
|
||||
PartnerResponse = await axios.post(
|
||||
"http://localhost:1337/qb/",
|
||||
QbXmlResponse.data,
|
||||
{
|
||||
headers: {
|
||||
Authorization: `Bearer ${await auth.currentUser.getIdToken()}`,
|
||||
},
|
||||
}
|
||||
);
|
||||
} catch (error) {
|
||||
console.log("Error connecting to quickbooks or partner.", error);
|
||||
notification["error"]({
|
||||
message: t("jobs.errors.exporting-partner"),
|
||||
if (bodyshop.accountingconfig && bodyshop.accountingconfig.qbo) {
|
||||
PartnerResponse = await axios.post(`/qbo/receivables`, {
|
||||
jobIds: [job.id],
|
||||
custDataOnly: true,
|
||||
});
|
||||
} else {
|
||||
//Default is QBD
|
||||
|
||||
return;
|
||||
let QbXmlResponse;
|
||||
try {
|
||||
QbXmlResponse = await axios.post(
|
||||
"/accounting/qbxml/receivables",
|
||||
{ jobIds: [job.id], custDataOnly: true },
|
||||
{
|
||||
headers: {
|
||||
Authorization: `Bearer ${await auth.currentUser.getIdToken()}`,
|
||||
},
|
||||
}
|
||||
);
|
||||
console.log("handle -> XML", QbXmlResponse);
|
||||
} catch (error) {
|
||||
console.log("Error getting QBXML from Server.", error);
|
||||
notification["error"]({
|
||||
message: t("jobs.errors.exporting", {
|
||||
error: "Unable to retrieve QBXML. " + JSON.stringify(error.message),
|
||||
}),
|
||||
});
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
//let PartnerResponse;
|
||||
try {
|
||||
PartnerResponse = await axios.post(
|
||||
"http://localhost:1337/qb/",
|
||||
QbXmlResponse.data,
|
||||
{
|
||||
headers: {
|
||||
Authorization: `Bearer ${await auth.currentUser.getIdToken()}`,
|
||||
},
|
||||
}
|
||||
);
|
||||
} catch (error) {
|
||||
console.log("Error connecting to quickbooks or partner.", error);
|
||||
notification["error"]({
|
||||
message: t("jobs.errors.exporting-partner"),
|
||||
});
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
//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);
|
||||
|
||||
@@ -16,6 +16,7 @@ import JobEmployeeAssignments from "../job-employee-assignments/job-employee-ass
|
||||
import ProductionListColumnProductionNote from "../production-list-columns/production-list-columns.productionnote.component";
|
||||
import "./jobs-detail-header.styles.scss";
|
||||
import JobsRelatedRos from "../jobs-related-ros/jobs-related-ros.component";
|
||||
import { DateTimeFormatter } from "../../utils/DateFormatter";
|
||||
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
jobRO: selectJobReadOnly,
|
||||
@@ -70,6 +71,12 @@ export function JobsDetailHeader({ job, bodyshop, disabled }) {
|
||||
{job.production_vars && job.production_vars.alert ? (
|
||||
<ExclamationCircleFilled className="production-alert" />
|
||||
) : null}
|
||||
{job.status === bodyshop.md_ro_statuses.default_scheduled &&
|
||||
job.scheduled_in ? (
|
||||
<Tag>
|
||||
<DateTimeFormatter>{job.scheduled_in}</DateTimeFormatter>
|
||||
</Tag>
|
||||
) : null}
|
||||
</Space>
|
||||
</DataLabel>
|
||||
<DataLabel label={t("jobs.fields.ins_co_nm_short")}>
|
||||
|
||||
@@ -124,7 +124,6 @@ export function JobsDetailRates({ jobRO, form, job, bodyshop }) {
|
||||
{t("jobs.forms.laborrates")}
|
||||
</Divider>
|
||||
<Space>
|
||||
<div></div>
|
||||
<JobsDetailRatesChangeButton form={form} disabled={jobRO} />
|
||||
<JobsMarkPstExempt form={form} />
|
||||
</Space>
|
||||
|
||||
@@ -38,7 +38,6 @@ export function JobsExportAllButton({
|
||||
setLoading(true);
|
||||
if (bodyshop.accountingconfig && bodyshop.accountingconfig.qbo) {
|
||||
PartnerResponse = await axios.post(`/qbo/receivables`, {
|
||||
withCredentials: true,
|
||||
jobIds: jobIds,
|
||||
});
|
||||
} else {
|
||||
@@ -170,12 +169,7 @@ export function JobsExportAllButton({
|
||||
};
|
||||
|
||||
return (
|
||||
<Button
|
||||
onClick={handleQbxml}
|
||||
loading={loading}
|
||||
disabled={disabled}
|
||||
type="dashed"
|
||||
>
|
||||
<Button onClick={handleQbxml} loading={loading} disabled={disabled}>
|
||||
{t("jobs.actions.export")}
|
||||
</Button>
|
||||
);
|
||||
|
||||
@@ -3,17 +3,17 @@ import { notification } from "antd";
|
||||
import React, { useState } from "react";
|
||||
//import SpinComponent from "../../components/loading-spinner/loading-spinner.component";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { connect } from "react-redux";
|
||||
import { createStructuredSelector } from "reselect";
|
||||
import AlertComponent from "../../components/alert/alert.component";
|
||||
import { logImEXEvent } from "../../firebase/firebase.utils";
|
||||
import {
|
||||
DELETE_NOTE,
|
||||
QUERY_NOTES_BY_JOB_PK,
|
||||
} from "../../graphql/notes.queries";
|
||||
import JobNotesComponent from "./jobs.notes.component";
|
||||
import { insertAuditTrail } from "../../redux/application/application.actions";
|
||||
import { connect } from "react-redux";
|
||||
import { createStructuredSelector } from "reselect";
|
||||
import AuditTrailMapping from "../../utils/AuditTrailMappings";
|
||||
import JobNotesComponent from "./jobs.notes.component";
|
||||
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
//currentUser: selectCurrentUser
|
||||
@@ -63,6 +63,7 @@ export function JobNotesContainer({ jobId, insertAuditTrail }) {
|
||||
refetch={refetch}
|
||||
deleteLoading={deleteLoading}
|
||||
handleNoteDelete={handleNoteDelete}
|
||||
ro_number={data ? data.jobs_by_pk.ro_number : null}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -13,7 +13,9 @@ import { createStructuredSelector } from "reselect";
|
||||
import { selectJobReadOnly } from "../../redux/application/application.selectors";
|
||||
import { setModalContext } from "../../redux/modals/modals.actions";
|
||||
import { DateTimeFormatter } from "../../utils/DateFormatter";
|
||||
import { TemplateList } from "../../utils/TemplateConstants";
|
||||
import NoteUpsertModal from "../note-upsert-modal/note-upsert-modal.container";
|
||||
import PrintWrapperComponent from "../print-wrapper/print-wrapper.component";
|
||||
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
jobRO: selectJobReadOnly,
|
||||
@@ -33,8 +35,12 @@ export function JobNotesComponent({
|
||||
jobId,
|
||||
setNoteUpsertContext,
|
||||
deleteLoading,
|
||||
ro_number,
|
||||
}) {
|
||||
const { t } = useTranslation();
|
||||
const Templates = TemplateList("job_special", {
|
||||
ro_number,
|
||||
});
|
||||
|
||||
const columns = [
|
||||
{
|
||||
@@ -107,6 +113,18 @@ export function JobNotesComponent({
|
||||
>
|
||||
<EditFilled />
|
||||
</Button>
|
||||
<PrintWrapperComponent
|
||||
emailOnly
|
||||
templateObject={{
|
||||
name: Templates.individual_job_note.key,
|
||||
|
||||
variables: { id: record.id },
|
||||
}}
|
||||
messageObject={{
|
||||
subject: Templates.individual_job_note.subject,
|
||||
}}
|
||||
id={record.id}
|
||||
/>
|
||||
</Space>
|
||||
),
|
||||
},
|
||||
|
||||
@@ -74,7 +74,6 @@ export default function OwnerFindModalComponent({
|
||||
return (
|
||||
<div>
|
||||
<Table
|
||||
title={() => t("owners.labels.existing_owners")}
|
||||
pagination={{ position: "bottom" }}
|
||||
columns={columns}
|
||||
rowKey="id"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { Modal } from "antd";
|
||||
import React from "react";
|
||||
import { useQuery } from "@apollo/client";
|
||||
import { useLazyQuery } from "@apollo/client";
|
||||
import { Input, Modal } from "antd";
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { QUERY_SEARCH_OWNER_BY_IDX } from "../../graphql/owners.queries";
|
||||
import AlertComponent from "../alert/alert.component";
|
||||
@@ -17,14 +17,27 @@ export default function OwnerFindModalContainer({
|
||||
}) {
|
||||
//use owner object to run query and find what possible owners there are.
|
||||
const { t } = useTranslation();
|
||||
const [searchText, setSearchText] = useState(null);
|
||||
|
||||
const ownersList = useQuery(QUERY_SEARCH_OWNER_BY_IDX, {
|
||||
variables: {
|
||||
search: owner ? `${owner.ownr_fn || ""} ${owner.ownr_ln || ""}` : null,
|
||||
},
|
||||
skip: !owner,
|
||||
fetchPolicy: "network-only",
|
||||
});
|
||||
const [callSearchowners, ownersList] = useLazyQuery(
|
||||
QUERY_SEARCH_OWNER_BY_IDX,
|
||||
{
|
||||
variables: {
|
||||
search: searchText,
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (modalProps.visible && owner) {
|
||||
const s = `${owner.ownr_fn || ""} ${owner.ownr_ln || ""} ${
|
||||
owner.ownr_co_nm || ""
|
||||
}`;
|
||||
|
||||
setSearchText(s.trim());
|
||||
callSearchowners({ variables: { search: s.trim() } });
|
||||
}
|
||||
}, [callSearchowners, modalProps.visible, owner]);
|
||||
|
||||
return (
|
||||
<Modal
|
||||
@@ -35,16 +48,25 @@ export default function OwnerFindModalContainer({
|
||||
{loading ? <LoadingSpinner /> : null}
|
||||
{error ? <AlertComponent message={error.message} type="error" /> : null}
|
||||
{owner ? (
|
||||
<OwnerFindModalComponent
|
||||
selectedOwner={selectedOwner}
|
||||
setSelectedOwner={setSelectedOwner}
|
||||
ownersListLoading={ownersList.loading}
|
||||
ownersList={
|
||||
ownersList.data && ownersList.data.search_owners
|
||||
? ownersList.data.search_owners
|
||||
: null
|
||||
}
|
||||
/>
|
||||
<>
|
||||
<Input.Search
|
||||
value={searchText}
|
||||
onChange={(e) => setSearchText(e.target.value)}
|
||||
onSearch={(val) =>
|
||||
callSearchowners({ variables: { search: val.trim() } })
|
||||
}
|
||||
/>
|
||||
<OwnerFindModalComponent
|
||||
selectedOwner={selectedOwner}
|
||||
setSelectedOwner={setSelectedOwner}
|
||||
ownersListLoading={ownersList.loading}
|
||||
ownersList={
|
||||
ownersList.data && ownersList.data.search_owners
|
||||
? ownersList.data.search_owners
|
||||
: null
|
||||
}
|
||||
/>
|
||||
</>
|
||||
) : null}
|
||||
</Modal>
|
||||
);
|
||||
|
||||
@@ -119,7 +119,7 @@ export function PartsOrderListTableComponent({
|
||||
</Button>
|
||||
<Popconfirm
|
||||
title={t("parts_orders.labels.confirmdelete")}
|
||||
disabled={jobRO || !record.return}
|
||||
disabled={jobRO}
|
||||
onConfirm={async () => {
|
||||
//Delete the parts return.!
|
||||
|
||||
@@ -139,7 +139,7 @@ export function PartsOrderListTableComponent({
|
||||
});
|
||||
}}
|
||||
>
|
||||
<Button disabled={jobRO || !record.return}>
|
||||
<Button disabled={jobRO}>
|
||||
<DeleteFilled />
|
||||
</Button>
|
||||
</Popconfirm>
|
||||
|
||||
@@ -40,8 +40,7 @@ export function PayableExportAll({
|
||||
setLoading(true);
|
||||
if (!!loadingCallback) loadingCallback(true);
|
||||
if (bodyshop.accountingconfig && bodyshop.accountingconfig.qbo) {
|
||||
PartnerResponse = await axios.post(`/qbo/receivables`, {
|
||||
withCredentials: true,
|
||||
PartnerResponse = await axios.post(`/qbo/payables`, {
|
||||
bills: billids,
|
||||
});
|
||||
} else {
|
||||
@@ -169,12 +168,7 @@ export function PayableExportAll({
|
||||
};
|
||||
|
||||
return (
|
||||
<Button
|
||||
onClick={handleQbxml}
|
||||
loading={loading}
|
||||
disabled={disabled}
|
||||
type="dashed"
|
||||
>
|
||||
<Button onClick={handleQbxml} loading={loading} disabled={disabled}>
|
||||
{t("jobs.actions.exportselected")}
|
||||
</Button>
|
||||
);
|
||||
|
||||
@@ -42,7 +42,6 @@ export function PayableExportButton({
|
||||
let PartnerResponse;
|
||||
if (bodyshop.accountingconfig && bodyshop.accountingconfig.qbo) {
|
||||
PartnerResponse = await axios.post(`/qbo/payables`, {
|
||||
withCredentials: true,
|
||||
bills: [billId],
|
||||
});
|
||||
} else {
|
||||
@@ -171,12 +170,7 @@ export function PayableExportButton({
|
||||
};
|
||||
|
||||
return (
|
||||
<Button
|
||||
onClick={handleQbxml}
|
||||
loading={loading}
|
||||
disabled={disabled}
|
||||
type="dashed"
|
||||
>
|
||||
<Button onClick={handleQbxml} loading={loading} disabled={disabled}>
|
||||
{t("jobs.actions.export")}
|
||||
</Button>
|
||||
);
|
||||
|
||||
@@ -8,6 +8,7 @@ import { createStructuredSelector } from "reselect";
|
||||
import { auth, logImEXEvent } from "../../firebase/firebase.utils";
|
||||
import { INSERT_EXPORT_LOG } from "../../graphql/accounting.queries";
|
||||
import { UPDATE_PAYMENTS } from "../../graphql/payments.queries";
|
||||
|
||||
import {
|
||||
selectBodyshop,
|
||||
selectCurrentUser,
|
||||
@@ -33,55 +34,63 @@ export function PaymentExportButton({
|
||||
|
||||
const handleQbxml = async () => {
|
||||
logImEXEvent("accounting_payment_export");
|
||||
|
||||
setLoading(true);
|
||||
if (!!loadingCallback) loadingCallback(true);
|
||||
|
||||
let QbXmlResponse;
|
||||
try {
|
||||
QbXmlResponse = await axios.post(
|
||||
"/accounting/qbxml/payments",
|
||||
{ payments: [paymentId] },
|
||||
{
|
||||
headers: {
|
||||
Authorization: `Bearer ${await auth.currentUser.getIdToken()}`,
|
||||
},
|
||||
}
|
||||
);
|
||||
console.log("handle -> XML", QbXmlResponse);
|
||||
} catch (error) {
|
||||
console.log("Error getting QBXML from Server.", error);
|
||||
notification["error"]({
|
||||
message: t("payments.errors.exporting", {
|
||||
error: "Unable to retrieve QBXML. " + JSON.stringify(error.message),
|
||||
}),
|
||||
});
|
||||
if (loadingCallback) loadingCallback(false);
|
||||
setLoading(false);
|
||||
return;
|
||||
}
|
||||
|
||||
//Check if it's a QBO Setup.
|
||||
let PartnerResponse;
|
||||
|
||||
try {
|
||||
PartnerResponse = await axios.post(
|
||||
"http://localhost:1337/qb/",
|
||||
//"http://609feaeae986.ngrok.io/qb/",
|
||||
QbXmlResponse.data
|
||||
);
|
||||
} catch (error) {
|
||||
console.log("Error connecting to quickbooks or partner.", error);
|
||||
notification["error"]({
|
||||
message: t("payments.errors.exporting-partner"),
|
||||
if (bodyshop.accountingconfig && bodyshop.accountingconfig.qbo) {
|
||||
PartnerResponse = await axios.post(`/qbo/payments`, {
|
||||
payments: [paymentId],
|
||||
});
|
||||
if (!!loadingCallback) loadingCallback(false);
|
||||
setLoading(false);
|
||||
return;
|
||||
} else {
|
||||
//Default is QBD
|
||||
|
||||
if (!!loadingCallback) loadingCallback(true);
|
||||
|
||||
let QbXmlResponse;
|
||||
try {
|
||||
QbXmlResponse = await axios.post(
|
||||
"/accounting/qbxml/payments",
|
||||
{ payments: [paymentId] },
|
||||
{
|
||||
headers: {
|
||||
Authorization: `Bearer ${await auth.currentUser.getIdToken()}`,
|
||||
},
|
||||
}
|
||||
);
|
||||
console.log("handle -> XML", QbXmlResponse);
|
||||
} catch (error) {
|
||||
console.log("Error getting QBXML from Server.", error);
|
||||
notification["error"]({
|
||||
message: t("payments.errors.exporting", {
|
||||
error: "Unable to retrieve QBXML. " + JSON.stringify(error.message),
|
||||
}),
|
||||
});
|
||||
if (loadingCallback) loadingCallback(false);
|
||||
setLoading(false);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
PartnerResponse = await axios.post(
|
||||
"http://localhost:1337/qb/",
|
||||
QbXmlResponse.data
|
||||
);
|
||||
} catch (error) {
|
||||
console.log("Error connecting to quickbooks or partner.", error);
|
||||
notification["error"]({
|
||||
message: t("payments.errors.exporting-partner"),
|
||||
});
|
||||
if (!!loadingCallback) loadingCallback(false);
|
||||
setLoading(false);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
console.log("handleQbxml -> PartnerResponse", PartnerResponse);
|
||||
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.map((ft) =>
|
||||
@@ -123,7 +132,14 @@ export function PaymentExportButton({
|
||||
|
||||
const paymentUpdateResponse = await updatePayment({
|
||||
variables: {
|
||||
paymentIdList: [paymentId],
|
||||
paymentIdList: successfulTransactions.map(
|
||||
(st) =>
|
||||
st[
|
||||
bodyshop.accountingconfig && bodyshop.accountingconfig.qbo
|
||||
? "paymentid"
|
||||
: "id"
|
||||
]
|
||||
),
|
||||
payment: {
|
||||
exportedat: new Date(),
|
||||
},
|
||||
@@ -155,12 +171,7 @@ export function PaymentExportButton({
|
||||
};
|
||||
|
||||
return (
|
||||
<Button
|
||||
onClick={handleQbxml}
|
||||
loading={loading}
|
||||
disabled={disabled}
|
||||
type="dashed"
|
||||
>
|
||||
<Button onClick={handleQbxml} loading={loading} disabled={disabled}>
|
||||
{t("jobs.actions.export")}
|
||||
</Button>
|
||||
);
|
||||
|
||||
@@ -8,6 +8,7 @@ import { connect } from "react-redux";
|
||||
import { createStructuredSelector } from "reselect";
|
||||
import { INSERT_EXPORT_LOG } from "../../graphql/accounting.queries";
|
||||
import { UPDATE_PAYMENTS } from "../../graphql/payments.queries";
|
||||
|
||||
import {
|
||||
selectBodyshop,
|
||||
selectCurrentUser,
|
||||
@@ -33,42 +34,49 @@ export function PaymentsExportAllButton({
|
||||
const handleQbxml = async () => {
|
||||
setLoading(true);
|
||||
if (!!loadingCallback) loadingCallback(true);
|
||||
|
||||
let QbXmlResponse;
|
||||
try {
|
||||
QbXmlResponse = await axios.post("/accounting/qbxml/payments", {
|
||||
let PartnerResponse;
|
||||
if (bodyshop.accountingconfig && bodyshop.accountingconfig.qbo) {
|
||||
PartnerResponse = await axios.post(`/qbo/payments`, {
|
||||
payments: paymentIds,
|
||||
});
|
||||
} catch (error) {
|
||||
console.log("Error getting QBXML from Server.", error);
|
||||
notification["error"]({
|
||||
message: t("payments.errors.exporting", {
|
||||
error: "Unable to retrieve QBXML. " + JSON.stringify(error.message),
|
||||
}),
|
||||
});
|
||||
if (loadingCallback) loadingCallback(false);
|
||||
setLoading(false);
|
||||
return;
|
||||
} else {
|
||||
let QbXmlResponse;
|
||||
try {
|
||||
QbXmlResponse = await axios.post("/accounting/qbxml/payments", {
|
||||
payments: paymentIds,
|
||||
});
|
||||
} catch (error) {
|
||||
console.log("Error getting QBXML from Server.", error);
|
||||
notification["error"]({
|
||||
message: t("payments.errors.exporting", {
|
||||
error: "Unable to retrieve QBXML. " + JSON.stringify(error.message),
|
||||
}),
|
||||
});
|
||||
if (loadingCallback) loadingCallback(false);
|
||||
setLoading(false);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
PartnerResponse = await axios.post(
|
||||
"http://localhost:1337/qb/",
|
||||
QbXmlResponse.data
|
||||
);
|
||||
} catch (error) {
|
||||
console.log("Error connecting to quickbooks or partner.", error);
|
||||
notification["error"]({
|
||||
message: t("payments.errors.exporting-partner"),
|
||||
});
|
||||
if (!!loadingCallback) loadingCallback(false);
|
||||
setLoading(false);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
let PartnerResponse;
|
||||
|
||||
try {
|
||||
PartnerResponse = await axios.post(
|
||||
"http://localhost:1337/qb/",
|
||||
QbXmlResponse.data
|
||||
);
|
||||
} catch (error) {
|
||||
console.log("Error connecting to quickbooks or partner.", error);
|
||||
notification["error"]({
|
||||
message: t("payments.errors.exporting-partner"),
|
||||
});
|
||||
if (!!loadingCallback) loadingCallback(false);
|
||||
setLoading(false);
|
||||
return;
|
||||
}
|
||||
|
||||
const groupedData = _.groupBy(PartnerResponse.data, "id");
|
||||
const groupedData = _.groupBy(
|
||||
PartnerResponse.data,
|
||||
bodyshop.accountingconfig.qbo ? "paymentid" : "id"
|
||||
);
|
||||
const proms = [];
|
||||
Object.keys(groupedData).forEach((key) => {
|
||||
proms.push(
|
||||
@@ -144,12 +152,7 @@ export function PaymentsExportAllButton({
|
||||
};
|
||||
|
||||
return (
|
||||
<Button
|
||||
onClick={handleQbxml}
|
||||
loading={loading}
|
||||
disabled={disabled}
|
||||
type="dashed"
|
||||
>
|
||||
<Button onClick={handleQbxml} loading={loading} disabled={disabled}>
|
||||
{t("jobs.actions.exportselected")}
|
||||
</Button>
|
||||
);
|
||||
|
||||
@@ -8,6 +8,7 @@ export default function PrintWrapperComponent({
|
||||
messageObject = {},
|
||||
children,
|
||||
id,
|
||||
emailOnly = false,
|
||||
}) {
|
||||
const [loading, setLoading] = useState(false);
|
||||
const handlePrint = async (type) => {
|
||||
@@ -19,7 +20,7 @@ export default function PrintWrapperComponent({
|
||||
return (
|
||||
<Space>
|
||||
{children || null}
|
||||
<PrinterFilled onClick={() => handlePrint("p")} />
|
||||
{!emailOnly && <PrinterFilled onClick={() => handlePrint("p")} />}
|
||||
<MailFilled onClick={() => handlePrint("e")} />
|
||||
{loading && <Spin />}
|
||||
</Space>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Input, PageHeader, Space, Spin } from "antd";
|
||||
import { Input, Space, Spin } from "antd";
|
||||
import React from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { connect } from "react-redux";
|
||||
@@ -26,8 +26,7 @@ export function ProductionBoardFilters({
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
<PageHeader
|
||||
extra={
|
||||
|
||||
<Space wrap>
|
||||
{loading && <Spin />}
|
||||
<Input.Search
|
||||
@@ -46,7 +45,6 @@ export function ProductionBoardFilters({
|
||||
allowClear
|
||||
/>
|
||||
</Space>
|
||||
}
|
||||
></PageHeader>
|
||||
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,106 +1,171 @@
|
||||
import { CalendarOutlined, EyeFilled } from "@ant-design/icons";
|
||||
import { Card, Col, Row, Space } from "antd";
|
||||
import React from "react";
|
||||
import { Card, Row, Col, Dropdown } from "antd";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { Link } from "react-router-dom";
|
||||
import { DateTimeFormatter } from "../../utils/DateFormatter";
|
||||
import ProductionAlert from "../production-list-columns/production-list-columns.alert.component";
|
||||
import { EyeFilled } from "@ant-design/icons";
|
||||
import { Link } from "react-router-dom";
|
||||
import "./production-board-card.styles.scss";
|
||||
import ProductionRemoveButton from "../production-remove-button/production-remove-button.component";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import ProductionSubletsManageComponent from "../production-sublets-manage/production-sublets-manage.component";
|
||||
import ProductionListColumnProductionNote from "../production-list-columns/production-list-columns.productionnote.component";
|
||||
import ProductionSubletsManageComponent from "../production-sublets-manage/production-sublets-manage.component";
|
||||
import "./production-board-card.styles.scss";
|
||||
|
||||
export default function ProductionBoardCard(technician, card) {
|
||||
export default function ProductionBoardCard(
|
||||
technician,
|
||||
card,
|
||||
bodyshop,
|
||||
cardSettings
|
||||
) {
|
||||
const { t } = useTranslation();
|
||||
const menu = (
|
||||
<div>
|
||||
<Card title={t("general.labels.actions")}>
|
||||
<ProductionRemoveButton jobId={card.id} />
|
||||
</Card>
|
||||
</div>
|
||||
);
|
||||
|
||||
let employee_body, employee_prep, employee_refinish; //employee_csr;
|
||||
if (card.employee_body) {
|
||||
employee_body = bodyshop.employees.find((e) => e.id === card.employee_body);
|
||||
}
|
||||
if (card.employee_prep) {
|
||||
employee_prep = bodyshop.employees.find((e) => e.id === card.employee_prep);
|
||||
}
|
||||
if (card.employee_refinish) {
|
||||
employee_refinish = bodyshop.employees.find(
|
||||
(e) => e.id === card.employee_refinish
|
||||
);
|
||||
}
|
||||
// if (card.employee_csr) {
|
||||
// employee_csr = bodyshop.employees.find((e) => e.id === card.employee_csr);
|
||||
// }
|
||||
|
||||
return (
|
||||
<Dropdown overlay={menu} trigger={["contextMenu"]}>
|
||||
<Card
|
||||
className="react-kanban-card imex-kanban-card tight-antd-rows"
|
||||
//style={{ margin: ".2rem 0rem" }}
|
||||
title={`${card.ro_number || t("general.labels.na")} - ${
|
||||
card.v_model_yr
|
||||
} ${card.v_make_desc || ""} ${card.v_model_desc || ""}`}
|
||||
>
|
||||
<Row>
|
||||
<Card
|
||||
className="react-kanban-card imex-kanban-card"
|
||||
size="small"
|
||||
title={
|
||||
<Space>
|
||||
<ProductionAlert record={card} key="alert" />
|
||||
<span style={{ fontWeight: "bolder" }}>
|
||||
{card.ro_number || t("general.labels.na")}
|
||||
</span>
|
||||
</Space>
|
||||
}
|
||||
extra={
|
||||
technician ? (
|
||||
<Link to={`/tech/joblookup?selected=${card.id}`}>
|
||||
<EyeFilled />
|
||||
</Link>
|
||||
) : (
|
||||
<Link to={`/manage/jobs/${card.id}`}>
|
||||
<EyeFilled />
|
||||
</Link>
|
||||
)
|
||||
}
|
||||
>
|
||||
<Row>
|
||||
{cardSettings && cardSettings.ownr_nm && (
|
||||
<Col span={24}>
|
||||
<div className="ellipses">{`${card.ownr_fn || ""} ${
|
||||
card.ownr_ln || ""
|
||||
} ${card.ownr_co_nm || ""}`}</div>
|
||||
{cardSettings && cardSettings.compact ? (
|
||||
<div className="ellipses">{`${card.ownr_ln || ""} ${
|
||||
card.ownr_co_nm || ""
|
||||
}`}</div>
|
||||
) : (
|
||||
<div className="ellipses">{`${card.ownr_ln || ""}, ${
|
||||
card.ownr_fn || ""
|
||||
} ${card.ownr_co_nm || ""}`}</div>
|
||||
)}
|
||||
</Col>
|
||||
</Row>
|
||||
<Row>
|
||||
<Col span={12}>
|
||||
<div className="ellipses">{card.clm_no || ""}</div>
|
||||
</Col>
|
||||
<Col span={12}>
|
||||
)}
|
||||
<Col span={24}>
|
||||
<div className="ellipses">{`${card.v_model_yr || ""} ${
|
||||
card.v_make_desc || ""
|
||||
} ${card.v_model_desc || ""}`}</div>
|
||||
</Col>
|
||||
{cardSettings && cardSettings.ins_co_nm && card.ins_co_nm && (
|
||||
<Col span={cardSettings && cardSettings.compact ? 24 : 12}>
|
||||
<div className="ellipses">{card.ins_co_nm || ""}</div>
|
||||
</Col>
|
||||
</Row>
|
||||
<Row>
|
||||
)}
|
||||
{cardSettings && cardSettings.clm_no && card.clm_no && (
|
||||
<Col span={cardSettings && cardSettings.compact ? 24 : 12}>
|
||||
<div className="ellipses">{card.clm_no || ""}</div>
|
||||
</Col>
|
||||
)}
|
||||
|
||||
{cardSettings && cardSettings.employeeassignments && (
|
||||
<Col span={24}>
|
||||
<div className="imex-flex-row imex-flex-row__flex-space-around">
|
||||
<div className="mex-flex-row__margin">
|
||||
<div>{`B: ${card.labhrs.aggregate.sum.mod_lb_hrs || "?"}`}</div>
|
||||
<div>{`R: ${card.larhrs.aggregate.sum.mod_lb_hrs || "?"}`}</div>
|
||||
</div>
|
||||
<div className="mex-flex-row__margin">
|
||||
<div>{`B: ${
|
||||
card.employee_body_rel
|
||||
? `${card.employee_body_rel.first_name} ${card.employee_body_rel.last_name}`
|
||||
: ""
|
||||
}`}</div>
|
||||
<div>{`P: ${
|
||||
card.employee_prep_rel
|
||||
? `${card.employee_prep_rel.first_name} ${card.employee_prep_rel.last_name}`
|
||||
: ""
|
||||
}`}</div>
|
||||
<div>{`R: ${
|
||||
card.employee_refinish_rel
|
||||
? `${card.employee_refinish_rel.first_name} ${card.employee_refinish_rel.last_name}`
|
||||
: ""
|
||||
}`}</div>
|
||||
<div>{`CSR: ${
|
||||
card.employee_csr_rel
|
||||
? `${card.employee_csr_rel.first_name} ${card.employee_csr_rel.last_name}`
|
||||
: ""
|
||||
}`}</div>
|
||||
</div>
|
||||
</div>
|
||||
<Row>
|
||||
<Col span={cardSettings && cardSettings.compact ? 24 : 12}>{`B: ${
|
||||
employee_body
|
||||
? `${employee_body.first_name.substr(
|
||||
0,
|
||||
3
|
||||
)} ${employee_body.last_name.charAt(0)}`
|
||||
: ""
|
||||
} ${card.labhrs.aggregate.sum.mod_lb_hrs || "?"}h`}</Col>
|
||||
<Col span={cardSettings && cardSettings.compact ? 24 : 12}>{`P: ${
|
||||
employee_prep
|
||||
? `${employee_prep.first_name.substr(
|
||||
0,
|
||||
3
|
||||
)} ${employee_prep.last_name.charAt(0)}`
|
||||
: ""
|
||||
}`}</Col>
|
||||
<Col span={cardSettings && cardSettings.compact ? 24 : 12}>{`R: ${
|
||||
employee_refinish
|
||||
? `${employee_refinish.first_name.substr(
|
||||
0,
|
||||
3
|
||||
)} ${employee_refinish.last_name.charAt(0)}`
|
||||
: ""
|
||||
} ${card.larhrs.aggregate.sum.mod_lb_hrs || "?"}h`}</Col>
|
||||
{/* <Col span={cardSettings && cardSettings.compact ? 24 : 12}>{`C: ${
|
||||
employee_csr
|
||||
? `${employee_csr.first_name} ${employee_csr.last_name}`
|
||||
: ""
|
||||
}`}</Col> */}
|
||||
</Row>
|
||||
</Col>
|
||||
</Row>
|
||||
<Row>
|
||||
<Col span={18}>
|
||||
<DateTimeFormatter>{card.scheduled_completion}</DateTimeFormatter>
|
||||
)}
|
||||
{/* {cardSettings && cardSettings.laborhrs && (
|
||||
<Col span={24}>
|
||||
<Row>
|
||||
<Col span={cardSettings && cardSettings.compact ? 24 : 12}>{`B: ${
|
||||
card.labhrs.aggregate.sum.mod_lb_hrs || "?"
|
||||
} hrs`}</Col>
|
||||
<Col span={cardSettings && cardSettings.compact ? 24 : 12}>{`R: ${
|
||||
card.larhrs.aggregate.sum.mod_lb_hrs || "?"
|
||||
} hrs`}</Col>
|
||||
</Row>
|
||||
</Col>
|
||||
<Col span={6}>
|
||||
)} */}
|
||||
{cardSettings &&
|
||||
cardSettings.scheduled_completion &&
|
||||
card.scheduled_completion && (
|
||||
<Col span={cardSettings && cardSettings.compact ? 24 : 12}>
|
||||
<Space>
|
||||
<CalendarOutlined />
|
||||
<DateTimeFormatter format="MM/DD">
|
||||
{card.scheduled_completion}
|
||||
</DateTimeFormatter>
|
||||
</Space>
|
||||
</Col>
|
||||
)}
|
||||
{cardSettings && cardSettings.ats && card.alt_transport && (
|
||||
<Col span={12}>
|
||||
<div>{card.alt_transport || ""}</div>
|
||||
</Col>
|
||||
</Row>
|
||||
<div>
|
||||
<ProductionListColumnProductionNote record={card} />
|
||||
</div>
|
||||
<div className="imex-flex-row imex-flex-row__flex-space-around">
|
||||
<ProductionAlert record={card} key="alert" />
|
||||
<ProductionSubletsManageComponent subletJobLines={card.subletLines} />
|
||||
{technician ? (
|
||||
<Link to={`/tech/joblookup?selected=${card.id}`}>
|
||||
<EyeFilled />
|
||||
</Link>
|
||||
) : (
|
||||
<Link to={`/manage/jobs/${card.id}`}>
|
||||
<EyeFilled />
|
||||
</Link>
|
||||
)}
|
||||
</div>
|
||||
</Card>
|
||||
</Dropdown>
|
||||
)}
|
||||
{cardSettings && cardSettings.sublets && (
|
||||
<Col span={12}>
|
||||
<ProductionSubletsManageComponent
|
||||
subletJobLines={card.subletLines}
|
||||
/>
|
||||
</Col>
|
||||
)}
|
||||
{cardSettings && cardSettings.production_note && (
|
||||
<Col span={24}>
|
||||
{cardSettings && cardSettings.production_note && (
|
||||
<ProductionListColumnProductionNote record={card} />
|
||||
)}
|
||||
</Col>
|
||||
)}
|
||||
</Row>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,154 @@
|
||||
import { useMutation } from "@apollo/client";
|
||||
import {
|
||||
Button,
|
||||
Card,
|
||||
Col,
|
||||
Form,
|
||||
notification,
|
||||
Popover,
|
||||
Row,
|
||||
Switch,
|
||||
} from "antd";
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { UPDATE_KANBAN_SETTINGS } from "../../graphql/user.queries";
|
||||
|
||||
export default function ProductionBoardKanbanCardSettings({
|
||||
associationSettings,
|
||||
}) {
|
||||
const [form] = Form.useForm();
|
||||
const [visible, setVisible] = useState(false);
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [updateKbSettings] = useMutation(UPDATE_KANBAN_SETTINGS);
|
||||
|
||||
useEffect(() => {
|
||||
form.setFieldsValue(
|
||||
associationSettings && associationSettings.kanban_settings
|
||||
);
|
||||
}, [form, associationSettings, visible]);
|
||||
|
||||
const { t } = useTranslation();
|
||||
|
||||
const handleFinish = async (values) => {
|
||||
setLoading(true);
|
||||
const result = await updateKbSettings({
|
||||
variables: {
|
||||
id: associationSettings && associationSettings.id,
|
||||
ks: values,
|
||||
},
|
||||
});
|
||||
if (result.errors) {
|
||||
notification.open({
|
||||
type: "error",
|
||||
message: t("production.errors.settings", {
|
||||
error: JSON.stringify(result.errors),
|
||||
}),
|
||||
});
|
||||
}
|
||||
setVisible(false);
|
||||
setLoading(false);
|
||||
};
|
||||
|
||||
const overlay = (
|
||||
<div>
|
||||
<Card>
|
||||
<Form form={form} onFinish={handleFinish} layout="vertical">
|
||||
<Row gutter={[16, 16]}>
|
||||
<Col span={12}>
|
||||
<Form.Item
|
||||
label={t("production.labels.compact")}
|
||||
name="compact"
|
||||
valuePropName="checked"
|
||||
>
|
||||
<Switch />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
valuePropName="checked"
|
||||
label={t("production.labels.ownr_nm")}
|
||||
name="ownr_nm"
|
||||
>
|
||||
<Switch />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
valuePropName="checked"
|
||||
label={t("production.labels.clm_no")}
|
||||
name="clm_no"
|
||||
>
|
||||
<Switch />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
valuePropName="checked"
|
||||
label={t("production.labels.ins_co_nm")}
|
||||
name="ins_co_nm"
|
||||
>
|
||||
<Switch />
|
||||
</Form.Item>
|
||||
{/* <Form.Item
|
||||
valuePropName="checked"
|
||||
label={t("production.labels.laborhrs")}
|
||||
name="laborhrs"
|
||||
>
|
||||
<Switch />
|
||||
</Form.Item> */}
|
||||
<Form.Item
|
||||
valuePropName="checked"
|
||||
label={t("production.labels.employeeassignments")}
|
||||
name="employeeassignments"
|
||||
>
|
||||
<Switch />
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col span={12}>
|
||||
<Form.Item
|
||||
valuePropName="checked"
|
||||
label={t("production.labels.scheduled_completion")}
|
||||
name="scheduled_completion"
|
||||
>
|
||||
<Switch />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
valuePropName="checked"
|
||||
label={t("production.labels.ats")}
|
||||
name="ats"
|
||||
>
|
||||
<Switch />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
valuePropName="checked"
|
||||
label={t("production.labels.production_note")}
|
||||
name="production_note"
|
||||
>
|
||||
<Switch />
|
||||
</Form.Item>
|
||||
{/* <Form.Item
|
||||
valuePropName='checked' label={t("production.labels.alert")} name="alert">
|
||||
<Switch/>
|
||||
</Form.Item> */}
|
||||
<Form.Item
|
||||
valuePropName="checked"
|
||||
label={t("production.labels.sublets")}
|
||||
name="sublets"
|
||||
>
|
||||
<Switch />
|
||||
</Form.Item>
|
||||
</Col>
|
||||
</Row>
|
||||
</Form>
|
||||
<Button
|
||||
onClick={() => {
|
||||
form.submit();
|
||||
}}
|
||||
>
|
||||
{t("general.actions.save")}
|
||||
</Button>
|
||||
</Card>
|
||||
</div>
|
||||
);
|
||||
return (
|
||||
<Popover content={overlay} visible={visible}>
|
||||
<Button loading={loading} onClick={() => setVisible(true)}>
|
||||
{t("production.labels.cardsettings")}
|
||||
</Button>
|
||||
</Popover>
|
||||
);
|
||||
}
|
||||
@@ -1,7 +1,8 @@
|
||||
import { useApolloClient } from "@apollo/client";
|
||||
import Board, { moveCard } from "@lourenci/react-kanban";
|
||||
import "@lourenci/react-kanban/dist/styles.css";
|
||||
import { notification } from "antd";
|
||||
import Board, { moveCard } from "@asseinfo/react-kanban";
|
||||
//import "@asseinfo/react-kanban/dist/styles.css";
|
||||
import "./production-board-kanban.styles.scss";
|
||||
import { Grid, notification, PageHeader, Space, Statistic } from "antd";
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { connect } from "react-redux";
|
||||
@@ -16,6 +17,8 @@ import ProductionBoardFilters from "../production-board-filters/production-board
|
||||
import { selectTechnician } from "../../redux/tech/tech.selectors";
|
||||
import { insertAuditTrail } from "../../redux/application/application.actions";
|
||||
import AuditTrailMapping from "../../utils/AuditTrailMappings";
|
||||
import ProductionBoardKanbanCardSettings from "./production-board-kanban.card-settings.component";
|
||||
import styled from "styled-components";
|
||||
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
bodyshop: selectBodyshop,
|
||||
@@ -32,6 +35,7 @@ export function ProductionBoardKanbanComponent({
|
||||
bodyshop,
|
||||
technician,
|
||||
insertAuditTrail,
|
||||
associationSettings,
|
||||
}) {
|
||||
const [boardLanes, setBoardLanes] = useState({
|
||||
columns: [{ id: "Loading...", title: "Loading...", cards: [] }],
|
||||
@@ -116,6 +120,7 @@ export function ProductionBoardKanbanComponent({
|
||||
newChildCard ? newChildCard.id : null,
|
||||
newChildCardNewParent
|
||||
),
|
||||
// TODO: optimisticResponse
|
||||
});
|
||||
insertAuditTrail({
|
||||
jobid: card.id,
|
||||
@@ -131,21 +136,112 @@ export function ProductionBoardKanbanComponent({
|
||||
}
|
||||
};
|
||||
|
||||
const totalHrs = data
|
||||
.reduce(
|
||||
(acc, val) =>
|
||||
acc +
|
||||
(val.labhrs?.aggregate?.sum?.mod_lb_hrs || 0) +
|
||||
(val.larhrs?.aggregate?.sum?.mod_lb_hrs || 0),
|
||||
0
|
||||
)
|
||||
.toFixed(1);
|
||||
const selectedBreakpoint = Object.entries(Grid.useBreakpoint())
|
||||
.filter((screen) => !!screen[1])
|
||||
.slice(-1)[0];
|
||||
|
||||
const standardSizes = {
|
||||
xs: "250",
|
||||
sm: "250",
|
||||
md: "250",
|
||||
lg: "250",
|
||||
xl: "250",
|
||||
xxl: "250",
|
||||
};
|
||||
const compactSizes = {
|
||||
xs: "150",
|
||||
sm: "150",
|
||||
md: "150",
|
||||
lg: "150",
|
||||
xl: "155",
|
||||
xxl: "155",
|
||||
};
|
||||
|
||||
const width = selectedBreakpoint
|
||||
? associationSettings &&
|
||||
associationSettings.kanban_settings &&
|
||||
associationSettings.kanban_settings.compact
|
||||
? compactSizes[selectedBreakpoint[0]]
|
||||
: standardSizes[selectedBreakpoint[0]]
|
||||
: "250";
|
||||
|
||||
const Container = styled.div`
|
||||
.react-kanban-card-skeleton,
|
||||
.react-kanban-card,
|
||||
.react-kanban-card-adder-form {
|
||||
box-sizing: border-box;
|
||||
max-width: ${width}px;
|
||||
min-width: ${width}px;
|
||||
}
|
||||
`;
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Container>
|
||||
<IndefiniteLoading loading={isMoving} />
|
||||
<ProductionBoardFilters
|
||||
filter={filter}
|
||||
setFilter={setFilter}
|
||||
loading={isMoving}
|
||||
<PageHeader
|
||||
title={
|
||||
<Space>
|
||||
<Statistic
|
||||
title={t("dashboard.titles.productionhours")}
|
||||
value={totalHrs}
|
||||
/>
|
||||
<Statistic
|
||||
title={t("appointments.labels.inproduction")}
|
||||
value={data && data.length}
|
||||
/>
|
||||
</Space>
|
||||
}
|
||||
extra={
|
||||
<Space wrap>
|
||||
<ProductionBoardFilters
|
||||
filter={filter}
|
||||
setFilter={setFilter}
|
||||
loading={isMoving}
|
||||
/>
|
||||
<ProductionBoardKanbanCardSettings
|
||||
associationSettings={associationSettings}
|
||||
/>
|
||||
</Space>
|
||||
}
|
||||
/>
|
||||
|
||||
<Board
|
||||
children={boardLanes}
|
||||
disableCardDrag={isMoving}
|
||||
renderCard={(card) => ProductionBoardCard(technician, card)}
|
||||
renderCard={(card) =>
|
||||
ProductionBoardCard(
|
||||
technician,
|
||||
card,
|
||||
bodyshop,
|
||||
associationSettings &&
|
||||
associationSettings.kanban_settings &&
|
||||
Object.keys(associationSettings.kanban_settings).length > 0
|
||||
? associationSettings.kanban_settings
|
||||
: {
|
||||
ats: true,
|
||||
clm_no: true,
|
||||
compact: false,
|
||||
ownr_nm: true,
|
||||
sublets: true,
|
||||
ins_co_nm: true,
|
||||
production_note: true,
|
||||
employeeassignments: true,
|
||||
scheduled_completion: true,
|
||||
}
|
||||
)
|
||||
}
|
||||
onCardDragEnd={handleDragEnd}
|
||||
/>
|
||||
</div>
|
||||
</Container>
|
||||
);
|
||||
}
|
||||
export default connect(
|
||||
|
||||
@@ -1,25 +1,40 @@
|
||||
import { useSubscription } from "@apollo/client";
|
||||
import { useQuery, useSubscription } from "@apollo/client";
|
||||
import React from "react";
|
||||
import { connect } from "react-redux";
|
||||
import { createStructuredSelector } from "reselect";
|
||||
import { SUBSCRIPTION_JOBS_IN_PRODUCTION } from "../../graphql/jobs.queries";
|
||||
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||
import { QUERY_KANBAN_SETTINGS } from "../../graphql/user.queries";
|
||||
import {
|
||||
selectBodyshop,
|
||||
selectCurrentUser,
|
||||
} from "../../redux/user/user.selectors";
|
||||
import ProductionBoardKanbanComponent from "./production-board-kanban.component";
|
||||
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
bodyshop: selectBodyshop,
|
||||
currentUser: selectCurrentUser,
|
||||
});
|
||||
|
||||
export function ProductionBoardKanbanContainer({ bodyshop }) {
|
||||
export function ProductionBoardKanbanContainer({ bodyshop, currentUser }) {
|
||||
const { loading, data } = useSubscription(
|
||||
SUBSCRIPTION_JOBS_IN_PRODUCTION,
|
||||
{}
|
||||
);
|
||||
|
||||
const { loading: associationSettingsLoading, data: associationSettings } =
|
||||
useQuery(QUERY_KANBAN_SETTINGS, {
|
||||
variables: { email: currentUser.email },
|
||||
});
|
||||
|
||||
return (
|
||||
<ProductionBoardKanbanComponent
|
||||
loading={loading}
|
||||
loading={loading || associationSettingsLoading}
|
||||
data={data ? data.jobs : []}
|
||||
associationSettings={
|
||||
associationSettings && associationSettings.associations[0]
|
||||
? associationSettings.associations[0]
|
||||
: null
|
||||
}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,123 @@
|
||||
.react-kanban-board {
|
||||
padding: 5px;
|
||||
}
|
||||
.react-kanban-card {
|
||||
border-radius: 3px;
|
||||
background-color: #fff;
|
||||
padding: 4px;
|
||||
margin-bottom: 7px;
|
||||
}
|
||||
// .react-kanban-card-skeleton,
|
||||
// .react-kanban-card,
|
||||
// .react-kanban-card-adder-form {
|
||||
// box-sizing: border-box;
|
||||
// max-width: 145px;
|
||||
// min-width: 145px;
|
||||
// }
|
||||
|
||||
.react-kanban-card--dragging {
|
||||
box-shadow: 2px 2px grey;
|
||||
}
|
||||
.react-kanban-card__description {
|
||||
padding-top: 10px;
|
||||
}
|
||||
.react-kanban-card__title {
|
||||
border-bottom: 1px solid #eee;
|
||||
padding-bottom: 5px;
|
||||
font-weight: bold;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
.react-kanban-column {
|
||||
padding: 10px;
|
||||
border-radius: 2px;
|
||||
background-color: #eee;
|
||||
margin: 5px;
|
||||
}
|
||||
.react-kanban-column input:focus {
|
||||
outline: none;
|
||||
}
|
||||
.react-kanban-card-adder-form {
|
||||
border-radius: 3px;
|
||||
background-color: #fff;
|
||||
padding: 10px;
|
||||
margin-bottom: 7px;
|
||||
}
|
||||
.react-kanban-card-adder-form input {
|
||||
border: 0px;
|
||||
font-family: inherit;
|
||||
font-size: inherit;
|
||||
}
|
||||
.react-kanban-card-adder-button {
|
||||
width: 100%;
|
||||
margin-top: 5px;
|
||||
background-color: transparent;
|
||||
cursor: pointer;
|
||||
border: 1px solid #ccc;
|
||||
transition: 0.3s;
|
||||
border-radius: 3px;
|
||||
font-size: 20px;
|
||||
margin-bottom: 10px;
|
||||
font-weight: bold;
|
||||
}
|
||||
.react-kanban-card-adder-button:hover {
|
||||
background-color: #ccc;
|
||||
}
|
||||
.react-kanban-card-adder-form__title {
|
||||
font-weight: bold;
|
||||
border-bottom: 1px solid #eee;
|
||||
padding-bottom: 5px;
|
||||
font-weight: bold;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
width: 100%;
|
||||
padding: 0px;
|
||||
}
|
||||
.react-kanban-card-adder-form__title:focus {
|
||||
outline: none;
|
||||
}
|
||||
.react-kanban-card-adder-form__description {
|
||||
width: 100%;
|
||||
margin-top: 10px;
|
||||
}
|
||||
.react-kanban-card-adder-form__description:focus {
|
||||
outline: none;
|
||||
}
|
||||
.react-kanban-card-adder-form__button {
|
||||
background-color: #eee;
|
||||
border: none;
|
||||
padding: 5px;
|
||||
width: 45%;
|
||||
margin-top: 5px;
|
||||
border-radius: 3px;
|
||||
}
|
||||
.react-kanban-card-adder-form__button:hover {
|
||||
transition: 0.3s;
|
||||
cursor: pointer;
|
||||
background-color: #ccc;
|
||||
}
|
||||
.react-kanban-column-header {
|
||||
padding-bottom: 10px;
|
||||
font-weight: bold;
|
||||
}
|
||||
.react-kanban-column-header input:focus {
|
||||
outline: none;
|
||||
}
|
||||
.react-kanban-column-header__button {
|
||||
color: #333333;
|
||||
background-color: #ffffff;
|
||||
border-color: #cccccc;
|
||||
}
|
||||
.react-kanban-column-header__button:hover,
|
||||
.react-kanban-column-header__button:focus,
|
||||
.react-kanban-column-header__button:active {
|
||||
background-color: #e6e6e6;
|
||||
}
|
||||
.react-kanban-column-adder-button {
|
||||
border: 2px dashed #eee;
|
||||
height: 132px;
|
||||
margin: 5px;
|
||||
}
|
||||
.react-kanban-column-adder-button:hover {
|
||||
cursor: pointer;
|
||||
}
|
||||
@@ -6,9 +6,11 @@ import { useTranslation } from "react-i18next";
|
||||
import { connect } from "react-redux";
|
||||
import { createStructuredSelector } from "reselect";
|
||||
import { selectTechnician } from "../../redux/tech/tech.selectors";
|
||||
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
//currentUser: selectCurrentUser
|
||||
technician: selectTechnician,
|
||||
bodyshop: selectBodyshop,
|
||||
});
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
//setUserLanguage: language => dispatch(setUserLanguage(language))
|
||||
@@ -21,6 +23,7 @@ export default connect(
|
||||
export function ProductionColumnsComponent({
|
||||
columnState,
|
||||
technician,
|
||||
bodyshop,
|
||||
tableState,
|
||||
}) {
|
||||
const [columns, setColumns] = columnState;
|
||||
@@ -29,9 +32,11 @@ export function ProductionColumnsComponent({
|
||||
const handleAdd = (e) => {
|
||||
setColumns([
|
||||
...columns,
|
||||
...dataSource({ technician, state: tableState }).filter(
|
||||
(i) => i.key === e.key
|
||||
),
|
||||
...dataSource({
|
||||
technician,
|
||||
state: tableState,
|
||||
activeStatuses: bodyshop.md_ro_statuses.active_statuses,
|
||||
}).filter((i) => i.key === e.key),
|
||||
]);
|
||||
};
|
||||
|
||||
@@ -39,7 +44,11 @@ export function ProductionColumnsComponent({
|
||||
|
||||
const menu = (
|
||||
<Menu onClick={handleAdd}>
|
||||
{dataSource({ technician, state: tableState })
|
||||
{dataSource({
|
||||
technician,
|
||||
state: tableState,
|
||||
activeStatuses: bodyshop.md_ro_statuses.active_statuses,
|
||||
})
|
||||
.filter((i) => !columnKeys.includes(i.key))
|
||||
.map((item) => (
|
||||
<Menu.Item key={item.key}>{item.title}</Menu.Item>
|
||||
|
||||
@@ -40,7 +40,7 @@ export default function ProductionListColumnBodyPriority({ record }) {
|
||||
key="set"
|
||||
title={t("production.actions.bodypriority-set")}
|
||||
>
|
||||
{new Array(9).fill().map((value, index) => (
|
||||
{new Array(15).fill().map((value, index) => (
|
||||
<Menu.Item key={index + 1}>{index + 1}</Menu.Item>
|
||||
))}
|
||||
</Menu.SubMenu>
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
import i18n from "i18next";
|
||||
import moment from "moment";
|
||||
import React from "react";
|
||||
import { Link } from "react-router-dom";
|
||||
import CurrencyFormatter from "../../utils/CurrencyFormatter";
|
||||
import PhoneFormatter from "../../utils/PhoneFormatter";
|
||||
import { alphaSort, dateSort } from "../../utils/sorters";
|
||||
import { alphaSort, dateSort, statusSort } from "../../utils/sorters";
|
||||
import JobAltTransportChange from "../job-at-change/job-at-change.component";
|
||||
import ProductionSubletsManageComponent from "../production-sublets-manage/production-sublets-manage.component";
|
||||
import ProductionListColumnAlert from "./production-list-columns.alert.component";
|
||||
@@ -11,13 +12,13 @@ import ProductionListColumnBodyPriority from "./production-list-columns.bodyprio
|
||||
import ProductionListDate from "./production-list-columns.date.component";
|
||||
import ProductionListColumnDetailPriority from "./production-list-columns.detailpriority.component";
|
||||
import ProductionListEmployeeAssignment from "./production-list-columns.empassignment.component";
|
||||
import ProductionListLastContacted from "./production-list-columns.lastcontacted.component";
|
||||
import ProductionListColumnPaintPriority from "./production-list-columns.paintpriority.component";
|
||||
import ProductionListColumnNote from "./production-list-columns.productionnote.component";
|
||||
import ProductionListColumnStatus from "./production-list-columns.status.component";
|
||||
import ProductionlistColumnTouchTime from "./prodution-list-columns.touchtime.component";
|
||||
import ProductionListLastContacted from "./production-list-columns.lastcontacted.component";
|
||||
|
||||
const r = ({ technician, state }) => {
|
||||
const r = ({ technician, state, activeStatuses }) => {
|
||||
return [
|
||||
{
|
||||
title: i18n.t("jobs.actions.viewdetail"),
|
||||
@@ -109,6 +110,29 @@ const r = ({ technician, state }) => {
|
||||
state.sortedInfo.order,
|
||||
render: (text, record) => <ProductionListLastContacted record={record} />,
|
||||
},
|
||||
{
|
||||
title: i18n.t("jobs.fields.date_next_contact"),
|
||||
dataIndex: "date_next_contact",
|
||||
key: "date_next_contact",
|
||||
ellipsis: true,
|
||||
sorter: (a, b) => dateSort(a.date_next_contact, b.date_next_contact),
|
||||
sortOrder:
|
||||
state.sortedInfo.columnKey === "date_next_contact" &&
|
||||
state.sortedInfo.order,
|
||||
render: (text, record) => (
|
||||
<span
|
||||
style={{
|
||||
color:
|
||||
record.date_next_contact &&
|
||||
moment(record.date_next_contact).isBefore(moment())
|
||||
? "red"
|
||||
: "",
|
||||
}}
|
||||
>
|
||||
<ProductionListDate record={record} field="date_next_contact" time />
|
||||
</span>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: i18n.t("jobs.fields.scheduled_delivery"),
|
||||
dataIndex: "scheduled_delivery",
|
||||
@@ -210,7 +234,7 @@ const r = ({ technician, state }) => {
|
||||
dataIndex: "status",
|
||||
key: "status",
|
||||
ellipsis: true,
|
||||
sorter: (a, b) => alphaSort(a.status, b.status),
|
||||
sorter: (a, b) => statusSort(a.status, b.status, activeStatuses),
|
||||
sortOrder:
|
||||
state.sortedInfo.columnKey === "status" && state.sortedInfo.order,
|
||||
render: (text, record) => <ProductionListColumnStatus record={record} />,
|
||||
@@ -332,7 +356,7 @@ const r = ({ technician, state }) => {
|
||||
render: (text, record) => (
|
||||
<ProductionListEmployeeAssignment
|
||||
record={record}
|
||||
type="employee_body_rel"
|
||||
type="employee_body"
|
||||
/>
|
||||
),
|
||||
},
|
||||
@@ -343,7 +367,7 @@ const r = ({ technician, state }) => {
|
||||
render: (text, record) => (
|
||||
<ProductionListEmployeeAssignment
|
||||
record={record}
|
||||
type="employee_prep_rel"
|
||||
type="employee_prep"
|
||||
/>
|
||||
),
|
||||
},
|
||||
@@ -352,10 +376,7 @@ const r = ({ technician, state }) => {
|
||||
dataIndex: "employee_csr",
|
||||
key: "employee_csr",
|
||||
render: (text, record) => (
|
||||
<ProductionListEmployeeAssignment
|
||||
record={record}
|
||||
type="employee_csr_rel"
|
||||
/>
|
||||
<ProductionListEmployeeAssignment record={record} type="employee_csr" />
|
||||
),
|
||||
},
|
||||
{
|
||||
@@ -365,7 +386,7 @@ const r = ({ technician, state }) => {
|
||||
render: (text, record) => (
|
||||
<ProductionListEmployeeAssignment
|
||||
record={record}
|
||||
type="employee_refinish_rel"
|
||||
type="employee_refinish"
|
||||
/>
|
||||
),
|
||||
},
|
||||
|
||||
@@ -40,7 +40,7 @@ export default function ProductionListColumnDetailPriority({ record }) {
|
||||
key="set"
|
||||
title={t("production.actions.detailpriority-set")}
|
||||
>
|
||||
{new Array(9).fill().map((value, index) => (
|
||||
{new Array(15).fill().map((value, index) => (
|
||||
<Menu.Item key={index + 1}>{index + 1}</Menu.Item>
|
||||
))}
|
||||
</Menu.SubMenu>
|
||||
|
||||
@@ -49,7 +49,7 @@ export function ProductionListEmpAssignment({
|
||||
const result = await updateJob({
|
||||
variables: { jobId: record.id, job: { [empAssignment]: employeeid } },
|
||||
|
||||
awaitRefetchQueries: true,
|
||||
// awaitRefetchQueries: true,
|
||||
});
|
||||
|
||||
insertAuditTrail({
|
||||
@@ -145,13 +145,18 @@ export function ProductionListEmpAssignment({
|
||||
</Row>
|
||||
);
|
||||
|
||||
let theEmployee;
|
||||
|
||||
if (record[type])
|
||||
theEmployee = bodyshop.employees.find((e) => e.id === record[type]);
|
||||
|
||||
return (
|
||||
<Popover destroyTooltipOnHide content={popContent} visible={visibility}>
|
||||
<Spin spinning={loading}>
|
||||
{record[type] ? (
|
||||
<div>
|
||||
<span>{`${record[type].first_name || ""} ${
|
||||
record[type].last_name || ""
|
||||
<span>{`${theEmployee.first_name || ""} ${
|
||||
theEmployee.last_name || ""
|
||||
}`}</span>
|
||||
<DeleteFilled
|
||||
style={iconStyle}
|
||||
@@ -174,13 +179,13 @@ export function ProductionListEmpAssignment({
|
||||
|
||||
const determineFieldName = (operation) => {
|
||||
switch (operation) {
|
||||
case "employee_body_rel":
|
||||
case "employee_body":
|
||||
return "employee_body";
|
||||
case "employee_prep_rel":
|
||||
case "employee_prep":
|
||||
return "employee_prep";
|
||||
case "employee_refinish_rel":
|
||||
case "employee_refinish":
|
||||
return "employee_refinish";
|
||||
case "employee_csr_rel":
|
||||
case "employee_csr":
|
||||
return "employee_csr";
|
||||
default:
|
||||
return null;
|
||||
|
||||
@@ -29,7 +29,11 @@ export function ProductionLastContacted({ currentUser, record }) {
|
||||
const [visible, setVisible] = useState(false);
|
||||
const { t } = useTranslation();
|
||||
const [form] = Form.useForm();
|
||||
const handleFinish = async ({ date_last_contacted, note }) => {
|
||||
const handleFinish = async ({
|
||||
date_last_contacted,
|
||||
date_next_contact,
|
||||
note,
|
||||
}) => {
|
||||
logImEXEvent("production_last_contacted");
|
||||
|
||||
//e.stopPropagation();
|
||||
@@ -38,6 +42,7 @@ export function ProductionLastContacted({ currentUser, record }) {
|
||||
jobId: record.id,
|
||||
job: {
|
||||
date_last_contacted,
|
||||
...(date_next_contact ? { date_next_contact } : {}),
|
||||
},
|
||||
},
|
||||
});
|
||||
@@ -98,7 +103,16 @@ export function ProductionLastContacted({ currentUser, record }) {
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
>
|
||||
<Form form={form} onFinish={handleFinish} layout="vertical">
|
||||
<Form.Item name="date_last_contacted">
|
||||
<Form.Item
|
||||
name="date_last_contacted"
|
||||
label={t("jobs.fields.date_last_contacted")}
|
||||
>
|
||||
<FormDateTimePickerComponent />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
name="date_next_contact"
|
||||
label={t("jobs.fields.date_next_contact")}
|
||||
>
|
||||
<FormDateTimePickerComponent />
|
||||
</Form.Item>
|
||||
<Form.Item label={t("notes.labels.notetoadd")} name="note">
|
||||
|
||||
@@ -40,7 +40,7 @@ export default function ProductionListColumnPaintPriority({ record }) {
|
||||
key="set"
|
||||
title={t("production.actions.paintpriority-set")}
|
||||
>
|
||||
{new Array(9).fill().map((value, index) => (
|
||||
{new Array(15).fill().map((value, index) => (
|
||||
<Menu.Item key={index + 1}>{index + 1}</Menu.Item>
|
||||
))}
|
||||
</Menu.SubMenu>
|
||||
|
||||
@@ -37,9 +37,11 @@ export function ProductionListTable({
|
||||
.filter((pc) => pc.name === value)[0]
|
||||
.columns.columnKeys.map((k) => {
|
||||
return {
|
||||
...ProductionListColumns({ technician, state }).find(
|
||||
(e) => e.key === k.key
|
||||
),
|
||||
...ProductionListColumns({
|
||||
technician,
|
||||
state,
|
||||
activeStatuses: bodyshop.md_ro_statuses.active_statuses,
|
||||
}).find((e) => e.key === k.key),
|
||||
width: k.width,
|
||||
};
|
||||
})
|
||||
@@ -88,9 +90,11 @@ export function ProductionListTable({
|
||||
setColumns(
|
||||
bodyshop.production_config[0].columns.columnKeys.map((k) => {
|
||||
return {
|
||||
...ProductionListColumns({ technician, state }).find(
|
||||
(e) => e.key === k.key
|
||||
),
|
||||
...ProductionListColumns({
|
||||
technician,
|
||||
state,
|
||||
activeStatuses: bodyshop.md_ro_statuses.active_statuses,
|
||||
}).find((e) => e.key === k.key),
|
||||
width: k.width,
|
||||
};
|
||||
})
|
||||
|
||||
@@ -1,4 +1,12 @@
|
||||
import { Dropdown, Input, Menu, PageHeader, Space, Table } from "antd";
|
||||
import {
|
||||
Dropdown,
|
||||
Input,
|
||||
Menu,
|
||||
PageHeader,
|
||||
Space,
|
||||
Statistic,
|
||||
Table,
|
||||
} from "antd";
|
||||
import React, { useMemo, useState } from "react";
|
||||
import ReactDragListView from "react-drag-listview";
|
||||
import { useTranslation } from "react-i18next";
|
||||
@@ -16,6 +24,7 @@ import ProductionListSaveConfigButton from "../production-list-save-config-butto
|
||||
import ProductionListPrint from "./production-list-print.component";
|
||||
import ProductionListTableViewSelect from "./production-list-table-view-select.component";
|
||||
import ResizeableTitle from "./production-list-table.resizeable.component";
|
||||
import { useTreatments } from "@splitsoftware/splitio-react";
|
||||
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
bodyshop: selectBodyshop,
|
||||
@@ -31,7 +40,9 @@ export function ProductionListTable({
|
||||
currentUser,
|
||||
}) {
|
||||
const [searchText, setSearchText] = useState("");
|
||||
|
||||
const { Production_List_Status_Colors } = useTreatments([
|
||||
"Production_List_Status_Colors",
|
||||
]);
|
||||
const assoc = bodyshop.associations.find(
|
||||
(a) => a.useremail === currentUser.email
|
||||
);
|
||||
@@ -59,9 +70,11 @@ export function ProductionListTable({
|
||||
matchingColumnConfig &&
|
||||
matchingColumnConfig.columns.columnKeys.map((k) => {
|
||||
return {
|
||||
...ProductionListColumns({ technician, state }).find(
|
||||
(e) => e.key === k.key
|
||||
),
|
||||
...ProductionListColumns({
|
||||
technician,
|
||||
state,
|
||||
activeStatuses: bodyshop.md_ro_statuses.active_statuses,
|
||||
}).find((e) => e.key === k.key),
|
||||
width: k.width,
|
||||
};
|
||||
})) ||
|
||||
@@ -156,9 +169,30 @@ export function ProductionListTable({
|
||||
|
||||
if (!!!columns) return <div>No columns found.</div>;
|
||||
|
||||
const totalHrs = data
|
||||
.reduce(
|
||||
(acc, val) =>
|
||||
acc +
|
||||
(val.labhrs?.aggregate?.sum?.mod_lb_hrs || 0) +
|
||||
(val.larhrs?.aggregate?.sum?.mod_lb_hrs || 0),
|
||||
0
|
||||
)
|
||||
.toFixed(1);
|
||||
return (
|
||||
<div>
|
||||
<PageHeader
|
||||
title={
|
||||
<Space>
|
||||
<Statistic
|
||||
title={t("dashboard.titles.productionhours")}
|
||||
value={totalHrs}
|
||||
/>
|
||||
<Statistic
|
||||
title={t("appointments.labels.inproduction")}
|
||||
value={dataSource && dataSource.length}
|
||||
/>
|
||||
</Space>
|
||||
}
|
||||
extra={
|
||||
<Space wrap>
|
||||
<ProductionListColumnsAdd
|
||||
@@ -192,8 +226,28 @@ export function ProductionListTable({
|
||||
handleSelector=".prod-header-dropdown"
|
||||
>
|
||||
<Table
|
||||
sticky
|
||||
pagination={false}
|
||||
size="small"
|
||||
className="production-list-table"
|
||||
onRow={
|
||||
Production_List_Status_Colors.treatment === "on" &&
|
||||
((record, index) => {
|
||||
if (!bodyshop.md_ro_statuses.production_colors) return null;
|
||||
|
||||
const color = bodyshop.md_ro_statuses.production_colors.find(
|
||||
(x) => x.status === record.status
|
||||
);
|
||||
|
||||
if (!color) return null;
|
||||
|
||||
return {
|
||||
style: {
|
||||
backgroundColor: `rgb(${color.color.r},${color.color.g},${color.color.b},${color.color.a})`,
|
||||
},
|
||||
};
|
||||
})
|
||||
}
|
||||
components={{
|
||||
header: {
|
||||
cell: ResizeableTitle,
|
||||
|
||||
@@ -56,7 +56,10 @@ export default function ProductionSubletsManageComponent({ subletJobLines }) {
|
||||
<Button
|
||||
key="complete"
|
||||
loading={loading}
|
||||
onClick={() => handleSubletMark(s, "complete")}
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
handleSubletMark(s, "complete");
|
||||
}}
|
||||
type={s.sublet_completed ? "primary" : "ghost"}
|
||||
>
|
||||
<CheckCircleFilled
|
||||
@@ -66,7 +69,10 @@ export default function ProductionSubletsManageComponent({ subletJobLines }) {
|
||||
<Button
|
||||
key="sublet"
|
||||
loading={loading}
|
||||
onClick={() => handleSubletMark(s, "ignore")}
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
handleSubletMark(s, "ignore");
|
||||
}}
|
||||
type={s.sublet_ignored ? "primary" : "ghost"}
|
||||
>
|
||||
<EyeInvisibleFilled
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Space, Tag } from "antd";
|
||||
import { Space } from "antd";
|
||||
import Axios from "axios";
|
||||
import queryString from "query-string";
|
||||
import React, { useEffect } from "react";
|
||||
@@ -9,7 +9,7 @@ import QboSignIn from "../../assets/qbo/C2QB_green_btn_med_default.svg";
|
||||
export default function QboAuthorizeComponent() {
|
||||
const location = useLocation();
|
||||
const history = useHistory();
|
||||
const [cookies, setCookie] = useCookies(["access_token", "refresh_token"]);
|
||||
const [, setCookie] = useCookies(["access_token", "refresh_token"]);
|
||||
|
||||
const handleQbSignIn = async () => {
|
||||
const result = await Axios.post("/qbo/authorize");
|
||||
@@ -24,16 +24,20 @@ export default function QboAuthorizeComponent() {
|
||||
const hasBeenCalledBack = code && realmId && state;
|
||||
|
||||
if (hasBeenCalledBack) {
|
||||
setCookie("qbo_code", code, { path: "/" });
|
||||
setCookie("qbo_state", state, { path: "/" });
|
||||
// setCookie("qbo_code", code, { path: "/" });
|
||||
// setCookie("qbo_state", state, { path: "/" });
|
||||
|
||||
let expires = new Date();
|
||||
expires.setTime(expires.getTime() + 8726400 * 1000);
|
||||
// let expires = new Date();
|
||||
// expires.setTime(expires.getTime() + 8726400 * 1000);
|
||||
|
||||
setCookie("qbo_realmId", realmId, {
|
||||
path: "/",
|
||||
expires,
|
||||
});
|
||||
// setCookie("qbo_realmId", realmId, {
|
||||
// path: "/",
|
||||
// expires,
|
||||
|
||||
// ...(process.env.NODE_ENV !== "development"
|
||||
// ? { domain: `.${window.location.host}` }
|
||||
// : {}),
|
||||
// });
|
||||
|
||||
history.push({ pathname: `/manage/accounting/receivables` });
|
||||
}
|
||||
@@ -48,9 +52,7 @@ export default function QboAuthorizeComponent() {
|
||||
src={QboSignIn}
|
||||
style={{ cursor: "pointer" }}
|
||||
/>
|
||||
{!cookies.qbo_realmId && (
|
||||
<Tag color="red">No QuickBooks company has been connected.</Tag>
|
||||
)}
|
||||
|
||||
{error && JSON.parse(decodeURIComponent(error)).error_description}
|
||||
</Space>
|
||||
);
|
||||
|
||||
@@ -3,6 +3,7 @@ import { Button, Card, Col, PageHeader, Row, Space } from "antd";
|
||||
import React from "react";
|
||||
import ScheduleCalendarWrapperComponent from "../schedule-calendar-wrapper/scheduler-calendar-wrapper.component";
|
||||
import ScheduleModal from "../schedule-job-modal/schedule-job-modal.container";
|
||||
//import ScheduleManualEvent from "../schedule-manual-event/schedule-manual-event.component";
|
||||
import ScheduleProductionList from "../schedule-production-list/schedule-production-list.component";
|
||||
|
||||
export default function ScheduleCalendarComponent({ data, refetch }) {
|
||||
@@ -21,8 +22,10 @@ export default function ScheduleCalendarComponent({ data, refetch }) {
|
||||
>
|
||||
<SyncOutlined />
|
||||
</Button>
|
||||
|
||||
<ScheduleProductionList />
|
||||
{
|
||||
// <ScheduleManualEvent />
|
||||
}
|
||||
</Space>
|
||||
}
|
||||
/>
|
||||
|
||||
@@ -5,9 +5,8 @@ import {
|
||||
Input,
|
||||
Row,
|
||||
Select,
|
||||
Space,
|
||||
Switch,
|
||||
Typography,
|
||||
Space, Switch,
|
||||
Typography
|
||||
} from "antd";
|
||||
import axios from "axios";
|
||||
import moment from "moment";
|
||||
@@ -75,6 +74,12 @@ export function ScheduleJobModalComponent({
|
||||
return (
|
||||
<Row gutter={[16, 16]}>
|
||||
<Col span={12}>
|
||||
<Space>
|
||||
<Typography.Title level={3}>{lbrHrsData?.jobs_by_pk?.ro_number}</Typography.Title>
|
||||
<Typography.Title
|
||||
level={4}
|
||||
>{`B/R Hrs:${lbrHrsData?.jobs_by_pk.labhrs?.aggregate.sum.mod_lb_hrs}/${lbrHrsData?.jobs_by_pk.larhrs?.aggregate.sum.mod_lb_hrs}`}</Typography.Title>
|
||||
</Space>
|
||||
<LayoutFormRow grow>
|
||||
<Form.Item
|
||||
name="start"
|
||||
|
||||
@@ -63,6 +63,20 @@ export function ScheduleJobModalContainer({
|
||||
skip: !visible || !!!jobId,
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
if (
|
||||
existingAppointments.data &&
|
||||
existingAppointments.data.appointments.length > 0 &&
|
||||
!existingAppointments.data.appointments[0].canceled
|
||||
) {
|
||||
form.setFieldsValue({
|
||||
color: existingAppointments.data.appointments[0].color,
|
||||
|
||||
note: existingAppointments.data.appointments[0].note,
|
||||
});
|
||||
}
|
||||
}, [existingAppointments.data, form]);
|
||||
|
||||
const handleFinish = async (values) => {
|
||||
logImEXEvent("schedule_new_appointment");
|
||||
|
||||
@@ -105,7 +119,7 @@ export function ScheduleJobModalContainer({
|
||||
start: moment(values.start),
|
||||
end: moment(values.start).add(bodyshop.appt_length || 60, "minutes"),
|
||||
color: values.color,
|
||||
note:values.note
|
||||
note: values.note,
|
||||
},
|
||||
jobId: jobId,
|
||||
altTransport: values.alt_transport,
|
||||
@@ -188,6 +202,9 @@ export function ScheduleJobModalContainer({
|
||||
start: null,
|
||||
// smartDates: [],
|
||||
scheduled_completion: null,
|
||||
color: context.color,
|
||||
alt_transport: context.alt_transport,
|
||||
note: context.note,
|
||||
}}
|
||||
>
|
||||
<ScheduleJobModalComponent
|
||||
|
||||
@@ -0,0 +1,119 @@
|
||||
import { useMutation } from "@apollo/client";
|
||||
import { Button, Card, Form, Input, Popover, Space } from "antd";
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { logImEXEvent } from "../../firebase/firebase.utils";
|
||||
import {
|
||||
INSERT_APPOINTMENT,
|
||||
UPDATE_APPOINTMENT,
|
||||
} from "../../graphql/appointments.queries";
|
||||
import FormDateTimePickerComponent from "../form-date-time-picker/form-date-time-picker.component";
|
||||
|
||||
export default function ScheduleManualEvent({ event }) {
|
||||
const { t } = useTranslation();
|
||||
const [insertAppointment] = useMutation(INSERT_APPOINTMENT);
|
||||
const [updateAppointment] = useMutation(UPDATE_APPOINTMENT);
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [form] = Form.useForm();
|
||||
const [visibility, setVisibility] = useState(false);
|
||||
// const [callQuery, { loading: entryLoading, data: entryData }] = useLazyQuery(
|
||||
// QUERY_SCOREBOARD_ENTRY
|
||||
// );
|
||||
|
||||
useEffect(() => {
|
||||
if (visibility && event) {
|
||||
form.setFieldsValue({ event });
|
||||
}
|
||||
}, [visibility, form, event]);
|
||||
|
||||
useEffect(() => {
|
||||
// if (entryData && entryData.scoreboard && entryData.scoreboard[0]) {
|
||||
// console.log("Setting FOrm");
|
||||
// // form.setFieldsValue(entryData.scoreboard[0]);
|
||||
// }
|
||||
}, [form]);
|
||||
|
||||
const handleFinish = async (values) => {
|
||||
logImEXEvent("job_close_add_to_scoreboard");
|
||||
|
||||
setLoading(true);
|
||||
try {
|
||||
if (event && event.id) {
|
||||
updateAppointment();
|
||||
} else {
|
||||
insertAppointment();
|
||||
}
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
} finally {
|
||||
setLoading(false);
|
||||
setVisibility(false);
|
||||
}
|
||||
};
|
||||
|
||||
const overlay = (
|
||||
<Card>
|
||||
<div>
|
||||
<Form form={form} layout="vertical" onFinish={handleFinish}>
|
||||
<Form.Item
|
||||
label={t("schedule.fields.note")}
|
||||
name="note"
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
//message: t("general.validation.required"),
|
||||
},
|
||||
]}
|
||||
>
|
||||
<Input />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("schedule.fields.start")}
|
||||
name="start"
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
//message: t("general.validation.required"),
|
||||
},
|
||||
]}
|
||||
>
|
||||
<FormDateTimePickerComponent />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("schedule.fields.end")}
|
||||
name="end"
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
//message: t("general.validation.required"),
|
||||
},
|
||||
]}
|
||||
>
|
||||
<FormDateTimePickerComponent />
|
||||
</Form.Item>
|
||||
|
||||
<Space wrap>
|
||||
<Button type="primary" htmlType="submit">
|
||||
{t("general.actions.save")}
|
||||
</Button>
|
||||
<Button onClick={() => setVisibility(false)}>
|
||||
{t("general.actions.cancel")}
|
||||
</Button>
|
||||
</Space>
|
||||
</Form>
|
||||
</div>
|
||||
</Card>
|
||||
);
|
||||
|
||||
const handleClick = (e) => {
|
||||
setVisibility(true);
|
||||
};
|
||||
|
||||
return (
|
||||
<Popover content={overlay} visible={visibility}>
|
||||
<Button loading={loading} onClick={handleClick}>
|
||||
{t("schedule.labels.manualevent")}
|
||||
</Button>
|
||||
</Popover>
|
||||
);
|
||||
}
|
||||
@@ -17,6 +17,8 @@ import PhoneFormItem, {
|
||||
import FormListMoveArrows from "../form-list-move-arrows/form-list-move-arrows.component";
|
||||
import LayoutFormRow from "../layout-form-row/layout-form-row.component";
|
||||
import CurrencyInput from "../form-items-formatted/currency-form-item.component";
|
||||
import FormItemEmail from "../form-items-formatted/email-form-item.component";
|
||||
|
||||
export default function ShopInfoGeneral({ form }) {
|
||||
const { t } = useTranslation();
|
||||
return (
|
||||
@@ -472,6 +474,19 @@ export default function ShopInfoGeneral({ form }) {
|
||||
>
|
||||
<Switch />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
name={["md_ded_notes"]}
|
||||
label={t("bodyshop.fields.md_ded_notes")}
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
//message: t("general.validation.required"),
|
||||
type: "array",
|
||||
},
|
||||
]}
|
||||
>
|
||||
<Select mode="tags" />
|
||||
</Form.Item>
|
||||
</LayoutFormRow>
|
||||
<LayoutFormRow grow header={t("bodyshop.labels.messagingpresets")}>
|
||||
<Form.List name={["md_messaging_presets"]}>
|
||||
@@ -706,7 +721,7 @@ export default function ShopInfoGeneral({ form }) {
|
||||
>
|
||||
<Input />
|
||||
</Form.Item>
|
||||
<Space wrap>
|
||||
<Space>
|
||||
<Form.Item
|
||||
label={t("bodyshop.fields.md_ins_co.zip")}
|
||||
key={`${index}zip`}
|
||||
@@ -744,6 +759,177 @@ export default function ShopInfoGeneral({ form }) {
|
||||
}}
|
||||
</Form.List>
|
||||
</LayoutFormRow>
|
||||
<LayoutFormRow grow header={t("bodyshop.labels.estimators")}>
|
||||
<Form.List name={["md_estimators"]}>
|
||||
{(fields, { add, remove, move }) => {
|
||||
return (
|
||||
<div>
|
||||
{fields.map((field, index) => (
|
||||
<Form.Item key={field.key}>
|
||||
<LayoutFormRow noDivider>
|
||||
<Form.Item
|
||||
label={t("jobs.fields.est_co_nm")}
|
||||
key={`${index}est_co_nm`}
|
||||
name={[field.name, "est_co_nm"]}
|
||||
>
|
||||
<Input />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("jobs.fields.est_ct_fn")}
|
||||
key={`${index}est_ct_fn`}
|
||||
name={[field.name, "est_ct_fn"]}
|
||||
>
|
||||
<Input />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("jobs.fields.est_ct_ln")}
|
||||
key={`${index}est_ct_ln`}
|
||||
name={[field.name, "est_ct_ln"]}
|
||||
>
|
||||
<Input />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("jobs.fields.est_ph1")}
|
||||
key={`${index}est_ph1`}
|
||||
name={[field.name, "est_ph1"]}
|
||||
rules={[
|
||||
({ getFieldValue }) =>
|
||||
PhoneItemFormatterValidation(getFieldValue, [
|
||||
field.name,
|
||||
"est_ph",
|
||||
]),
|
||||
]}
|
||||
>
|
||||
<Input />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("jobs.fields.est_ea")}
|
||||
key={`${index}est_ea`}
|
||||
name={[field.name, "est_ea"]}
|
||||
rules={[
|
||||
{
|
||||
type: "email",
|
||||
message: "This is not a valid email address.",
|
||||
},
|
||||
]}
|
||||
>
|
||||
<FormItemEmail
|
||||
email={form.getFieldValue([field.name, "est_ea"])}
|
||||
/>
|
||||
</Form.Item>
|
||||
<Space>
|
||||
<DeleteFilled
|
||||
onClick={() => {
|
||||
remove(field.name);
|
||||
}}
|
||||
/>
|
||||
<FormListMoveArrows
|
||||
move={move}
|
||||
index={index}
|
||||
total={fields.length}
|
||||
/>
|
||||
</Space>
|
||||
</LayoutFormRow>
|
||||
</Form.Item>
|
||||
))}
|
||||
<Form.Item>
|
||||
<Button
|
||||
type="dashed"
|
||||
onClick={() => {
|
||||
add();
|
||||
}}
|
||||
style={{ width: "100%" }}
|
||||
>
|
||||
{t("general.actions.add")}
|
||||
</Button>
|
||||
</Form.Item>
|
||||
</div>
|
||||
);
|
||||
}}
|
||||
</Form.List>
|
||||
</LayoutFormRow>
|
||||
<LayoutFormRow grow header={t("bodyshop.labels.filehandlers")}>
|
||||
<Form.List name={["md_filehandlers"]}>
|
||||
{(fields, { add, remove, move }) => {
|
||||
return (
|
||||
<div>
|
||||
{fields.map((field, index) => (
|
||||
<Form.Item key={field.key}>
|
||||
<LayoutFormRow noDivider>
|
||||
<Form.Item
|
||||
label={t("jobs.fields.ins_ct_fn")}
|
||||
key={`${index}ins_ct_fn`}
|
||||
name={[field.name, "ins_ct_fn"]}
|
||||
>
|
||||
<Input />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("jobs.fields.ins_ct_ln")}
|
||||
key={`${index}ins_ct_ln`}
|
||||
name={[field.name, "ins_ct_ln"]}
|
||||
>
|
||||
<Input />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("jobs.fields.ins_ph1")}
|
||||
key={`${index}ins_ph1`}
|
||||
name={[field.name, "ins_ph1"]}
|
||||
rules={[
|
||||
({ getFieldValue }) =>
|
||||
PhoneItemFormatterValidation(getFieldValue, [
|
||||
field.name,
|
||||
"ins_ph",
|
||||
]),
|
||||
]}
|
||||
>
|
||||
<Input />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("jobs.fields.ins_ea")}
|
||||
key={`${index}ins_ea`}
|
||||
name={[field.name, "ins_ea"]}
|
||||
rules={[
|
||||
{
|
||||
type: "email",
|
||||
message: "This is not a valid email address.",
|
||||
},
|
||||
]}
|
||||
>
|
||||
<FormItemEmail
|
||||
email={form.getFieldValue([field.name, "ins_ea"])}
|
||||
/>
|
||||
</Form.Item>
|
||||
<Space>
|
||||
<DeleteFilled
|
||||
onClick={() => {
|
||||
remove(field.name);
|
||||
}}
|
||||
/>
|
||||
<FormListMoveArrows
|
||||
move={move}
|
||||
index={index}
|
||||
total={fields.length}
|
||||
/>
|
||||
</Space>
|
||||
</LayoutFormRow>
|
||||
</Form.Item>
|
||||
))}
|
||||
<Form.Item>
|
||||
<Button
|
||||
type="dashed"
|
||||
onClick={() => {
|
||||
add();
|
||||
}}
|
||||
style={{ width: "100%" }}
|
||||
>
|
||||
{t("general.actions.add")}
|
||||
</Button>
|
||||
</Form.Item>
|
||||
</div>
|
||||
);
|
||||
}}
|
||||
</Form.List>
|
||||
</LayoutFormRow>
|
||||
<LayoutFormRow grow header={t("bodyshop.fields.md_ccc_rates")}>
|
||||
<Form.List name={["md_ccc_rates"]}>
|
||||
{(fields, { add, remove, move }) => {
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,8 +1,12 @@
|
||||
import { Form, Select } from "antd";
|
||||
import { DeleteFilled } from "@ant-design/icons";
|
||||
import { useTreatments } from "@splitsoftware/splitio-react";
|
||||
import { Button, Form, Select, Space } from "antd";
|
||||
import React, { useState } from "react";
|
||||
import { ChromePicker } from "react-color";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import styled from "styled-components";
|
||||
import LayoutFormRow from "../layout-form-row/layout-form-row.component";
|
||||
|
||||
const SelectorDiv = styled.div`
|
||||
.ant-form-item .ant-select {
|
||||
width: 200px;
|
||||
@@ -11,6 +15,9 @@ const SelectorDiv = styled.div`
|
||||
|
||||
export default function ShopInfoROStatusComponent({ form }) {
|
||||
const { t } = useTranslation();
|
||||
const { Production_List_Status_Colors } = useTreatments([
|
||||
"Production_List_Status_Colors",
|
||||
]);
|
||||
|
||||
const [options, setOptions] = useState(
|
||||
form.getFieldValue(["md_ro_statuses", "statuses"]) || []
|
||||
@@ -257,6 +264,97 @@ export default function ShopInfoROStatusComponent({ form }) {
|
||||
</Select>
|
||||
</Form.Item>
|
||||
</LayoutFormRow>
|
||||
{Production_List_Status_Colors.treatment === "on" && (
|
||||
<LayoutFormRow
|
||||
grow
|
||||
header={t("bodyshop.fields.statuses.production_colors")}
|
||||
>
|
||||
<Form.List name={["md_ro_statuses", "production_colors"]}>
|
||||
{(fields, { add, remove, move }) => {
|
||||
return (
|
||||
<div>
|
||||
<LayoutFormRow>
|
||||
{fields.map((field, index) => (
|
||||
<Form.Item key={field.key}>
|
||||
<Space direction="vertical">
|
||||
<div style={{ display: "flex" }}>
|
||||
<Form.Item
|
||||
style={{ flex: 1 }}
|
||||
label={t("jobs.fields.status")}
|
||||
key={`${index}status`}
|
||||
name={[field.name, "status"]}
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
//message: t("general.validation.required"),
|
||||
},
|
||||
]}
|
||||
>
|
||||
<Select>
|
||||
{options.map((item, idx) => (
|
||||
<Select.Option key={idx} value={item}>
|
||||
{item}
|
||||
</Select.Option>
|
||||
))}
|
||||
</Select>
|
||||
</Form.Item>
|
||||
<DeleteFilled
|
||||
onClick={() => {
|
||||
remove(field.name);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<Form.Item
|
||||
label={t("bodyshop.fields.statuses.color")}
|
||||
key={`${index}color`}
|
||||
name={[field.name, "color"]}
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
//message: t("general.validation.required"),
|
||||
},
|
||||
]}
|
||||
>
|
||||
<ColorPicker />
|
||||
</Form.Item>
|
||||
</Space>
|
||||
</Form.Item>
|
||||
))}
|
||||
</LayoutFormRow>
|
||||
<Form.Item>
|
||||
<Button
|
||||
type="dashed"
|
||||
onClick={() => {
|
||||
add();
|
||||
}}
|
||||
style={{ width: "100%" }}
|
||||
>
|
||||
{t("general.actions.add")}
|
||||
</Button>
|
||||
</Form.Item>
|
||||
</div>
|
||||
);
|
||||
}}
|
||||
</Form.List>
|
||||
</LayoutFormRow>
|
||||
)}
|
||||
</SelectorDiv>
|
||||
);
|
||||
}
|
||||
|
||||
const ColorPicker = ({ value, onChange, style, ...restProps }) => {
|
||||
const handleChange = (color) => {
|
||||
console.log(
|
||||
"🚀 ~ file: shop-info.rostatus.component.jsx ~ line 345 ~ color",
|
||||
color
|
||||
);
|
||||
if (onChange) onChange(color.rgb);
|
||||
};
|
||||
return (
|
||||
<ChromePicker
|
||||
{...restProps}
|
||||
color={value}
|
||||
onChangeComplete={handleChange}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -44,7 +44,7 @@ export default function ShopInfoSchedulingComponent({ form }) {
|
||||
},
|
||||
]}
|
||||
>
|
||||
<TimePicker showSecond={false} format="hh:mm" />
|
||||
<TimePicker showSecond={false} format="HH:mm" />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("bodyshop.fields.schedule_end_time")}
|
||||
@@ -56,7 +56,7 @@ export default function ShopInfoSchedulingComponent({ form }) {
|
||||
},
|
||||
]}
|
||||
>
|
||||
<TimePicker showSecond={false} format="hh:mm" />
|
||||
<TimePicker showSecond={false} format="HH:mm" />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
name={["appt_alt_transport"]}
|
||||
|
||||
@@ -196,10 +196,11 @@ export const CANCEL_APPOINTMENT_BY_ID = gql`
|
||||
|
||||
export const QUERY_APPOINTMENTS_BY_JOBID = gql`
|
||||
query QUERY_APPOINTMENTS_BY_JOBID($jobid: uuid!) {
|
||||
appointments(where: { jobid: { _eq: $jobid } }) {
|
||||
appointments(where: { jobid: { _eq: $jobid } }, order_by: { start: desc }) {
|
||||
start
|
||||
id
|
||||
end
|
||||
color
|
||||
isintake
|
||||
arrived
|
||||
canceled
|
||||
|
||||
@@ -94,7 +94,13 @@ export const QUERY_BODYSHOP = gql`
|
||||
attach_pdf_to_email
|
||||
tt_allow_post_to_invoiced
|
||||
cdk_configuration
|
||||
md_estimators
|
||||
md_ded_notes
|
||||
pbs_configuration
|
||||
pbs_serialnumber
|
||||
md_filehandlers
|
||||
employees {
|
||||
user_email
|
||||
id
|
||||
active
|
||||
first_name
|
||||
@@ -184,6 +190,11 @@ export const UPDATE_SHOP = gql`
|
||||
attach_pdf_to_email
|
||||
tt_allow_post_to_invoiced
|
||||
cdk_configuration
|
||||
md_estimators
|
||||
md_ded_notes
|
||||
pbs_configuration
|
||||
pbs_serialnumber
|
||||
md_filehandlers
|
||||
employees {
|
||||
id
|
||||
first_name
|
||||
@@ -191,6 +202,7 @@ export const UPDATE_SHOP = gql`
|
||||
last_name
|
||||
employee_number
|
||||
rates
|
||||
user_email
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ import { gql } from "@apollo/client";
|
||||
|
||||
export const QUERY_EMPLOYEES = gql`
|
||||
query QUERY_EMPLOYEES {
|
||||
employees {
|
||||
employees(order_by: { employee_number: asc }) {
|
||||
last_name
|
||||
id
|
||||
first_name
|
||||
|
||||
@@ -125,6 +125,7 @@ export const SUBSCRIPTION_JOBS_IN_PRODUCTION = gql`
|
||||
scheduled_completion
|
||||
scheduled_delivery
|
||||
date_last_contacted
|
||||
date_next_contact
|
||||
ins_co_nm
|
||||
clm_total
|
||||
ownr_ph1
|
||||
@@ -134,39 +135,10 @@ export const SUBSCRIPTION_JOBS_IN_PRODUCTION = gql`
|
||||
production_vars
|
||||
kanbanparent
|
||||
alt_transport
|
||||
joblines_status {
|
||||
part_type
|
||||
count
|
||||
status
|
||||
}
|
||||
employee_body
|
||||
employee_body_rel {
|
||||
id
|
||||
first_name
|
||||
last_name
|
||||
}
|
||||
employee_refinish
|
||||
employee_refinish_rel {
|
||||
id
|
||||
first_name
|
||||
last_name
|
||||
}
|
||||
employee_prep
|
||||
employee_prep_rel {
|
||||
id
|
||||
first_name
|
||||
last_name
|
||||
}
|
||||
employee_csr_rel {
|
||||
id
|
||||
first_name
|
||||
last_name
|
||||
}
|
||||
partcount: joblines_aggregate(where: { removed: { _eq: false } }) {
|
||||
nodes {
|
||||
status
|
||||
}
|
||||
}
|
||||
employee_csr
|
||||
labhrs: joblines_aggregate(
|
||||
where: {
|
||||
_and: [{ mod_lbr_ty: { _neq: "LAR" } }, { removed: { _eq: false } }]
|
||||
@@ -208,6 +180,7 @@ export const QUERY_LBR_HRS_BY_PK = gql`
|
||||
query QUERY_LBR_HRS_BY_PK($id: uuid!) {
|
||||
jobs_by_pk(id: $id) {
|
||||
id
|
||||
ro_number
|
||||
labhrs: joblines_aggregate(
|
||||
where: {
|
||||
_and: [{ mod_lbr_ty: { _neq: "LAR" } }, { removed: { _eq: false } }]
|
||||
@@ -385,6 +358,7 @@ export const GET_JOB_BY_PK = gql`
|
||||
vehicleid
|
||||
driveable
|
||||
towin
|
||||
loss_of_use
|
||||
vehicle {
|
||||
id
|
||||
plate_no
|
||||
@@ -463,6 +437,7 @@ export const GET_JOB_BY_PK = gql`
|
||||
production_vars
|
||||
ca_gst_registrant
|
||||
ownerid
|
||||
ded_note
|
||||
owner {
|
||||
id
|
||||
ownr_fn
|
||||
@@ -516,6 +491,7 @@ export const GET_JOB_BY_PK = gql`
|
||||
date_scheduled
|
||||
date_invoiced
|
||||
date_last_contacted
|
||||
date_next_contact
|
||||
date_exported
|
||||
status
|
||||
owner_owing
|
||||
@@ -533,6 +509,7 @@ export const GET_JOB_BY_PK = gql`
|
||||
unq_seq
|
||||
line_ind
|
||||
line_desc
|
||||
line_ref
|
||||
part_type
|
||||
oem_partno
|
||||
db_price
|
||||
@@ -735,6 +712,7 @@ export const QUERY_JOB_CARD_DETAILS = gql`
|
||||
actual_completion
|
||||
actual_delivery
|
||||
actual_in
|
||||
scheduled_in
|
||||
po_number
|
||||
id
|
||||
ins_co_nm
|
||||
@@ -757,6 +735,7 @@ export const QUERY_JOB_CARD_DETAILS = gql`
|
||||
scheduled_delivery
|
||||
date_invoiced
|
||||
date_last_contacted
|
||||
date_next_contact
|
||||
date_open
|
||||
date_exported
|
||||
|
||||
@@ -843,6 +822,7 @@ export const QUERY_TECH_JOB_DETAILS = gql`
|
||||
scheduled_delivery
|
||||
date_invoiced
|
||||
date_last_contacted
|
||||
date_next_contact
|
||||
date_open
|
||||
date_exported
|
||||
voided
|
||||
@@ -1769,7 +1749,7 @@ export const QUERY_JOB_CLOSE_DETAILS = gql`
|
||||
actual_in
|
||||
kmin
|
||||
kmout
|
||||
joblines(where: { removed: { _eq: false } }) {
|
||||
joblines(where: { removed: { _eq: false } }, order_by: { line_no: asc }) {
|
||||
id
|
||||
removed
|
||||
tax_part
|
||||
|
||||
@@ -14,6 +14,7 @@ export const QUERY_NOTES_BY_JOB_PK = gql`
|
||||
query QUERY_NOTES_BY_JOB_PK($id: uuid!) {
|
||||
jobs_by_pk(id: $id) {
|
||||
id
|
||||
ro_number
|
||||
notes {
|
||||
created_at
|
||||
created_by
|
||||
|
||||
@@ -2,7 +2,7 @@ import { gql } from "@apollo/client";
|
||||
|
||||
export const QUERY_SEARCH_OWNER_BY_IDX = gql`
|
||||
query QUERY_SEARCH_OWNER_BY_IDX($search: String!) {
|
||||
search_owners(args: { search: $search }) {
|
||||
search_owners(args: { search: $search }, limit: 100) {
|
||||
ownr_fn
|
||||
ownr_ln
|
||||
ownr_ph1
|
||||
|
||||
@@ -69,3 +69,25 @@ export const UPDATE_FCM_TOKEN = gql`
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
export const QUERY_KANBAN_SETTINGS = gql`
|
||||
query QUERY_KANBAN_SETTINGS($email: String!) {
|
||||
associations(
|
||||
where: { _and: { useremail: { _eq: $email }, active: { _eq: true } } }
|
||||
) {
|
||||
id
|
||||
kanban_settings
|
||||
}
|
||||
}
|
||||
`;
|
||||
export const UPDATE_KANBAN_SETTINGS = gql`
|
||||
mutation UPDATE_KANBAN_SETTINGS($id: uuid!, $ks: jsonb) {
|
||||
update_associations_by_pk(
|
||||
pk_columns: { id: $id }
|
||||
_set: { kanban_settings: $ks }
|
||||
) {
|
||||
id
|
||||
kanban_settings
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
@@ -23,6 +23,11 @@ Dinero.globalRoundingMode = "HALF_EVEN";
|
||||
if (process.env.NODE_ENV !== "development") {
|
||||
Sentry.init({
|
||||
dsn: "https://fd7e89369b6b4bdc9c6c4c9f22fa4ee4@o492140.ingest.sentry.io/5651027",
|
||||
ignoreErrors: [
|
||||
"ResizeObserver loop",
|
||||
"Module specifier, 'fs' does not start",
|
||||
"Module specifier, 'zlib' does not start with",
|
||||
],
|
||||
integrations: [
|
||||
// new Integrations.BrowserTracing(),
|
||||
// new Sentry.Integrations.Breadcrumbs({ console: true }),
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { Button, Col, PageHeader, Row } from "antd";
|
||||
import { Button, Col, PageHeader, Row, Space, Form, Switch } from "antd";
|
||||
|
||||
import React from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import ContractCarsContainer from "../../components/contract-cars/contract-cars.container";
|
||||
@@ -13,14 +14,25 @@ export default function ContractCreatePageComponent({
|
||||
const { t } = useTranslation();
|
||||
|
||||
const CreateButton = (
|
||||
<Button
|
||||
disabled={!selectedJobState[0] || !selectedCarState[0]}
|
||||
type="primary"
|
||||
onClick={() => form.submit()}
|
||||
loading={loading}
|
||||
>
|
||||
{t("general.actions.create")}
|
||||
</Button>
|
||||
<Space size="large">
|
||||
{selectedJobState[0] && selectedCarState[0] && (
|
||||
<Form.Item
|
||||
label={t("jobs.actions.addtoproduction")}
|
||||
name="addtoproduction"
|
||||
valuePropName="checked"
|
||||
>
|
||||
<Switch />
|
||||
</Form.Item>
|
||||
)}
|
||||
<Button
|
||||
disabled={!selectedJobState[0] || !selectedCarState[0]}
|
||||
type="primary"
|
||||
onClick={() => form.submit()}
|
||||
loading={loading}
|
||||
>
|
||||
{t("general.actions.create")}
|
||||
</Button>
|
||||
</Space>
|
||||
);
|
||||
|
||||
return (
|
||||
|
||||
@@ -7,6 +7,7 @@ import { useHistory, useLocation } from "react-router-dom";
|
||||
import { createStructuredSelector } from "reselect";
|
||||
import RbacWrapper from "../../components/rbac-wrapper/rbac-wrapper.component";
|
||||
import { INSERT_NEW_CONTRACT } from "../../graphql/cccontracts.queries";
|
||||
import { UPDATE_JOB } from "../../graphql/jobs.queries";
|
||||
import {
|
||||
setBreadcrumbs,
|
||||
setSelectedHeader,
|
||||
@@ -37,8 +38,9 @@ export function ContractCreatePageContainer({
|
||||
(location.state && location.state.jobId) || null
|
||||
);
|
||||
const [insertContract] = useMutation(INSERT_NEW_CONTRACT);
|
||||
const [intakeJob] = useMutation(UPDATE_JOB);
|
||||
|
||||
const handleFinish = async (values) => {
|
||||
const handleFinish = async ({ addtoproduction, ...values }) => {
|
||||
if (!!selectedCarState[0] && !!selectedJobState[0]) {
|
||||
setLoading(true);
|
||||
const result = await insertContract({
|
||||
@@ -54,12 +56,35 @@ export function ContractCreatePageContainer({
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
if (!result.errors) {
|
||||
//Update the courtesy car to have the damage.
|
||||
notification["success"]({
|
||||
message: t("contracts.successes.saved"),
|
||||
});
|
||||
|
||||
//Intake the job if required
|
||||
if (addtoproduction) {
|
||||
const result2 = await intakeJob({
|
||||
variables: {
|
||||
jobId: selectedJobState[0],
|
||||
job: {
|
||||
actual_in: new Date(),
|
||||
inproduction: true,
|
||||
status: bodyshop.md_ro_statuses.default_arrived,
|
||||
},
|
||||
},
|
||||
});
|
||||
if (result2.errors) {
|
||||
notification["error"]({
|
||||
message: t("jobs.errors.saving", {
|
||||
error: JSON.stringify(!result2.errors),
|
||||
}),
|
||||
});
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
form.resetFields();
|
||||
form.resetFields();
|
||||
history.push(
|
||||
|
||||
@@ -120,7 +120,11 @@ export function DmsContainer({ bodyshop, setBreadcrumbs, setSelectedHeader }) {
|
||||
if (loading) return <LoadingSpinner />;
|
||||
if (error) return <AlertComponent message={error.message} type="error" />;
|
||||
|
||||
if (!jobId || !bodyshop.cdk_dealerid || !(data && data.jobs_by_pk))
|
||||
if (
|
||||
!jobId ||
|
||||
!(bodyshop.cdk_dealerid || bodyshop.pbs_serialnumber) ||
|
||||
!(data && data.jobs_by_pk)
|
||||
)
|
||||
return <Result status="404" />;
|
||||
|
||||
return (
|
||||
|
||||
@@ -208,7 +208,7 @@ export function JobsCloseComponent({ job, bodyshop, jobRO }) {
|
||||
>
|
||||
<DateTimePicker disabled={jobRO} />
|
||||
</Form.Item>
|
||||
{bodyshop.cdk_dealerid && (
|
||||
{(bodyshop.cdk_dealerid || bodyshop.pbs_serialnumber) && (
|
||||
<Form.Item
|
||||
label={t("jobs.fields.kmin")}
|
||||
name="kmin"
|
||||
@@ -221,7 +221,7 @@ export function JobsCloseComponent({ job, bodyshop, jobRO }) {
|
||||
<InputNumber precision={0} disabled={jobRO} />
|
||||
</Form.Item>
|
||||
)}
|
||||
{bodyshop.cdk_dealerid && (
|
||||
{(bodyshop.cdk_dealerid || bodyshop.pbs_serialnumber) && (
|
||||
<Form.Item
|
||||
label={t("jobs.fields.kmout")}
|
||||
name="kmout"
|
||||
|
||||
@@ -196,7 +196,11 @@ export function JobsDetailPage({
|
||||
<PrinterFilled />
|
||||
{t("jobs.actions.printCenter")}
|
||||
</Button>
|
||||
<JobsConvertButton job={job} refetch={refetch} />
|
||||
<JobsConvertButton
|
||||
job={job}
|
||||
refetch={refetch}
|
||||
parentFormIsFieldsTouched={form.isFieldsTouched}
|
||||
/>
|
||||
<JobsDetailHeaderActions key="actions" job={job} refetch={refetch} />
|
||||
<Button
|
||||
type="primary"
|
||||
|
||||
@@ -5,12 +5,12 @@ import preval from "preval.macro";
|
||||
import React, { lazy, Suspense, useEffect } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { connect } from "react-redux";
|
||||
import ErrorBoundary from "../../components/error-boundary/error-boundary.component";
|
||||
import { Link, Route, Switch } from "react-router-dom";
|
||||
import { createStructuredSelector } from "reselect";
|
||||
import BreadCrumbs from "../../components/breadcrumbs/breadcrumbs.component";
|
||||
import ChatAffixContainer from "../../components/chat-affix/chat-affix.container";
|
||||
import ConflictComponent from "../../components/conflict/conflict.component";
|
||||
import ErrorBoundary from "../../components/error-boundary/error-boundary.component";
|
||||
import FcmNotification from "../../components/fcm-notification/fcm-notification.component";
|
||||
//import FooterComponent from "../../components/footer/footer.component";
|
||||
//Component Imports
|
||||
|
||||
@@ -6,8 +6,8 @@ import AlertComponent from "../../components/alert/alert.component";
|
||||
import LoadingSpinner from "../../components/loading-spinner/loading-spinner.component";
|
||||
import { QUERY_BODYSHOP } from "../../graphql/bodyshop.queries";
|
||||
import { setBodyshop } from "../../redux/user/user.actions";
|
||||
import ManagePage from "./manage.page.component";
|
||||
import "../../utils/RegisterSw";
|
||||
import ManagePage from "./manage.page.component";
|
||||
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
setBodyshop: (bs) => dispatch(setBodyshop(bs)),
|
||||
@@ -21,7 +21,9 @@ function ManagePageContainer({ match, setBodyshop }) {
|
||||
const { t } = useTranslation();
|
||||
|
||||
useEffect(() => {
|
||||
if (data) setBodyshop(data.bodyshops[0] || { notfound: true });
|
||||
if (data) {
|
||||
setBodyshop(data.bodyshops[0] || { notfound: true });
|
||||
}
|
||||
}, [data, setBodyshop]);
|
||||
|
||||
if (loading)
|
||||
|
||||
@@ -13,7 +13,7 @@ import { doc } from "firebase/firestore";
|
||||
import i18next from "i18next";
|
||||
import LogRocket from "logrocket";
|
||||
import { all, call, delay, put, select, takeLatest } from "redux-saga/effects";
|
||||
import { tracker } from "../../App/App.container";
|
||||
import { factory, tracker } from "../../App/App.container";
|
||||
import {
|
||||
getCurrentUser,
|
||||
logImEXEvent,
|
||||
@@ -250,6 +250,8 @@ export function* SetAuthLevelFromShopDetails({ payload }) {
|
||||
try {
|
||||
const userEmail = yield select((state) => state.user.currentUser.email);
|
||||
|
||||
factory.client(payload.imexshopid);
|
||||
|
||||
const authRecord = payload.associations.filter(
|
||||
(a) => a.useremail === userEmail
|
||||
);
|
||||
|
||||
@@ -270,6 +270,7 @@
|
||||
"md_categories": "Categories",
|
||||
"md_ccc_rates": "Courtesy Car Contract Rate Presets",
|
||||
"md_classes": "Classes",
|
||||
"md_ded_notes": "Deductible Notes",
|
||||
"md_hour_split": {
|
||||
"paint": "Paint Hour Split",
|
||||
"prep": "Prep Hour Split"
|
||||
@@ -450,6 +451,7 @@
|
||||
"status": "Status Label",
|
||||
"statuses": {
|
||||
"active_statuses": "Active Statuses (Filtering for Active Jobs throughout system)",
|
||||
"color": "Color",
|
||||
"default_arrived": "Default Arrived Status (Transition to Production)",
|
||||
"default_bo": "Default Backordered Status",
|
||||
"default_canceled": "Default Canceled Status",
|
||||
@@ -466,6 +468,7 @@
|
||||
"open_statuses": "Open Statuses",
|
||||
"post_production_statuses": "Post-Production Statuses",
|
||||
"pre_production_statuses": "Pre-Production Statuses",
|
||||
"production_colors": "Production Status Colors",
|
||||
"production_statuses": "Production Statuses"
|
||||
},
|
||||
"target_touchtime": "Target Touch Time",
|
||||
@@ -499,6 +502,8 @@
|
||||
},
|
||||
"emaillater": "Email Later",
|
||||
"employees": "Employees",
|
||||
"estimators": "Estimators",
|
||||
"filehandlers": "File Handlers",
|
||||
"insurancecos": "Insurance Companies",
|
||||
"intakechecklist": "Intake Checklist",
|
||||
"jobstatuses": "Job Statuses",
|
||||
@@ -531,6 +536,7 @@
|
||||
"save": "Shop configuration saved successfully. "
|
||||
},
|
||||
"validation": {
|
||||
"centermustexist": "The chosen responsibility center does not exist.",
|
||||
"larsplit": "Refinish hour split must add up to 1.",
|
||||
"useremailmustexist": "This email is not a valid user."
|
||||
}
|
||||
@@ -1053,7 +1059,8 @@
|
||||
},
|
||||
"profitcenter_labor": "Profit Center: Labor",
|
||||
"profitcenter_part": "Profit Center: Part",
|
||||
"prt_dsmk_p": "Line Markup %",
|
||||
"prt_dsmk_m": "Line Discount/Markup $",
|
||||
"prt_dsmk_p": "Line Discount/Markup %",
|
||||
"status": "Status",
|
||||
"tax_part": "Tax Part",
|
||||
"total": "Total",
|
||||
@@ -1086,8 +1093,10 @@
|
||||
"addtoscoreboard": "Add to Scoreboard",
|
||||
"allocate": "Allocate",
|
||||
"autoallocate": "Auto Allocate",
|
||||
"changefilehandler": "Change File Handler",
|
||||
"changelaborrate": "Change Labor Rate",
|
||||
"changestatus": "Change Status",
|
||||
"changestimator": "Change Estimator",
|
||||
"convert": "Convert",
|
||||
"deliver": "Deliver",
|
||||
"dms": {
|
||||
@@ -1199,9 +1208,11 @@
|
||||
"date_exported": "Exported",
|
||||
"date_invoiced": "Invoiced",
|
||||
"date_last_contacted": "Last Contacted Date",
|
||||
"date_next_contact": "Next Contact Date",
|
||||
"date_open": "Open",
|
||||
"date_scheduled": "Scheduled",
|
||||
"ded_amt": "Deductible",
|
||||
"ded_note": "Deductible Note",
|
||||
"ded_status": "Deductible Status",
|
||||
"depreciation_taxes": "Depreciation/Taxes",
|
||||
"dms": {
|
||||
@@ -1277,6 +1288,7 @@
|
||||
"local_tax_rate": "Local Tax Rate",
|
||||
"loss_date": "Loss Date",
|
||||
"loss_desc": "Loss Description",
|
||||
"loss_of_use": "Loss of Use",
|
||||
"ma2s": "2 Stage Paint",
|
||||
"ma3s": "3 Stage Pain",
|
||||
"mabl": "MABL?",
|
||||
@@ -1526,6 +1538,7 @@
|
||||
"sale_labor": "Sales - Labor",
|
||||
"sale_parts": "Sales - Parts & Sublet",
|
||||
"sales": "Sales",
|
||||
"savebeforeconversion": "You have unsaved changes on the job. Please save them before converting it. ",
|
||||
"scheduledinchange": "The scheduled in is based off the latest appointment. To change this date, please schedule or reschedule the job. ",
|
||||
"specialcoveragepolicy": "Special Coverage Policy Applies",
|
||||
"state_tax_amt": "Provincial/State Taxes",
|
||||
@@ -1716,7 +1729,8 @@
|
||||
"new": "New Conversation"
|
||||
},
|
||||
"errors": {
|
||||
"invalidphone": "The phone number is invalid. Unable to open conversation. "
|
||||
"invalidphone": "The phone number is invalid. Unable to open conversation. ",
|
||||
"noattachedjobs": "No jobs have been associated to this conversation. "
|
||||
},
|
||||
"labels": {
|
||||
"archive": "Archive",
|
||||
@@ -1726,6 +1740,7 @@
|
||||
"nojobs": "Not associated to any job.",
|
||||
"phonenumber": "Phone #",
|
||||
"presets": "Presets",
|
||||
"recentonly": "Only your most recent 50 conversations will be shown here. If you are looking for an older conversation, find the related contact and click their phone number to view the conversation.",
|
||||
"selectmedia": "Select Media",
|
||||
"sentby": "Sent by {{by}} at {{time}}",
|
||||
"typeamessage": "Send a message...",
|
||||
@@ -1815,6 +1830,7 @@
|
||||
"receivebill": "Receive Bill"
|
||||
},
|
||||
"errors": {
|
||||
"associatedbills": "This parts order cannot",
|
||||
"backordering": "Error backordering part {{message}}.",
|
||||
"creating": "Error encountered when creating parts order. "
|
||||
},
|
||||
@@ -1840,7 +1856,7 @@
|
||||
},
|
||||
"labels": {
|
||||
"allpartsto": "All Parts Location",
|
||||
"confirmdelete": "Are you sure you want to delete this item? It cannot be recovered. ",
|
||||
"confirmdelete": "Are you sure you want to delete this item? It cannot be recovered. Job line statuses will not be updated and may require manual review. ",
|
||||
"email": "Send by Email",
|
||||
"inthisorder": "Parts in this Order",
|
||||
"newpartsorder": "New Parts Order",
|
||||
@@ -1959,6 +1975,7 @@
|
||||
"zip": "Postal Code/Zip"
|
||||
},
|
||||
"3rdpartypayer": "Invoice to Third Party Payer",
|
||||
"ab_proof_of_loss": "AB - Proof of Loss",
|
||||
"appointment_confirmation": "Appointment Confirmation",
|
||||
"appointment_reminder": "Appointment Reminder",
|
||||
"casl_authorization": "CASL Authorization",
|
||||
@@ -1976,12 +1993,16 @@
|
||||
"fippa_authorization": "FIPPA Authorization",
|
||||
"glass_express_checklist": "Glass Express Checklist",
|
||||
"guarantee": "Repair Guarantee",
|
||||
"individual_job_note": "Job Note RO # {{ro_number}}",
|
||||
"invoice_customer_payable": "Invoice (Customer Payable)",
|
||||
"invoice_total_payable": "Invoice (Total Payable)",
|
||||
"iou_form": "IOU Form",
|
||||
"job_costing_ro": "Job Costing",
|
||||
"job_notes": "Job Notes",
|
||||
"key_tag": "Key Tag",
|
||||
"mpi_animal_checklist": "MPI - Animal Checklist",
|
||||
"mpi_eglass_auth": "MPI - eGlass Auth",
|
||||
"mpi_final_acct_sheet": "MPI - Final Accounting Sheet",
|
||||
"paint_grid": "Paint Grid",
|
||||
"parts_label_single": "Parts Label - Single",
|
||||
"parts_list": "Parts List",
|
||||
@@ -2011,7 +2032,8 @@
|
||||
"worksheet_by_line_number": "Worksheet by Line Number",
|
||||
"worksheet_sorted_by_operation": "Worksheet by Operation",
|
||||
"worksheet_sorted_by_operation_no_hours": "Worksheet by Operation (No Hours)",
|
||||
"worksheet_sorted_by_operation_part_type": "Worksheet by Operation & Part Type"
|
||||
"worksheet_sorted_by_operation_part_type": "Worksheet by Operation & Part Type",
|
||||
"worksheet_sorted_by_operation_type": "Worksheet by Operation Type"
|
||||
},
|
||||
"labels": {
|
||||
"groups": {
|
||||
@@ -2056,20 +2078,31 @@
|
||||
},
|
||||
"errors": {
|
||||
"boardupdate": "Error encountered updating job. {{message}}",
|
||||
"removing": "Error removing from production board. {{error}}"
|
||||
"removing": "Error removing from production board. {{error}}",
|
||||
"settings": "Error saving board settings: {{error}}"
|
||||
},
|
||||
"labels": {
|
||||
"alert": "Alert",
|
||||
"alertoff": "Remove alert from job",
|
||||
"alerton": "Add alert to job",
|
||||
"ats": "Alternative Transportation",
|
||||
"bodyhours": "B",
|
||||
"bodypriority": "B/P",
|
||||
"cardsettings": "Card Settings",
|
||||
"clm_no": "Claim Number",
|
||||
"compact": "Compact Cards",
|
||||
"detailpriority": "D/P",
|
||||
"employeeassignments": "Employee Assignments",
|
||||
"employeesearch": "Employee Search",
|
||||
"ins_co_nm": "Insurance Company Name",
|
||||
"jobdetail": "Job Details",
|
||||
"laborhrs": "Labor Hours",
|
||||
"note": "Production Note",
|
||||
"ownr_nm": "Owner Name",
|
||||
"paintpriority": "P/P",
|
||||
"production_note": "Production Note",
|
||||
"refinishhours": "R",
|
||||
"scheduled_completion": "Scheduled Completion",
|
||||
"selectview": "Select a View",
|
||||
"sublets": "Sublets",
|
||||
"totalhours": "Total Hrs ",
|
||||
@@ -2164,6 +2197,7 @@
|
||||
"open_orders_csr": "Open Orders by CSR",
|
||||
"open_orders_estimator": "Open Orders by Estimator",
|
||||
"open_orders_ins_co": "Open Orders by Insurance Company",
|
||||
"open_orders_status": "Open Orders by Status",
|
||||
"parts_backorder": "Backordered Parts",
|
||||
"parts_not_recieved": "Parts Not Received",
|
||||
"payments_by_date": "Payments by Date",
|
||||
@@ -2195,6 +2229,11 @@
|
||||
"work_in_progress_payables": "Work in Progress - Payables"
|
||||
}
|
||||
},
|
||||
"schedule": {
|
||||
"labels": {
|
||||
"manualevent": "Add Manual Event"
|
||||
}
|
||||
},
|
||||
"scoreboard": {
|
||||
"actions": {
|
||||
"edit": "Edit"
|
||||
|
||||
@@ -270,6 +270,7 @@
|
||||
"md_categories": "",
|
||||
"md_ccc_rates": "",
|
||||
"md_classes": "",
|
||||
"md_ded_notes": "",
|
||||
"md_hour_split": {
|
||||
"paint": "",
|
||||
"prep": ""
|
||||
@@ -450,6 +451,7 @@
|
||||
"status": "",
|
||||
"statuses": {
|
||||
"active_statuses": "",
|
||||
"color": "",
|
||||
"default_arrived": "",
|
||||
"default_bo": "",
|
||||
"default_canceled": "",
|
||||
@@ -466,6 +468,7 @@
|
||||
"open_statuses": "",
|
||||
"post_production_statuses": "",
|
||||
"pre_production_statuses": "",
|
||||
"production_colors": "",
|
||||
"production_statuses": ""
|
||||
},
|
||||
"target_touchtime": "",
|
||||
@@ -499,6 +502,8 @@
|
||||
},
|
||||
"emaillater": "",
|
||||
"employees": "",
|
||||
"estimators": "",
|
||||
"filehandlers": "",
|
||||
"insurancecos": "",
|
||||
"intakechecklist": "",
|
||||
"jobstatuses": "",
|
||||
@@ -531,6 +536,7 @@
|
||||
"save": ""
|
||||
},
|
||||
"validation": {
|
||||
"centermustexist": "",
|
||||
"larsplit": "",
|
||||
"useremailmustexist": ""
|
||||
}
|
||||
@@ -1053,6 +1059,7 @@
|
||||
},
|
||||
"profitcenter_labor": "",
|
||||
"profitcenter_part": "",
|
||||
"prt_dsmk_m": "",
|
||||
"prt_dsmk_p": "",
|
||||
"status": "Estado",
|
||||
"tax_part": "",
|
||||
@@ -1086,8 +1093,10 @@
|
||||
"addtoscoreboard": "",
|
||||
"allocate": "",
|
||||
"autoallocate": "",
|
||||
"changefilehandler": "",
|
||||
"changelaborrate": "",
|
||||
"changestatus": "Cambiar Estado",
|
||||
"changestimator": "",
|
||||
"convert": "Convertir",
|
||||
"deliver": "",
|
||||
"dms": {
|
||||
@@ -1199,9 +1208,11 @@
|
||||
"date_exported": "Exportado",
|
||||
"date_invoiced": "Facturado",
|
||||
"date_last_contacted": "",
|
||||
"date_next_contact": "",
|
||||
"date_open": "Abierto",
|
||||
"date_scheduled": "Programado",
|
||||
"ded_amt": "Deducible",
|
||||
"ded_note": "",
|
||||
"ded_status": "Estado deducible",
|
||||
"depreciation_taxes": "Depreciación / Impuestos",
|
||||
"dms": {
|
||||
@@ -1277,6 +1288,7 @@
|
||||
"local_tax_rate": "",
|
||||
"loss_date": "Fecha de pérdida",
|
||||
"loss_desc": "",
|
||||
"loss_of_use": "",
|
||||
"ma2s": "",
|
||||
"ma3s": "",
|
||||
"mabl": "",
|
||||
@@ -1526,6 +1538,7 @@
|
||||
"sale_labor": "",
|
||||
"sale_parts": "",
|
||||
"sales": "",
|
||||
"savebeforeconversion": "",
|
||||
"scheduledinchange": "",
|
||||
"specialcoveragepolicy": "",
|
||||
"state_tax_amt": "",
|
||||
@@ -1716,7 +1729,8 @@
|
||||
"new": ""
|
||||
},
|
||||
"errors": {
|
||||
"invalidphone": ""
|
||||
"invalidphone": "",
|
||||
"noattachedjobs": ""
|
||||
},
|
||||
"labels": {
|
||||
"archive": "",
|
||||
@@ -1726,6 +1740,7 @@
|
||||
"nojobs": "",
|
||||
"phonenumber": "",
|
||||
"presets": "",
|
||||
"recentonly": "",
|
||||
"selectmedia": "",
|
||||
"sentby": "",
|
||||
"typeamessage": "Enviar un mensaje...",
|
||||
@@ -1815,6 +1830,7 @@
|
||||
"receivebill": ""
|
||||
},
|
||||
"errors": {
|
||||
"associatedbills": "",
|
||||
"backordering": "",
|
||||
"creating": "Se encontró un error al crear el pedido de piezas."
|
||||
},
|
||||
@@ -1959,6 +1975,7 @@
|
||||
"zip": ""
|
||||
},
|
||||
"3rdpartypayer": "",
|
||||
"ab_proof_of_loss": "",
|
||||
"appointment_confirmation": "",
|
||||
"appointment_reminder": "",
|
||||
"casl_authorization": "",
|
||||
@@ -1976,12 +1993,16 @@
|
||||
"fippa_authorization": "",
|
||||
"glass_express_checklist": "",
|
||||
"guarantee": "",
|
||||
"individual_job_note": "",
|
||||
"invoice_customer_payable": "",
|
||||
"invoice_total_payable": "",
|
||||
"iou_form": "",
|
||||
"job_costing_ro": "",
|
||||
"job_notes": "",
|
||||
"key_tag": "",
|
||||
"mpi_animal_checklist": "",
|
||||
"mpi_eglass_auth": "",
|
||||
"mpi_final_acct_sheet": "",
|
||||
"paint_grid": "",
|
||||
"parts_label_single": "",
|
||||
"parts_list": "",
|
||||
@@ -2011,7 +2032,8 @@
|
||||
"worksheet_by_line_number": "",
|
||||
"worksheet_sorted_by_operation": "",
|
||||
"worksheet_sorted_by_operation_no_hours": "",
|
||||
"worksheet_sorted_by_operation_part_type": ""
|
||||
"worksheet_sorted_by_operation_part_type": "",
|
||||
"worksheet_sorted_by_operation_type": ""
|
||||
},
|
||||
"labels": {
|
||||
"groups": {
|
||||
@@ -2056,20 +2078,31 @@
|
||||
},
|
||||
"errors": {
|
||||
"boardupdate": "",
|
||||
"removing": ""
|
||||
"removing": "",
|
||||
"settings": ""
|
||||
},
|
||||
"labels": {
|
||||
"alert": "",
|
||||
"alertoff": "",
|
||||
"alerton": "",
|
||||
"ats": "",
|
||||
"bodyhours": "",
|
||||
"bodypriority": "",
|
||||
"cardsettings": "",
|
||||
"clm_no": "",
|
||||
"compact": "",
|
||||
"detailpriority": "",
|
||||
"employeeassignments": "",
|
||||
"employeesearch": "",
|
||||
"ins_co_nm": "",
|
||||
"jobdetail": "",
|
||||
"laborhrs": "",
|
||||
"note": "",
|
||||
"ownr_nm": "",
|
||||
"paintpriority": "",
|
||||
"production_note": "",
|
||||
"refinishhours": "",
|
||||
"scheduled_completion": "",
|
||||
"selectview": "",
|
||||
"sublets": "",
|
||||
"totalhours": "",
|
||||
@@ -2164,6 +2197,7 @@
|
||||
"open_orders_csr": "",
|
||||
"open_orders_estimator": "",
|
||||
"open_orders_ins_co": "",
|
||||
"open_orders_status": "",
|
||||
"parts_backorder": "",
|
||||
"parts_not_recieved": "",
|
||||
"payments_by_date": "",
|
||||
@@ -2195,6 +2229,11 @@
|
||||
"work_in_progress_payables": ""
|
||||
}
|
||||
},
|
||||
"schedule": {
|
||||
"labels": {
|
||||
"manualevent": ""
|
||||
}
|
||||
},
|
||||
"scoreboard": {
|
||||
"actions": {
|
||||
"edit": ""
|
||||
|
||||
@@ -270,6 +270,7 @@
|
||||
"md_categories": "",
|
||||
"md_ccc_rates": "",
|
||||
"md_classes": "",
|
||||
"md_ded_notes": "",
|
||||
"md_hour_split": {
|
||||
"paint": "",
|
||||
"prep": ""
|
||||
@@ -450,6 +451,7 @@
|
||||
"status": "",
|
||||
"statuses": {
|
||||
"active_statuses": "",
|
||||
"color": "",
|
||||
"default_arrived": "",
|
||||
"default_bo": "",
|
||||
"default_canceled": "",
|
||||
@@ -466,6 +468,7 @@
|
||||
"open_statuses": "",
|
||||
"post_production_statuses": "",
|
||||
"pre_production_statuses": "",
|
||||
"production_colors": "",
|
||||
"production_statuses": ""
|
||||
},
|
||||
"target_touchtime": "",
|
||||
@@ -499,6 +502,8 @@
|
||||
},
|
||||
"emaillater": "",
|
||||
"employees": "",
|
||||
"estimators": "",
|
||||
"filehandlers": "",
|
||||
"insurancecos": "",
|
||||
"intakechecklist": "",
|
||||
"jobstatuses": "",
|
||||
@@ -531,6 +536,7 @@
|
||||
"save": ""
|
||||
},
|
||||
"validation": {
|
||||
"centermustexist": "",
|
||||
"larsplit": "",
|
||||
"useremailmustexist": ""
|
||||
}
|
||||
@@ -1053,6 +1059,7 @@
|
||||
},
|
||||
"profitcenter_labor": "",
|
||||
"profitcenter_part": "",
|
||||
"prt_dsmk_m": "",
|
||||
"prt_dsmk_p": "",
|
||||
"status": "Statut",
|
||||
"tax_part": "",
|
||||
@@ -1086,8 +1093,10 @@
|
||||
"addtoscoreboard": "",
|
||||
"allocate": "",
|
||||
"autoallocate": "",
|
||||
"changefilehandler": "",
|
||||
"changelaborrate": "",
|
||||
"changestatus": "Changer le statut",
|
||||
"changestimator": "",
|
||||
"convert": "Convertir",
|
||||
"deliver": "",
|
||||
"dms": {
|
||||
@@ -1199,9 +1208,11 @@
|
||||
"date_exported": "Exportés",
|
||||
"date_invoiced": "Facturé",
|
||||
"date_last_contacted": "",
|
||||
"date_next_contact": "",
|
||||
"date_open": "Ouvrir",
|
||||
"date_scheduled": "Prévu",
|
||||
"ded_amt": "Déductible",
|
||||
"ded_note": "",
|
||||
"ded_status": "Statut de franchise",
|
||||
"depreciation_taxes": "Amortissement / taxes",
|
||||
"dms": {
|
||||
@@ -1277,6 +1288,7 @@
|
||||
"local_tax_rate": "",
|
||||
"loss_date": "Date de perte",
|
||||
"loss_desc": "",
|
||||
"loss_of_use": "",
|
||||
"ma2s": "",
|
||||
"ma3s": "",
|
||||
"mabl": "",
|
||||
@@ -1526,6 +1538,7 @@
|
||||
"sale_labor": "",
|
||||
"sale_parts": "",
|
||||
"sales": "",
|
||||
"savebeforeconversion": "",
|
||||
"scheduledinchange": "",
|
||||
"specialcoveragepolicy": "",
|
||||
"state_tax_amt": "",
|
||||
@@ -1716,7 +1729,8 @@
|
||||
"new": ""
|
||||
},
|
||||
"errors": {
|
||||
"invalidphone": ""
|
||||
"invalidphone": "",
|
||||
"noattachedjobs": ""
|
||||
},
|
||||
"labels": {
|
||||
"archive": "",
|
||||
@@ -1726,6 +1740,7 @@
|
||||
"nojobs": "",
|
||||
"phonenumber": "",
|
||||
"presets": "",
|
||||
"recentonly": "",
|
||||
"selectmedia": "",
|
||||
"sentby": "",
|
||||
"typeamessage": "Envoyer un message...",
|
||||
@@ -1815,6 +1830,7 @@
|
||||
"receivebill": ""
|
||||
},
|
||||
"errors": {
|
||||
"associatedbills": "",
|
||||
"backordering": "",
|
||||
"creating": "Erreur rencontrée lors de la création de la commande de pièces."
|
||||
},
|
||||
@@ -1959,6 +1975,7 @@
|
||||
"zip": ""
|
||||
},
|
||||
"3rdpartypayer": "",
|
||||
"ab_proof_of_loss": "",
|
||||
"appointment_confirmation": "",
|
||||
"appointment_reminder": "",
|
||||
"casl_authorization": "",
|
||||
@@ -1976,12 +1993,16 @@
|
||||
"fippa_authorization": "",
|
||||
"glass_express_checklist": "",
|
||||
"guarantee": "",
|
||||
"individual_job_note": "",
|
||||
"invoice_customer_payable": "",
|
||||
"invoice_total_payable": "",
|
||||
"iou_form": "",
|
||||
"job_costing_ro": "",
|
||||
"job_notes": "",
|
||||
"key_tag": "",
|
||||
"mpi_animal_checklist": "",
|
||||
"mpi_eglass_auth": "",
|
||||
"mpi_final_acct_sheet": "",
|
||||
"paint_grid": "",
|
||||
"parts_label_single": "",
|
||||
"parts_list": "",
|
||||
@@ -2011,7 +2032,8 @@
|
||||
"worksheet_by_line_number": "",
|
||||
"worksheet_sorted_by_operation": "",
|
||||
"worksheet_sorted_by_operation_no_hours": "",
|
||||
"worksheet_sorted_by_operation_part_type": ""
|
||||
"worksheet_sorted_by_operation_part_type": "",
|
||||
"worksheet_sorted_by_operation_type": ""
|
||||
},
|
||||
"labels": {
|
||||
"groups": {
|
||||
@@ -2056,20 +2078,31 @@
|
||||
},
|
||||
"errors": {
|
||||
"boardupdate": "",
|
||||
"removing": ""
|
||||
"removing": "",
|
||||
"settings": ""
|
||||
},
|
||||
"labels": {
|
||||
"alert": "",
|
||||
"alertoff": "",
|
||||
"alerton": "",
|
||||
"ats": "",
|
||||
"bodyhours": "",
|
||||
"bodypriority": "",
|
||||
"cardsettings": "",
|
||||
"clm_no": "",
|
||||
"compact": "",
|
||||
"detailpriority": "",
|
||||
"employeeassignments": "",
|
||||
"employeesearch": "",
|
||||
"ins_co_nm": "",
|
||||
"jobdetail": "",
|
||||
"laborhrs": "",
|
||||
"note": "",
|
||||
"ownr_nm": "",
|
||||
"paintpriority": "",
|
||||
"production_note": "",
|
||||
"refinishhours": "",
|
||||
"scheduled_completion": "",
|
||||
"selectview": "",
|
||||
"sublets": "",
|
||||
"totalhours": "",
|
||||
@@ -2164,6 +2197,7 @@
|
||||
"open_orders_csr": "",
|
||||
"open_orders_estimator": "",
|
||||
"open_orders_ins_co": "",
|
||||
"open_orders_status": "",
|
||||
"parts_backorder": "",
|
||||
"parts_not_recieved": "",
|
||||
"payments_by_date": "",
|
||||
@@ -2195,6 +2229,11 @@
|
||||
"work_in_progress_payables": ""
|
||||
}
|
||||
},
|
||||
"schedule": {
|
||||
"labels": {
|
||||
"manualevent": ""
|
||||
}
|
||||
},
|
||||
"scoreboard": {
|
||||
"actions": {
|
||||
"edit": ""
|
||||
|
||||
@@ -12,7 +12,9 @@ export function DateFormatter(props) {
|
||||
|
||||
export function DateTimeFormatter(props) {
|
||||
return props.children
|
||||
? moment(props.children).format("MM/DD/YYYY hh:mm a")
|
||||
? moment(props.children).format(
|
||||
props.format ? props.format : "MM/DD/YYYY hh:mm a"
|
||||
)
|
||||
: null;
|
||||
}
|
||||
|
||||
|
||||
@@ -182,6 +182,18 @@ export const TemplateList = (type, context) => {
|
||||
disabled: false,
|
||||
group: "worksheet",
|
||||
},
|
||||
worksheet_sorted_by_operation_type: {
|
||||
title: i18n.t(
|
||||
"printcenter.jobs.worksheet_sorted_by_operation_type"
|
||||
),
|
||||
description: "All Jobs Notes",
|
||||
subject: i18n.t(
|
||||
"printcenter.jobs.worksheet_sorted_by_operation_type"
|
||||
),
|
||||
key: "worksheet_sorted_by_operation_type",
|
||||
disabled: false,
|
||||
group: "worksheet",
|
||||
},
|
||||
worksheet_sorted_by_operation: {
|
||||
title: i18n.t("printcenter.jobs.worksheet_sorted_by_operation"),
|
||||
description: "All Jobs Notes",
|
||||
@@ -374,6 +386,50 @@ export const TemplateList = (type, context) => {
|
||||
CA_SK: true,
|
||||
},
|
||||
},
|
||||
mpi_final_acct_sheet: {
|
||||
title: i18n.t("printcenter.jobs.mpi_final_acct_sheet"),
|
||||
description: "Thank You Letter by RO",
|
||||
key: "mpi_final_acct_sheet",
|
||||
subject: i18n.t("printcenter.jobs.mpi_final_acct_sheet"),
|
||||
disabled: false,
|
||||
group: "post",
|
||||
regions: {
|
||||
CA_MB: true,
|
||||
},
|
||||
},
|
||||
mpi_eglass_auth: {
|
||||
title: i18n.t("printcenter.jobs.mpi_eglass_auth"),
|
||||
description: "Thank You Letter by RO",
|
||||
key: "mpi_eglass_auth",
|
||||
subject: i18n.t("printcenter.jobs.mpi_eglass_auth"),
|
||||
disabled: false,
|
||||
group: "pre",
|
||||
regions: {
|
||||
CA_MB: true,
|
||||
},
|
||||
},
|
||||
mpi_animal_checklist: {
|
||||
title: i18n.t("printcenter.jobs.mpi_animal_checklist"),
|
||||
description: "Thank You Letter by RO",
|
||||
key: "mpi_animal_checklist",
|
||||
subject: i18n.t("printcenter.jobs.mpi_animal_checklist"),
|
||||
disabled: false,
|
||||
group: "pre",
|
||||
regions: {
|
||||
CA_MB: true,
|
||||
},
|
||||
},
|
||||
ab_proof_of_loss: {
|
||||
title: i18n.t("printcenter.jobs.ab_proof_of_loss"),
|
||||
description: "Thank You Letter by RO",
|
||||
key: "ab_proof_of_loss",
|
||||
subject: i18n.t("printcenter.jobs.ab_proof_of_loss"),
|
||||
disabled: false,
|
||||
group: "pre",
|
||||
regions: {
|
||||
CA_AB: true,
|
||||
},
|
||||
},
|
||||
// parts_label_multi: {
|
||||
// title: i18n.t("printcenter.jobs.parts_label_multi"),
|
||||
// description: "Thank You Letter by RO",
|
||||
@@ -407,6 +463,15 @@ export const TemplateList = (type, context) => {
|
||||
subject: i18n.t("printcenter.jobs.csi_invitation_action"),
|
||||
disabled: false,
|
||||
},
|
||||
individual_job_note: {
|
||||
title: i18n.t("printcenter.jobs.individual_job_note"),
|
||||
description: "CSI invite",
|
||||
key: "individual_job_note",
|
||||
subject: i18n.t("printcenter.jobs.individual_job_note", {
|
||||
ro_number: (context && context.ro_number) || "",
|
||||
}),
|
||||
disabled: false,
|
||||
},
|
||||
}
|
||||
: {}),
|
||||
...(!type || type === "appointment"
|
||||
@@ -1148,6 +1213,19 @@ export const TemplateList = (type, context) => {
|
||||
},
|
||||
group: "jobs",
|
||||
},
|
||||
open_orders_status: {
|
||||
title: i18n.t("reportcenter.templates.open_orders_status"),
|
||||
description: "",
|
||||
subject: i18n.t("reportcenter.templates.open_orders_status"),
|
||||
key: "open_orders_status",
|
||||
//idtype: "vendor",
|
||||
disabled: false,
|
||||
rangeFilter: {
|
||||
object: i18n.t("reportcenter.labels.objects.jobs"),
|
||||
field: i18n.t("jobs.fields.date_open"),
|
||||
},
|
||||
group: "jobs",
|
||||
},
|
||||
open_orders_csr: {
|
||||
title: i18n.t("reportcenter.templates.open_orders_csr"),
|
||||
description: "",
|
||||
|
||||
@@ -9,5 +9,11 @@ export function alphaSort(a, b) {
|
||||
}
|
||||
|
||||
export function dateSort(a, b) {
|
||||
return new Date(b) - new Date(a);
|
||||
return new Date(a) - new Date(b);
|
||||
}
|
||||
|
||||
export function statusSort(a, b, statusList) {
|
||||
return (
|
||||
statusList.findIndex((x) => x === a) - statusList.findIndex((x) => x === b)
|
||||
);
|
||||
}
|
||||
|
||||
3261
client/yarn.lock
3261
client/yarn.lock
File diff suppressed because it is too large
Load Diff
@@ -203,6 +203,8 @@
|
||||
- authlevel
|
||||
- default_prod_list_view
|
||||
- id
|
||||
- kanban_settings
|
||||
- qbo_realmId
|
||||
- shopid
|
||||
- useremail
|
||||
filter:
|
||||
@@ -216,6 +218,8 @@
|
||||
- active
|
||||
- authlevel
|
||||
- default_prod_list_view
|
||||
- kanban_settings
|
||||
- qbo_realmId
|
||||
filter:
|
||||
bodyshop:
|
||||
associations:
|
||||
@@ -824,6 +828,9 @@
|
||||
- md_categories
|
||||
- md_ccc_rates
|
||||
- md_classes
|
||||
- md_ded_notes
|
||||
- md_estimators
|
||||
- md_filehandlers
|
||||
- md_hour_split
|
||||
- md_ins_cos
|
||||
- md_jobline_presets
|
||||
@@ -838,6 +845,8 @@
|
||||
- md_responsibility_centers
|
||||
- md_ro_statuses
|
||||
- messagingservicesid
|
||||
- pbs_configuration
|
||||
- pbs_serialnumber
|
||||
- phone
|
||||
- prodtargethrs
|
||||
- production_config
|
||||
@@ -898,6 +907,9 @@
|
||||
- md_categories
|
||||
- md_ccc_rates
|
||||
- md_classes
|
||||
- md_ded_notes
|
||||
- md_estimators
|
||||
- md_filehandlers
|
||||
- md_hour_split
|
||||
- md_ins_cos
|
||||
- md_jobline_presets
|
||||
@@ -2582,9 +2594,11 @@
|
||||
- date_exported
|
||||
- date_invoiced
|
||||
- date_last_contacted
|
||||
- date_next_contact
|
||||
- date_open
|
||||
- date_scheduled
|
||||
- ded_amt
|
||||
- ded_note
|
||||
- ded_status
|
||||
- deliverchecklist
|
||||
- depreciation_taxes
|
||||
@@ -2660,6 +2674,7 @@
|
||||
- loss_cat
|
||||
- loss_date
|
||||
- loss_desc
|
||||
- loss_of_use
|
||||
- loss_type
|
||||
- other_amount_payable
|
||||
- owner_owing
|
||||
@@ -2831,9 +2846,11 @@
|
||||
- date_exported
|
||||
- date_invoiced
|
||||
- date_last_contacted
|
||||
- date_next_contact
|
||||
- date_open
|
||||
- date_scheduled
|
||||
- ded_amt
|
||||
- ded_note
|
||||
- ded_status
|
||||
- deliverchecklist
|
||||
- depreciation_taxes
|
||||
@@ -2909,6 +2926,7 @@
|
||||
- loss_cat
|
||||
- loss_date
|
||||
- loss_desc
|
||||
- loss_of_use
|
||||
- loss_type
|
||||
- other_amount_payable
|
||||
- owner_owing
|
||||
@@ -3090,9 +3108,11 @@
|
||||
- date_exported
|
||||
- date_invoiced
|
||||
- date_last_contacted
|
||||
- date_next_contact
|
||||
- date_open
|
||||
- date_scheduled
|
||||
- ded_amt
|
||||
- ded_note
|
||||
- ded_status
|
||||
- deliverchecklist
|
||||
- depreciation_taxes
|
||||
@@ -3168,6 +3188,7 @@
|
||||
- loss_cat
|
||||
- loss_date
|
||||
- loss_desc
|
||||
- loss_of_use
|
||||
- loss_type
|
||||
- other_amount_payable
|
||||
- owner_owing
|
||||
|
||||
@@ -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 "pbs_configuration" jsonb
|
||||
-- null default jsonb_build_object();
|
||||
@@ -0,0 +1,2 @@
|
||||
alter table "public"."bodyshops" add column "pbs_configuration" jsonb
|
||||
null default jsonb_build_object();
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user