Compare commits
133 Commits
release/20
...
feature/IO
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3c3f9521f6 | ||
|
|
6155b8bf24 | ||
|
|
2586855f11 | ||
|
|
b3b3c4c737 | ||
|
|
abe5fadeea | ||
|
|
f1a10e0df4 | ||
|
|
7dc3c00628 | ||
|
|
7594f53e88 | ||
|
|
b861957342 | ||
|
|
2bf24ff5a1 | ||
|
|
833baca9cc | ||
|
|
889ef61185 | ||
|
|
add1eddbc1 | ||
|
|
0cabd80b94 | ||
|
|
7f756bab88 | ||
|
|
99b847822f | ||
|
|
f66d9b8c09 | ||
|
|
5660de42af | ||
|
|
ccbe92c275 | ||
|
|
b0ea8a71fb | ||
|
|
060871306f | ||
|
|
2eb4e142ff | ||
|
|
adf8cf9e8d | ||
|
|
e921f28105 | ||
|
|
51ebfd86e7 | ||
|
|
79a90bb9ee | ||
|
|
1664f9c935 | ||
|
|
ea7c22daec | ||
|
|
10ffb33ec9 | ||
|
|
f9e023f922 | ||
|
|
c7832bdd82 | ||
|
|
2df046c39d | ||
|
|
46065f1986 | ||
|
|
5eda224393 | ||
|
|
e387abcd14 | ||
|
|
4c2d4e20a6 | ||
|
|
d9e2ef9300 | ||
|
|
b00fdadc1b | ||
|
|
4d5e06b9fc | ||
|
|
1f4c1c9e92 | ||
|
|
2227acab3a | ||
|
|
081165b6f5 | ||
|
|
7e717c0b1f | ||
|
|
e242aaa9f5 | ||
|
|
2db88f57df | ||
|
|
cd0b7a4e56 | ||
|
|
2a3b4e89ab | ||
|
|
1d77eda3e2 | ||
|
|
490e1f696a | ||
|
|
b96c618f54 | ||
|
|
b9d11580d4 | ||
|
|
27fadb9ae2 | ||
|
|
4c5a2cefe9 | ||
|
|
175692559c | ||
|
|
57b27f73c3 | ||
|
|
7badb09ba1 | ||
|
|
c39f1d824a | ||
|
|
132cf98a37 | ||
|
|
f4b3a990d7 | ||
|
|
20371ea00d | ||
|
|
3086a654a1 | ||
|
|
60768c8847 | ||
|
|
a68f8d6880 | ||
|
|
522665256e | ||
|
|
d3fe2c9d06 | ||
|
|
f080e84985 | ||
|
|
2a0ad46eea | ||
|
|
7725080a11 | ||
|
|
1932795f55 | ||
|
|
cc7bd1c792 | ||
|
|
7799d93f3d | ||
|
|
a7cf36d5f8 | ||
|
|
54cc02068c | ||
|
|
f575870685 | ||
|
|
171b61b92f | ||
|
|
de44116940 | ||
|
|
3c0a883326 | ||
|
|
14d6cc94dd | ||
|
|
d787821345 | ||
|
|
ed6eab4c38 | ||
|
|
b12c9407d9 | ||
|
|
300aee5b02 | ||
|
|
0a93551db4 | ||
|
|
8602ccbb8a | ||
|
|
d7b0e3046b | ||
|
|
0e06b449cb | ||
|
|
8e8208dd9a | ||
|
|
79e2fecb24 | ||
|
|
86e909e4e9 | ||
|
|
c7ff893397 | ||
|
|
3981b8684c | ||
|
|
1fb856f95f | ||
|
|
c62c3fa938 | ||
|
|
be4feca990 | ||
|
|
ec45454b3d | ||
|
|
0e78cb47f9 | ||
|
|
e5b8d003ec | ||
|
|
2eb81dde37 | ||
|
|
614549a545 | ||
|
|
5717727d2a | ||
|
|
f3714cea1e | ||
|
|
a3375e6152 | ||
|
|
f48fb7130e | ||
|
|
1460fa6fd7 | ||
|
|
3d9a07bd39 | ||
|
|
bd4aa4027a | ||
|
|
9fa995f002 | ||
|
|
3ed48b26f1 | ||
|
|
d585cacdfc | ||
|
|
55ddaca328 | ||
|
|
146bf95e51 | ||
|
|
58defad2ea | ||
|
|
9c40a03a06 | ||
|
|
9b4247d6f6 | ||
|
|
8a6d94f193 | ||
|
|
6e7d1abd70 | ||
|
|
6bd74aae87 | ||
|
|
f21caa10fc | ||
|
|
3d164eb070 | ||
|
|
88ab3a21e2 | ||
|
|
45bc1893a0 | ||
|
|
d053e682d7 | ||
|
|
984a4a4cf6 | ||
|
|
9438ef9683 | ||
|
|
10a354e479 | ||
|
|
31092c20a9 | ||
|
|
86beaf049c | ||
|
|
3bc0653230 | ||
|
|
b950b3f825 | ||
|
|
3891fbefdf | ||
|
|
73bcc72fc3 | ||
|
|
f3911859c7 | ||
|
|
12e3d61cfb |
1
.vscode/settings.json
vendored
1
.vscode/settings.json
vendored
@@ -1,5 +1,4 @@
|
||||
{
|
||||
"eslint.workingDirectories": ["./client", "./"],
|
||||
"xml.fileAssociations": [
|
||||
{
|
||||
"pattern": "**/Test.xml",
|
||||
|
||||
@@ -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
|
||||
@@ -3665,6 +3665,27 @@
|
||||
</translation>
|
||||
</translations>
|
||||
</concept_node>
|
||||
<concept_node>
|
||||
<name>addpartsrule</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>addspeedprint</name>
|
||||
<definition_loaded>false</definition_loaded>
|
||||
@@ -4572,6 +4593,27 @@
|
||||
</translation>
|
||||
</translations>
|
||||
</concept_node>
|
||||
<concept_node>
|
||||
<name>enforce_conversion_category</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>enforce_conversion_csr</name>
|
||||
<definition_loaded>false</definition_loaded>
|
||||
@@ -5411,6 +5453,27 @@
|
||||
</translation>
|
||||
</translations>
|
||||
</concept_node>
|
||||
<concept_node>
|
||||
<name>md_lost_sale_reasons</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>md_parts_order_comment</name>
|
||||
<definition_loaded>false</definition_loaded>
|
||||
@@ -5432,6 +5495,53 @@
|
||||
</translation>
|
||||
</translations>
|
||||
</concept_node>
|
||||
<folder_node>
|
||||
<name>md_parts_scan</name>
|
||||
<children>
|
||||
<concept_node>
|
||||
<name>expression</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>flags</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>
|
||||
<concept_node>
|
||||
<name>md_payment_types</name>
|
||||
<definition_loaded>false</definition_loaded>
|
||||
@@ -8754,6 +8864,27 @@
|
||||
</translation>
|
||||
</translations>
|
||||
</concept_node>
|
||||
<concept_node>
|
||||
<name>tt_enforce_hours_for_tech_console</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>use_fippa</name>
|
||||
<definition_loaded>false</definition_loaded>
|
||||
@@ -8775,6 +8906,27 @@
|
||||
</translation>
|
||||
</translations>
|
||||
</concept_node>
|
||||
<concept_node>
|
||||
<name>use_paint_scale_data</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>uselocalmediaserver</name>
|
||||
<definition_loaded>false</definition_loaded>
|
||||
@@ -9609,6 +9761,27 @@
|
||||
</translation>
|
||||
</translations>
|
||||
</concept_node>
|
||||
<concept_node>
|
||||
<name>partsscan</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>printlater</name>
|
||||
<definition_loaded>false</definition_loaded>
|
||||
@@ -16187,6 +16360,27 @@
|
||||
</translation>
|
||||
</translations>
|
||||
</concept_node>
|
||||
<concept_node>
|
||||
<name>excel</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>exceptiontitle</name>
|
||||
<definition_loaded>false</definition_loaded>
|
||||
@@ -17179,6 +17373,27 @@
|
||||
</translation>
|
||||
</translations>
|
||||
</concept_node>
|
||||
<concept_node>
|
||||
<name>total</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>totals</name>
|
||||
<definition_loaded>false</definition_loaded>
|
||||
@@ -20758,6 +20973,27 @@
|
||||
</translation>
|
||||
</translations>
|
||||
</concept_node>
|
||||
<concept_node>
|
||||
<name>sendpartspricechange</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>sendtodms</name>
|
||||
<definition_loaded>false</definition_loaded>
|
||||
@@ -21204,6 +21440,27 @@
|
||||
</translation>
|
||||
</translations>
|
||||
</concept_node>
|
||||
<concept_node>
|
||||
<name>partspricechange</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>saving</name>
|
||||
<definition_loaded>false</definition_loaded>
|
||||
@@ -24153,6 +24410,27 @@
|
||||
</translation>
|
||||
</translations>
|
||||
</concept_node>
|
||||
<concept_node>
|
||||
<name>lost_sale_reason</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>
|
||||
@@ -33231,6 +33509,27 @@
|
||||
</translation>
|
||||
</translations>
|
||||
</concept_node>
|
||||
<concept_node>
|
||||
<name>savetojobnotes</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>
|
||||
@@ -33612,6 +33911,27 @@
|
||||
<folder_node>
|
||||
<name>errors</name>
|
||||
<children>
|
||||
<concept_node>
|
||||
<name>deleting</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>noaccess</name>
|
||||
<definition_loaded>false</definition_loaded>
|
||||
@@ -34173,6 +34493,27 @@
|
||||
</translation>
|
||||
</translations>
|
||||
</concept_node>
|
||||
<concept_node>
|
||||
<name>deleteconfirm</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>existing_owners</name>
|
||||
<definition_loaded>false</definition_loaded>
|
||||
@@ -34283,6 +34624,27 @@
|
||||
<folder_node>
|
||||
<name>successes</name>
|
||||
<children>
|
||||
<concept_node>
|
||||
<name>delete</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>save</name>
|
||||
<definition_loaded>false</definition_loaded>
|
||||
@@ -37290,6 +37652,27 @@
|
||||
</translation>
|
||||
</translations>
|
||||
</concept_node>
|
||||
<concept_node>
|
||||
<name>dms_posting_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>envelope_return_address</name>
|
||||
<definition_loaded>false</definition_loaded>
|
||||
@@ -40775,6 +41158,27 @@
|
||||
</translation>
|
||||
</translations>
|
||||
</concept_node>
|
||||
<concept_node>
|
||||
<name>customer_list</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>cycle_time_analysis</name>
|
||||
<definition_loaded>false</definition_loaded>
|
||||
@@ -40922,6 +41326,48 @@
|
||||
</translation>
|
||||
</translations>
|
||||
</concept_node>
|
||||
<concept_node>
|
||||
<name>exported_gsr_by_ro</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>exported_gsr_by_ro_labor</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>gsr_by_atp</name>
|
||||
<definition_loaded>false</definition_loaded>
|
||||
@@ -42780,6 +43226,27 @@
|
||||
</translation>
|
||||
</translations>
|
||||
</concept_node>
|
||||
<concept_node>
|
||||
<name>ins_co_nm_filter</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>intake</name>
|
||||
<definition_loaded>false</definition_loaded>
|
||||
@@ -44744,6 +45211,27 @@
|
||||
</translation>
|
||||
</translations>
|
||||
</concept_node>
|
||||
<concept_node>
|
||||
<name>hoursenteredmorethanavailable</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>
|
||||
@@ -47056,6 +47544,27 @@
|
||||
<folder_node>
|
||||
<name>errors</name>
|
||||
<children>
|
||||
<concept_node>
|
||||
<name>deleting</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>noaccess</name>
|
||||
<definition_loaded>false</definition_loaded>
|
||||
@@ -47680,6 +48189,27 @@
|
||||
<folder_node>
|
||||
<name>labels</name>
|
||||
<children>
|
||||
<concept_node>
|
||||
<name>deleteconfirm</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>fromvehicle</name>
|
||||
<definition_loaded>false</definition_loaded>
|
||||
@@ -47769,6 +48299,27 @@
|
||||
<folder_node>
|
||||
<name>successes</name>
|
||||
<children>
|
||||
<concept_node>
|
||||
<name>delete</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>save</name>
|
||||
<definition_loaded>false</definition_loaded>
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
GENERATE_SOURCEMAP=false
|
||||
REACT_APP_GRAPHQL_ENDPOINT=https://db.imex.online/v1/graphql
|
||||
REACT_APP_GRAPHQL_ENDPOINT_WS=wss://db.imex.online/v1/graphql
|
||||
REACT_APP_GA_CODE=231103507
|
||||
|
||||
@@ -46,10 +46,11 @@
|
||||
"react-cookie": "^4.1.1",
|
||||
"react-dom": "^17.0.2",
|
||||
"react-drag-listview": "^0.2.1",
|
||||
"react-grid-gallery": "^0.5.5",
|
||||
"react-grid-gallery": "^1.0.0",
|
||||
"react-grid-layout": "^1.3.4",
|
||||
"react-i18next": "^12.2.0",
|
||||
"react-icons": "^4.7.1",
|
||||
"react-image-lightbox": "^5.1.4",
|
||||
"react-number-format": "^5.1.3",
|
||||
"react-redux": "^8.0.5",
|
||||
"react-resizable": "^3.0.4",
|
||||
|
||||
@@ -143,13 +143,16 @@
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//Update row highlighting on production board.
|
||||
//Update row highlighting on production board.
|
||||
.ant-table-tbody > tr.ant-table-row:hover > td {
|
||||
background: #eaeaea !important;
|
||||
}
|
||||
|
||||
.job-line-manual{
|
||||
.job-line-manual {
|
||||
color: tomato;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
td.ant-table-column-sort {
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
@@ -7,7 +7,9 @@ import { createStructuredSelector } from "reselect";
|
||||
import { selectBreadcrumbs } from "../../redux/application/application.selectors";
|
||||
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||
import GlobalSearch from "../global-search/global-search.component";
|
||||
import GlobalSearchOs from "../global-search/global-search-os.component";
|
||||
import "./breadcrumbs.styles.scss";
|
||||
import { useTreatments } from "@splitsoftware/splitio-react";
|
||||
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
breadcrumbs: selectBreadcrumbs,
|
||||
@@ -15,6 +17,12 @@ const mapStateToProps = createStructuredSelector({
|
||||
});
|
||||
|
||||
export function BreadCrumbs({ breadcrumbs, bodyshop }) {
|
||||
const { OpenSearch } = useTreatments(
|
||||
["OpenSearch"],
|
||||
{},
|
||||
bodyshop && bodyshop.imexshopid
|
||||
);
|
||||
|
||||
return (
|
||||
<Row className="breadcrumb-container">
|
||||
<Col xs={24} sm={24} md={16}>
|
||||
@@ -38,7 +46,7 @@ export function BreadCrumbs({ breadcrumbs, bodyshop }) {
|
||||
</Breadcrumb>
|
||||
</Col>
|
||||
<Col xs={24} sm={24} md={8}>
|
||||
<GlobalSearch />
|
||||
{OpenSearch.treatment === "on" ? <GlobalSearchOs /> : <GlobalSearch />}
|
||||
</Col>
|
||||
</Row>
|
||||
);
|
||||
|
||||
@@ -10,7 +10,10 @@ export default function CABCpvrtCalculator({ disabled, form }) {
|
||||
|
||||
const handleFinish = async (values) => {
|
||||
logImEXEvent("job_ca_bc_pvrt_calculate");
|
||||
form.setFieldsValue({ ca_bc_pvrt: ((values.rate||0) * (values.days||0)).toFixed(2) });
|
||||
form.setFieldsValue({
|
||||
ca_bc_pvrt: ((values.rate || 0) * (values.days || 0)).toFixed(2),
|
||||
});
|
||||
form.setFields([{ name: "ca_bc_pvrt", touched: true }]);
|
||||
setVisibility(false);
|
||||
};
|
||||
|
||||
|
||||
@@ -0,0 +1,216 @@
|
||||
import { AutoComplete, Divider, Input, Space } from "antd";
|
||||
import axios from "axios";
|
||||
import _ from "lodash";
|
||||
import React, { useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { Link, useHistory } from "react-router-dom";
|
||||
import PhoneNumberFormatter from "../../utils/PhoneFormatter";
|
||||
import OwnerNameDisplay, {
|
||||
OwnerNameDisplayFunction,
|
||||
} from "../owner-name-display/owner-name-display.component";
|
||||
import VehicleVinDisplay from "../vehicle-vin-display/vehicle-vin-display.component";
|
||||
|
||||
export default function GlobalSearchOs() {
|
||||
const { t } = useTranslation();
|
||||
const history = useHistory();
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [data, setData] = useState(false);
|
||||
|
||||
const executeSearch = async (v) => {
|
||||
if (v && v && v !== "" && v.length >= 3) {
|
||||
try {
|
||||
setLoading(true);
|
||||
const searchData = await axios.post("/search", {
|
||||
search: v,
|
||||
});
|
||||
|
||||
const resultsByType = {
|
||||
payments: [],
|
||||
jobs: [],
|
||||
bills: [],
|
||||
owners: [],
|
||||
vehicles: [],
|
||||
};
|
||||
|
||||
searchData.data.hits.hits.forEach((hit) => {
|
||||
resultsByType[hit._index].push(hit._source);
|
||||
});
|
||||
setData([
|
||||
{
|
||||
label: renderTitle(t("menus.header.search.jobs")),
|
||||
options: resultsByType.jobs.map((job) => {
|
||||
return {
|
||||
key: job.id,
|
||||
value: job.ro_number || "N/A",
|
||||
label: (
|
||||
<Link to={`/manage/jobs/${job.id}`}>
|
||||
<Space size="small" split={<Divider type="vertical" />}>
|
||||
<strong>{job.ro_number || t("general.labels.na")}</strong>
|
||||
<span>{`${job.status || ""}`}</span>
|
||||
<span>
|
||||
<OwnerNameDisplay ownerObject={job} />
|
||||
</span>
|
||||
<span>{`${job.v_model_yr || ""} ${
|
||||
job.v_make_desc || ""
|
||||
} ${job.v_model_desc || ""}`}</span>
|
||||
<span>{`${job.clm_no || ""}`}</span>
|
||||
</Space>
|
||||
</Link>
|
||||
),
|
||||
};
|
||||
}),
|
||||
},
|
||||
{
|
||||
label: renderTitle(t("menus.header.search.owners")),
|
||||
options: resultsByType.owners.map((owner) => {
|
||||
return {
|
||||
key: owner.id,
|
||||
value: OwnerNameDisplayFunction(owner),
|
||||
label: (
|
||||
<Link to={`/manage/owners/${owner.id}`}>
|
||||
<Space
|
||||
size="small"
|
||||
split={<Divider type="vertical" />}
|
||||
wrap
|
||||
>
|
||||
<span>
|
||||
<OwnerNameDisplay ownerObject={owner} />
|
||||
</span>
|
||||
<PhoneNumberFormatter>
|
||||
{owner.ownr_ph1}
|
||||
</PhoneNumberFormatter>
|
||||
<PhoneNumberFormatter>
|
||||
{owner.ownr_ph2}
|
||||
</PhoneNumberFormatter>
|
||||
</Space>
|
||||
</Link>
|
||||
),
|
||||
};
|
||||
}),
|
||||
},
|
||||
{
|
||||
label: renderTitle(t("menus.header.search.vehicles")),
|
||||
options: resultsByType.vehicles.map((vehicle) => {
|
||||
return {
|
||||
key: vehicle.id,
|
||||
value: `${vehicle.v_model_yr || ""} ${
|
||||
vehicle.v_make_desc || ""
|
||||
} ${vehicle.v_model_desc || ""}`,
|
||||
label: (
|
||||
<Link to={`/manage/vehicles/${vehicle.id}`}>
|
||||
<Space size="small" split={<Divider type="vertical" />}>
|
||||
<span>
|
||||
{`${vehicle.v_model_yr || ""} ${
|
||||
vehicle.v_make_desc || ""
|
||||
} ${vehicle.v_model_desc || ""}`}
|
||||
</span>
|
||||
<span>{vehicle.plate_no || ""}</span>
|
||||
<span>
|
||||
<VehicleVinDisplay>
|
||||
{vehicle.v_vin || ""}
|
||||
</VehicleVinDisplay>
|
||||
</span>
|
||||
</Space>
|
||||
</Link>
|
||||
),
|
||||
};
|
||||
}),
|
||||
},
|
||||
{
|
||||
label: renderTitle(t("menus.header.search.payments")),
|
||||
options: resultsByType.payments.map((payment) => {
|
||||
return {
|
||||
key: payment.id,
|
||||
value: `${payment.job?.ro_number} ${payment.amount}`,
|
||||
label: (
|
||||
<Link to={`/manage/jobs/${payment.job?.id}`}>
|
||||
<Space size="small" split={<Divider type="vertical" />}>
|
||||
<span>{payment.paymentnum}</span>
|
||||
<span>{payment.job?.ro_number}</span>
|
||||
<span>{payment.memo || ""}</span>
|
||||
<span>{payment.amount || ""}</span>
|
||||
<span>{payment.transactionid || ""}</span>
|
||||
</Space>
|
||||
</Link>
|
||||
),
|
||||
};
|
||||
}),
|
||||
},
|
||||
{
|
||||
label: renderTitle(t("menus.header.search.bills")),
|
||||
options: resultsByType.bills.map((bill) => {
|
||||
return {
|
||||
key: bill.id,
|
||||
value: `${bill.invoice_number} - ${bill.vendor.name}`,
|
||||
label: (
|
||||
<Link to={`/manage/bills?billid=${bill.id}`}>
|
||||
<Space size="small" split={<Divider type="vertical" />}>
|
||||
<span>{bill.invoice_number}</span>
|
||||
<span>{bill.vendor.name}</span>
|
||||
<span>{bill.date}</span>
|
||||
</Space>
|
||||
</Link>
|
||||
),
|
||||
};
|
||||
}),
|
||||
},
|
||||
// {
|
||||
// label: renderTitle(t("menus.header.search.phonebook")),
|
||||
// options: resultsByType.search_phonebook.map((pb) => {
|
||||
// return {
|
||||
// key: pb.id,
|
||||
// value: `${pb.firstname || ""} ${pb.lastname || ""} ${
|
||||
// pb.company || ""
|
||||
// }`,
|
||||
// label: (
|
||||
// <Link to={`/manage/phonebook?phonebookentry=${pb.id}`}>
|
||||
// <Space size="small" split={<Divider type="vertical" />}>
|
||||
// <span>{`${pb.firstname || ""} ${pb.lastname || ""} ${
|
||||
// pb.company || ""
|
||||
// }`}</span>
|
||||
// <PhoneNumberFormatter>{pb.phone1}</PhoneNumberFormatter>
|
||||
// <span>{pb.email}</span>
|
||||
// </Space>
|
||||
// </Link>
|
||||
// ),
|
||||
// };
|
||||
// }),
|
||||
// },
|
||||
]);
|
||||
} catch (error) {
|
||||
console.log("Error while fetching search results", error);
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
}
|
||||
};
|
||||
const debouncedExecuteSearch = _.debounce(executeSearch, 750);
|
||||
|
||||
const handleSearch = (value) => {
|
||||
debouncedExecuteSearch(value);
|
||||
};
|
||||
|
||||
const renderTitle = (title) => {
|
||||
return <span>{title}</span>;
|
||||
};
|
||||
|
||||
return (
|
||||
<AutoComplete
|
||||
options={data}
|
||||
onSearch={handleSearch}
|
||||
defaultActiveFirstOption
|
||||
onSelect={(val, opt) => {
|
||||
history.push(opt.label.props.to);
|
||||
}}
|
||||
onClear={() => setData([])}
|
||||
>
|
||||
<Input.Search
|
||||
size="large"
|
||||
placeholder={t("general.labels.globalsearch")}
|
||||
enterButton
|
||||
allowClear
|
||||
loading={loading}
|
||||
/>
|
||||
</AutoComplete>
|
||||
);
|
||||
}
|
||||
@@ -8,7 +8,7 @@ import { GLOBAL_SEARCH_QUERY } from "../../graphql/search.queries";
|
||||
import PhoneNumberFormatter from "../../utils/PhoneFormatter";
|
||||
import AlertComponent from "../alert/alert.component";
|
||||
import OwnerNameDisplay, {
|
||||
OwnerNameDisplayFunction
|
||||
OwnerNameDisplayFunction,
|
||||
} from "../owner-name-display/owner-name-display.component";
|
||||
import VehicleVinDisplay from "../vehicle-vin-display/vehicle-vin-display.component";
|
||||
export default function GlobalSearch() {
|
||||
@@ -18,11 +18,18 @@ export default function GlobalSearch() {
|
||||
useLazyQuery(GLOBAL_SEARCH_QUERY);
|
||||
|
||||
const executeSearch = (v) => {
|
||||
if (v && v.variables.search && v.variables.search !== "") callSearch(v);
|
||||
if (
|
||||
v &&
|
||||
v.variables.search &&
|
||||
v.variables.search !== "" &&
|
||||
v.variables.search.length >= 3
|
||||
)
|
||||
callSearch(v);
|
||||
};
|
||||
const debouncedExecuteSearch = _.debounce(executeSearch, 750);
|
||||
|
||||
const handleSearch = (value) => {
|
||||
console.log("Handle Search");
|
||||
debouncedExecuteSearch({ variables: { search: value } });
|
||||
};
|
||||
|
||||
@@ -37,7 +44,7 @@ export default function GlobalSearch() {
|
||||
options: data.search_jobs.map((job) => {
|
||||
return {
|
||||
key: job.id,
|
||||
value: job.ro_number,
|
||||
value: job.ro_number || "N/A",
|
||||
label: (
|
||||
<Link to={`/manage/jobs/${job.id}`}>
|
||||
<Space size="small" split={<Divider type="vertical" />}>
|
||||
|
||||
@@ -3,9 +3,11 @@ import {
|
||||
Button,
|
||||
Divider,
|
||||
Dropdown,
|
||||
Form,
|
||||
Menu,
|
||||
notification,
|
||||
Popover,
|
||||
Select,
|
||||
Space,
|
||||
} from "antd";
|
||||
import parsePhoneNumber from "libphonenumber-js";
|
||||
@@ -59,7 +61,10 @@ export function ScheduleEventComponent({
|
||||
|
||||
const blockContent = (
|
||||
<div>
|
||||
<Button onClick={() => handleCancel(event.id)} disabled={event.arrived}>
|
||||
<Button
|
||||
onClick={() => handleCancel({ id: event.id })}
|
||||
disabled={event.arrived}
|
||||
>
|
||||
{t("appointments.actions.cancel")}
|
||||
</Button>
|
||||
</div>
|
||||
@@ -203,10 +208,46 @@ export function ScheduleEventComponent({
|
||||
<Button>{t("appointments.actions.sendreminder")}</Button>
|
||||
</Dropdown>
|
||||
) : null}
|
||||
|
||||
<Button onClick={() => handleCancel(event.id)} disabled={event.arrived}>
|
||||
{t("appointments.actions.cancel")}
|
||||
</Button>
|
||||
<Popover
|
||||
trigger="click"
|
||||
disabled={event.arrived}
|
||||
content={
|
||||
<Form
|
||||
layout="vertical"
|
||||
onFinish={({ lost_sale_reason }) => {
|
||||
handleCancel({ id: event.id, lost_sale_reason });
|
||||
}}
|
||||
>
|
||||
<Form.Item
|
||||
name="lost_sale_reason"
|
||||
label={t("jobs.fields.lost_sale_reason")}
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
//message: t("general.validation.required"),
|
||||
},
|
||||
]}
|
||||
>
|
||||
<Select
|
||||
options={bodyshop.md_lost_sale_reasons.map((lsr) => ({
|
||||
label: lsr,
|
||||
value: lsr,
|
||||
}))}
|
||||
/>
|
||||
</Form.Item>
|
||||
<Button htmlType="submit">
|
||||
{t("appointments.actions.cancel")}
|
||||
</Button>
|
||||
</Form>
|
||||
}
|
||||
>
|
||||
<Button
|
||||
// onClick={() => handleCancel(event.id)}
|
||||
disabled={event.arrived}
|
||||
>
|
||||
{t("appointments.actions.cancel")}
|
||||
</Button>
|
||||
</Popover>
|
||||
{event.isintake ? (
|
||||
<Button
|
||||
disabled={event.arrived}
|
||||
@@ -249,7 +290,7 @@ export function ScheduleEventComponent({
|
||||
const RegularEvent = event.isintake ? (
|
||||
<Space
|
||||
wrap
|
||||
size='small'
|
||||
size="small"
|
||||
style={{
|
||||
backgroundColor:
|
||||
event.color && event.color.hex ? event.color.hex : event.color,
|
||||
|
||||
@@ -11,7 +11,7 @@ export default function ScheduleEventContainer({ bodyshop, event, refetch }) {
|
||||
const { t } = useTranslation();
|
||||
const [cancelAppointment] = useMutation(CANCEL_APPOINTMENT_BY_ID);
|
||||
const [updateJob] = useMutation(UPDATE_JOB);
|
||||
const handleCancel = async (id) => {
|
||||
const handleCancel = async ({ id, lost_sale_reason }) => {
|
||||
logImEXEvent("schedule_cancel_appt");
|
||||
|
||||
const cancelAppt = await cancelAppointment({
|
||||
@@ -38,7 +38,8 @@ export default function ScheduleEventContainer({ bodyshop, event, refetch }) {
|
||||
job: {
|
||||
date_scheduled: null,
|
||||
scheduled_in: null,
|
||||
scheduled_completion:null,
|
||||
scheduled_completion: null,
|
||||
lost_sale_reason,
|
||||
status: bodyshop.md_ro_statuses.default_imported,
|
||||
},
|
||||
},
|
||||
|
||||
@@ -103,7 +103,12 @@ export function JobLinesComponent({
|
||||
fixed: "left",
|
||||
key: "line_desc",
|
||||
sorter: (a, b) => alphaSort(a.line_desc, b.line_desc),
|
||||
onCell: (record) => ({ className: record.manual_line && "job-line-manual" }),
|
||||
onCell: (record) => ({
|
||||
className: record.manual_line && "job-line-manual",
|
||||
style: {
|
||||
...(record.critical ? { boxShadow: " -.5em 0 0 #FFC107" } : {}),
|
||||
},
|
||||
}),
|
||||
sortOrder:
|
||||
state.sortedInfo.columnKey === "line_desc" && state.sortedInfo.order,
|
||||
ellipsis: true,
|
||||
@@ -343,7 +348,7 @@ export function JobLinesComponent({
|
||||
onClick={() => {
|
||||
setJobLineEditContext({
|
||||
actions: { refetch: refetch, submit: form && form.submit },
|
||||
context: record,
|
||||
context: { ...record, jobid: job.id },
|
||||
});
|
||||
}}
|
||||
>
|
||||
|
||||
@@ -14,8 +14,12 @@ import UndefinedToNull from "../../utils/undefinedtonull";
|
||||
import JobLinesUpdsertModal from "./job-lines-upsert-modal.component";
|
||||
import Axios from "axios";
|
||||
import Dinero from "dinero.js";
|
||||
import CriticalPartsScan from "../../utils/criticalPartsScan";
|
||||
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||
import { useTreatments } from "@splitsoftware/splitio-react";
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
jobLineEditModal: selectJobLineEditModal,
|
||||
bodyshop: selectBodyshop,
|
||||
});
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
toggleModalVisible: () => dispatch(toggleModalVisible("jobLineEdit")),
|
||||
@@ -24,7 +28,13 @@ const mapDispatchToProps = (dispatch) => ({
|
||||
function JobLinesUpsertModalContainer({
|
||||
jobLineEditModal,
|
||||
toggleModalVisible,
|
||||
bodyshop,
|
||||
}) {
|
||||
const { CriticalPartsScanning } = useTreatments(
|
||||
["CriticalPartsScanning"],
|
||||
{},
|
||||
bodyshop.imexshopid
|
||||
);
|
||||
const { t } = useTranslation();
|
||||
const [insertJobLine] = useMutation(INSERT_NEW_JOB_LINE);
|
||||
const [updateJobLine] = useMutation(UPDATE_JOB_LINE);
|
||||
@@ -109,6 +119,9 @@ function JobLinesUpsertModalContainer({
|
||||
}
|
||||
toggleModalVisible();
|
||||
}
|
||||
if (CriticalPartsScanning.treatment === "on") {
|
||||
CriticalPartsScan(jobLineEditModal.context.jobid);
|
||||
}
|
||||
setLoading(false);
|
||||
};
|
||||
|
||||
|
||||
@@ -3,8 +3,9 @@ import {
|
||||
useApolloClient,
|
||||
useLazyQuery,
|
||||
useMutation,
|
||||
useQuery,
|
||||
useQuery
|
||||
} from "@apollo/client";
|
||||
import { useTreatments } from "@splitsoftware/splitio-react";
|
||||
import { Col, notification, Row } from "antd";
|
||||
import Axios from "axios";
|
||||
import Dinero from "dinero.js";
|
||||
@@ -19,7 +20,7 @@ import { logImEXEvent } from "../../firebase/firebase.utils";
|
||||
import {
|
||||
DELETE_AVAILABLE_JOB,
|
||||
QUERY_AVAILABLE_JOBS,
|
||||
QUERY_AVAILABLE_NEW_JOBS_EST_DATA_BY_PK,
|
||||
QUERY_AVAILABLE_NEW_JOBS_EST_DATA_BY_PK
|
||||
} from "../../graphql/available-jobs.queries";
|
||||
import { INSERT_NEW_JOB, UPDATE_JOB } from "../../graphql/jobs.queries";
|
||||
import { INSERT_NEW_NOTE } from "../../graphql/notes.queries";
|
||||
@@ -27,10 +28,11 @@ import { SEARCH_VEHICLE_BY_VIN } from "../../graphql/vehicles.queries";
|
||||
import { insertAuditTrail } from "../../redux/application/application.actions";
|
||||
import {
|
||||
selectBodyshop,
|
||||
selectCurrentUser,
|
||||
selectCurrentUser
|
||||
} from "../../redux/user/user.selectors";
|
||||
import confirmDialog from "../../utils/asyncConfirm";
|
||||
import AuditTrailMapping from "../../utils/AuditTrailMappings";
|
||||
import CriticalPartsScan from "../../utils/criticalPartsScan";
|
||||
import AlertComponent from "../alert/alert.component";
|
||||
import JobsAvailableScan from "../jobs-available-scan/jobs-available-scan.component";
|
||||
import JobsFindModalContainer from "../jobs-find-modal/jobs-find-modal.container";
|
||||
@@ -53,6 +55,11 @@ export function JobsAvailableContainer({
|
||||
currentUser,
|
||||
insertAuditTrail,
|
||||
}) {
|
||||
const { CriticalPartsScanning } = useTreatments(
|
||||
["CriticalPartsScanning"],
|
||||
{},
|
||||
bodyshop.imexshopid
|
||||
);
|
||||
const { loading, error, data, refetch } = useQuery(QUERY_AVAILABLE_JOBS, {
|
||||
fetchPolicy: "network-only",
|
||||
nextFetchPolicy: "network-only",
|
||||
@@ -155,6 +162,9 @@ export function JobsAvailableContainer({
|
||||
},
|
||||
})
|
||||
.then((r) => {
|
||||
if (CriticalPartsScanning.treatment === "on") {
|
||||
CriticalPartsScan(r.data.insert_jobs.returning[0].id);
|
||||
}
|
||||
notification["success"]({
|
||||
message: t("jobs.successes.created"),
|
||||
onClick: () => {
|
||||
@@ -241,7 +251,9 @@ export function JobsAvailableContainer({
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
if (CriticalPartsScanning.treatment === "on") {
|
||||
CriticalPartsScan(updateResult.data.update_jobs.returning[0].id);
|
||||
}
|
||||
if (updateResult.errors) {
|
||||
//error while inserting
|
||||
notification["error"]({
|
||||
|
||||
@@ -43,7 +43,7 @@ export function JobsConvertButton({
|
||||
const { t } = useTranslation();
|
||||
const [form] = Form.useForm();
|
||||
|
||||
const handleConvert = async ({ employee_csr, ...values }) => {
|
||||
const handleConvert = async ({ employee_csr, category, ...values }) => {
|
||||
if (parentFormIsFieldsTouched()) {
|
||||
alert(t("jobs.labels.savebeforeconversion"));
|
||||
return;
|
||||
@@ -55,6 +55,7 @@ export function JobsConvertButton({
|
||||
job: {
|
||||
converted: true,
|
||||
...(bodyshop.enforce_conversion_csr ? { employee_csr } : {}),
|
||||
...(bodyshop.enforce_conversion_category ? { category } : {}),
|
||||
...values,
|
||||
},
|
||||
},
|
||||
@@ -94,6 +95,7 @@ export function JobsConvertButton({
|
||||
driveable: true,
|
||||
towin: false,
|
||||
employee_csr: job.employee_csr,
|
||||
category: job.category,
|
||||
}}
|
||||
>
|
||||
<Form.Item
|
||||
@@ -107,8 +109,8 @@ export function JobsConvertButton({
|
||||
]}
|
||||
>
|
||||
<Select>
|
||||
{bodyshop.md_ins_cos.map((s) => (
|
||||
<Select.Option key={s.name} value={s.name}>
|
||||
{bodyshop.md_ins_cos.map((s, i) => (
|
||||
<Select.Option key={i} value={s.name}>
|
||||
{s.name}
|
||||
</Select.Option>
|
||||
))}
|
||||
@@ -197,6 +199,26 @@ export function JobsConvertButton({
|
||||
</Select>
|
||||
</Form.Item>
|
||||
)}
|
||||
{bodyshop.enforce_conversion_category && (
|
||||
<Form.Item
|
||||
name={"category"}
|
||||
label={t("jobs.fields.category")}
|
||||
rules={[
|
||||
{
|
||||
required: bodyshop.enforce_conversion_category,
|
||||
//message: t("general.validation.required"),
|
||||
},
|
||||
]}
|
||||
>
|
||||
<Select allowClear>
|
||||
{bodyshop.md_categories.map((s) => (
|
||||
<Select.Option key={s} value={s}>
|
||||
{s}
|
||||
</Select.Option>
|
||||
))}
|
||||
</Select>
|
||||
</Form.Item>
|
||||
)}
|
||||
<Form.Item
|
||||
label={t("jobs.fields.ca_gst_registrant")}
|
||||
name="ca_gst_registrant"
|
||||
|
||||
@@ -289,6 +289,12 @@ export function JobsDetailGeneral({ bodyshop, jobRO, job, form }) {
|
||||
>
|
||||
<Input disabled={jobRO} />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("jobs.fields.lost_sale_reason")}
|
||||
name="lost_sale_reason"
|
||||
>
|
||||
<Input disabled={jobRO} allowClear />
|
||||
</Form.Item>
|
||||
</FormRow>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -1,6 +1,15 @@
|
||||
import { DownCircleFilled } from "@ant-design/icons";
|
||||
import { useApolloClient, useMutation } from "@apollo/client";
|
||||
import { Button, Dropdown, Menu, notification, Popconfirm } from "antd";
|
||||
import {
|
||||
Button,
|
||||
Dropdown,
|
||||
Form,
|
||||
Menu,
|
||||
notification,
|
||||
Popconfirm,
|
||||
Popover,
|
||||
Select,
|
||||
} from "antd";
|
||||
import React, { useMemo } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { connect } from "react-redux";
|
||||
@@ -127,35 +136,63 @@ export function JobsDetailHeaderActions({
|
||||
<Menu.Item
|
||||
disabled={job.status !== bodyshop.md_ro_statuses.default_scheduled}
|
||||
>
|
||||
<Popconfirm
|
||||
title={t("general.labels.areyousure")}
|
||||
okText="Yes"
|
||||
cancelText="No"
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
<Popover
|
||||
trigger="click"
|
||||
disabled={job.status !== bodyshop.md_ro_statuses.default_scheduled}
|
||||
onConfirm={async () => {
|
||||
const jobUpdate = await cancelAllAppointments({
|
||||
variables: {
|
||||
jobid: job.id,
|
||||
job: {
|
||||
date_scheduled: null,
|
||||
scheduled_in: null,
|
||||
scheduled_completion: null,
|
||||
status: bodyshop.md_ro_statuses.default_imported,
|
||||
},
|
||||
},
|
||||
});
|
||||
if (!jobUpdate.errors) {
|
||||
notification["success"]({
|
||||
message: t("appointments.successes.canceled"),
|
||||
});
|
||||
return;
|
||||
}
|
||||
}}
|
||||
getPopupContainer={(trigger) => trigger.parentNode}
|
||||
content={
|
||||
<Form
|
||||
layout="vertical"
|
||||
onFinish={async ({ lost_sale_reason }) => {
|
||||
const jobUpdate = await cancelAllAppointments({
|
||||
variables: {
|
||||
jobid: job.id,
|
||||
job: {
|
||||
date_scheduled: null,
|
||||
scheduled_in: null,
|
||||
scheduled_completion: null,
|
||||
lost_sale_reason,
|
||||
status: bodyshop.md_ro_statuses.default_imported,
|
||||
},
|
||||
},
|
||||
});
|
||||
if (!jobUpdate.errors) {
|
||||
notification["success"]({
|
||||
message: t("appointments.successes.canceled"),
|
||||
});
|
||||
return;
|
||||
}
|
||||
}}
|
||||
>
|
||||
<Form.Item
|
||||
name="lost_sale_reason"
|
||||
label={t("jobs.fields.lost_sale_reason")}
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
//message: t("general.validation.required"),
|
||||
},
|
||||
]}
|
||||
>
|
||||
<Select
|
||||
options={bodyshop.md_lost_sale_reasons.map((lsr) => ({
|
||||
label: lsr,
|
||||
value: lsr,
|
||||
}))}
|
||||
/>
|
||||
</Form.Item>
|
||||
<Button
|
||||
htmlType="submit"
|
||||
disabled={
|
||||
job.status !== bodyshop.md_ro_statuses.default_scheduled
|
||||
}
|
||||
>
|
||||
{t("appointments.actions.cancel")}
|
||||
</Button>
|
||||
</Form>
|
||||
}
|
||||
>
|
||||
{t("menus.jobsactions.cancelallappointments")}
|
||||
</Popconfirm>
|
||||
</Popover>
|
||||
</Menu.Item>
|
||||
<Menu.Item
|
||||
disabled={
|
||||
|
||||
@@ -82,7 +82,7 @@ export function JobsDetailRates({ jobRO, form, job, bodyshop }) {
|
||||
>
|
||||
<CurrencyInput disabled={jobRO || bodyshop.cdk_dealerid} />
|
||||
</Form.Item>
|
||||
<Space align="end">
|
||||
<Space align="center">
|
||||
<Form.Item label={t("jobs.fields.ca_bc_pvrt")} name="ca_bc_pvrt">
|
||||
<CurrencyInput disabled={jobRO} min={0} />
|
||||
</Form.Item>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { EditFilled, FileExcelFilled, SyncOutlined } from "@ant-design/icons";
|
||||
import { Button, Card, Col, Row, Space } from "antd";
|
||||
import React, { useEffect, useState } from "react";
|
||||
import Gallery from "react-grid-gallery";
|
||||
import { Gallery } from "react-grid-gallery";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import DocumentsUploadComponent from "../documents-upload/documents-upload.component";
|
||||
import { DetermineFileType } from "../documents-upload/documents-upload.utility";
|
||||
@@ -11,6 +11,9 @@ import JobsDocumentsGalleryReassign from "./jobs-document-gallery.reassign.compo
|
||||
import JobsDocumentsDeleteButton from "./jobs-documents-gallery.delete.component";
|
||||
import JobsDocumentsGallerySelectAllComponent from "./jobs-documents-gallery.selectall.component";
|
||||
|
||||
import Lightbox from "react-image-lightbox";
|
||||
import "react-image-lightbox/style.css";
|
||||
|
||||
function JobsDocumentsComponent({
|
||||
data,
|
||||
jobId,
|
||||
@@ -23,11 +26,7 @@ function JobsDocumentsComponent({
|
||||
}) {
|
||||
const [galleryImages, setgalleryImages] = useState({ images: [], other: [] });
|
||||
const { t } = useTranslation();
|
||||
const [index, setIndex] = useState(0);
|
||||
|
||||
const onCurrentImageChange = (index) => {
|
||||
setIndex(index);
|
||||
};
|
||||
const [modalState, setModalState] = useState({ open: false, index: 0 });
|
||||
|
||||
useEffect(() => {
|
||||
let documents = data.reduce(
|
||||
@@ -35,14 +34,16 @@ function JobsDocumentsComponent({
|
||||
const fileType = DetermineFileType(value.type);
|
||||
if (value.type.startsWith("image")) {
|
||||
acc.images.push({
|
||||
src: GenerateSrcUrl(value),
|
||||
thumbnail: GenerateThumbUrl(value),
|
||||
thumbnailHeight: 225,
|
||||
thumbnailWidth: 225,
|
||||
// src: GenerateSrcUrl(value),
|
||||
src: GenerateThumbUrl(value),
|
||||
// src: GenerateSrcUrl(value),
|
||||
// thumbnail: GenerateThumbUrl(value),
|
||||
fullsize: GenerateSrcUrl(value),
|
||||
height: 225,
|
||||
width: 225,
|
||||
isSelected: false,
|
||||
key: value.key,
|
||||
extension: value.extension,
|
||||
|
||||
id: value.id,
|
||||
type: value.type,
|
||||
size: value.size,
|
||||
@@ -62,7 +63,7 @@ function JobsDocumentsComponent({
|
||||
const fileName = value.key.split("/").pop();
|
||||
acc.other.push({
|
||||
source: GenerateSrcUrl(value),
|
||||
src: "",
|
||||
src: thumb,
|
||||
thumbnail: thumb,
|
||||
tags: [
|
||||
{
|
||||
@@ -85,10 +86,9 @@ function JobsDocumentsComponent({
|
||||
]
|
||||
: []),
|
||||
],
|
||||
thumbnailHeight: 225,
|
||||
thumbnailWidth: 225,
|
||||
height: 225,
|
||||
width: 225,
|
||||
isSelected: false,
|
||||
|
||||
extension: value.extension,
|
||||
key: value.key,
|
||||
id: value.id,
|
||||
@@ -148,35 +148,15 @@ function JobsDocumentsComponent({
|
||||
<Card title={t("jobs.labels.documents-images")}>
|
||||
<Gallery
|
||||
images={galleryImages.images}
|
||||
backdropClosesModal={true}
|
||||
currentImageWillChange={onCurrentImageChange}
|
||||
customControls={[
|
||||
<Button
|
||||
key="edit-button"
|
||||
style={{
|
||||
float: "right",
|
||||
zIndex: "5",
|
||||
}}
|
||||
onClick={() => {
|
||||
const newWindow = window.open(
|
||||
`${window.location.protocol}//${window.location.host}/edit?documentId=${galleryImages.images[index].id}`,
|
||||
"_blank",
|
||||
"noopener,noreferrer"
|
||||
);
|
||||
if (newWindow) newWindow.opener = null;
|
||||
}}
|
||||
>
|
||||
<EditFilled />
|
||||
</Button>,
|
||||
]}
|
||||
onClickImage={(props) => {
|
||||
window.open(
|
||||
props.target.src,
|
||||
"_blank",
|
||||
"toolbar=0,location=0,menubar=0"
|
||||
);
|
||||
onClick={(index, item) => {
|
||||
setModalState({ open: true, index: index });
|
||||
// window.open(
|
||||
// item.fullsize,
|
||||
// "_blank",
|
||||
// "toolbar=0,location=0,menubar=0"
|
||||
// );
|
||||
}}
|
||||
onSelectImage={(index, image) => {
|
||||
onSelect={(index, image) => {
|
||||
setgalleryImages({
|
||||
...galleryImages,
|
||||
images: galleryImages.images.map((g, idx) =>
|
||||
@@ -191,8 +171,6 @@ function JobsDocumentsComponent({
|
||||
<Card title={t("jobs.labels.documents-other")}>
|
||||
<Gallery
|
||||
images={galleryImages.other}
|
||||
backdropClosesModal={true}
|
||||
enableLightbox={false}
|
||||
thumbnailStyle={() => {
|
||||
return {
|
||||
backgroundImage: <FileExcelFilled />,
|
||||
@@ -201,14 +179,14 @@ function JobsDocumentsComponent({
|
||||
cursor: "pointer",
|
||||
};
|
||||
}}
|
||||
onClickThumbnail={(index) => {
|
||||
onClick={(index) => {
|
||||
window.open(
|
||||
galleryImages.other[index].source,
|
||||
"_blank",
|
||||
"toolbar=0,location=0,menubar=0"
|
||||
);
|
||||
}}
|
||||
onSelectImage={(index) => {
|
||||
onSelect={(index) => {
|
||||
setgalleryImages({
|
||||
...galleryImages,
|
||||
other: galleryImages.other.map((g, idx) =>
|
||||
@@ -219,6 +197,53 @@ function JobsDocumentsComponent({
|
||||
/>
|
||||
</Card>
|
||||
</Col>
|
||||
{modalState.open && (
|
||||
<Lightbox
|
||||
toolbarButtons={[
|
||||
<EditFilled
|
||||
onClick={() => {
|
||||
const newWindow = window.open(
|
||||
`${window.location.protocol}//${
|
||||
window.location.host
|
||||
}/edit?documentId=${
|
||||
galleryImages.images[modalState.index].id
|
||||
}`,
|
||||
"_blank",
|
||||
"noopener,noreferrer"
|
||||
);
|
||||
if (newWindow) newWindow.opener = null;
|
||||
}}
|
||||
/>,
|
||||
]}
|
||||
mainSrc={galleryImages.images[modalState.index].fullsize}
|
||||
nextSrc={
|
||||
galleryImages.images[
|
||||
(modalState.index + 1) % galleryImages.images.length
|
||||
].fullsize
|
||||
}
|
||||
prevSrc={
|
||||
galleryImages.images[
|
||||
(modalState.index + galleryImages.images.length - 1) %
|
||||
galleryImages.images.length
|
||||
].fullsize
|
||||
}
|
||||
onCloseRequest={() => setModalState({ open: false, index: 0 })}
|
||||
onMovePrevRequest={() =>
|
||||
setModalState({
|
||||
...modalState,
|
||||
index:
|
||||
(modalState.index + galleryImages.images.length - 1) %
|
||||
galleryImages.images.length,
|
||||
})
|
||||
}
|
||||
onMoveNextRequest={() =>
|
||||
setModalState({
|
||||
...modalState,
|
||||
index: (modalState.index + 1) % galleryImages.images.length,
|
||||
})
|
||||
}
|
||||
/>
|
||||
)}
|
||||
</Row>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import React, { useEffect } from "react";
|
||||
import Gallery from "react-grid-gallery";
|
||||
import { Gallery } from "react-grid-gallery";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { GenerateSrcUrl, GenerateThumbUrl } from "./job-documents.utility";
|
||||
import { GenerateThumbUrl } from "./job-documents.utility";
|
||||
|
||||
function JobsDocumentGalleryExternal({
|
||||
data,
|
||||
@@ -15,8 +15,8 @@ function JobsDocumentGalleryExternal({
|
||||
let documents = data.reduce((acc, value) => {
|
||||
if (value.type.startsWith("image")) {
|
||||
acc.push({
|
||||
src: GenerateSrcUrl(value),
|
||||
thumbnail: GenerateThumbUrl(value),
|
||||
//src: GenerateSrcUrl(value),
|
||||
src: GenerateThumbUrl(value),
|
||||
thumbnailHeight: 225,
|
||||
thumbnailWidth: 225,
|
||||
isSelected: false,
|
||||
@@ -39,7 +39,7 @@ function JobsDocumentGalleryExternal({
|
||||
<Gallery
|
||||
images={galleryImages}
|
||||
backdropClosesModal={true}
|
||||
onSelectImage={(index, image) => {
|
||||
onSelect={(index, image) => {
|
||||
setgalleryImages(
|
||||
galleryImages.map((g, idx) =>
|
||||
index === idx ? { ...g, isSelected: !g.isSelected } : g
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { SyncOutlined, FileExcelFilled } from "@ant-design/icons";
|
||||
import { Alert, Button, Card, Space } from "antd";
|
||||
import React, { useEffect } from "react";
|
||||
import Gallery from "react-grid-gallery";
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { Gallery } from "react-grid-gallery";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { connect } from "react-redux";
|
||||
import { createStructuredSelector } from "reselect";
|
||||
@@ -19,6 +19,9 @@ import JobsLocalGalleryDownloadButton from "./jobs-documents-local-gallery.downl
|
||||
import JobsDocumentsLocalGalleryReassign from "./jobs-documents-local-gallery.reassign.component";
|
||||
import JobsDocumentsLocalGallerySelectAllComponent from "./jobs-documents-local-gallery.selectall.component";
|
||||
|
||||
import Lightbox from "react-image-lightbox";
|
||||
import "react-image-lightbox/style.css";
|
||||
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
bodyshop: selectBodyshop,
|
||||
allMedia: selectAllMedia,
|
||||
@@ -49,6 +52,7 @@ export function JobsDocumentsLocalGallery({
|
||||
vendorid,
|
||||
}) {
|
||||
const { t } = useTranslation();
|
||||
const [modalState, setModalState] = useState({ open: false, index: 0 });
|
||||
useEffect(() => {
|
||||
if (job) {
|
||||
if (invoice_number) {
|
||||
@@ -70,12 +74,20 @@ export function JobsDocumentsLocalGallery({
|
||||
) {
|
||||
acc.images.push({
|
||||
...val,
|
||||
fullsize: val.src,
|
||||
src: val.thumbnail,
|
||||
height: val.thumbnailHeight,
|
||||
width: val.thumbnailWidth,
|
||||
...(val.optimized && { src: val.optimized, fullsize: val.src }),
|
||||
});
|
||||
if (val.optimized) optimized = true;
|
||||
} else {
|
||||
acc.other.push({
|
||||
...val,
|
||||
fullsize: val.src,
|
||||
src: val.thumbnail,
|
||||
height: val.thumbnailHeight,
|
||||
width: val.thumbnailWidth,
|
||||
tags: [{ value: val.filename, title: val.filename }],
|
||||
});
|
||||
}
|
||||
@@ -120,8 +132,7 @@ export function JobsDocumentsLocalGallery({
|
||||
<Card title={t("jobs.labels.documents-images")}>
|
||||
<Gallery
|
||||
images={jobMedia.images}
|
||||
backdropClosesModal={true}
|
||||
onSelectImage={(index, image) => {
|
||||
onSelect={(index, image) => {
|
||||
toggleMediaSelected({ jobid: job.id, filename: image.filename });
|
||||
}}
|
||||
{...(optimized && {
|
||||
@@ -133,24 +144,23 @@ export function JobsDocumentsLocalGallery({
|
||||
/>,
|
||||
],
|
||||
})}
|
||||
onClickImage={(props) => {
|
||||
const media = allMedia[job.id].find(
|
||||
(m) => m.optimized === props.target.src
|
||||
);
|
||||
onClick={(index) => {
|
||||
setModalState({ open: true, index: index });
|
||||
// const media = allMedia[job.id].find(
|
||||
// (m) => m.optimized === item.src
|
||||
// );
|
||||
|
||||
window.open(
|
||||
media ? media.src : props.target.src,
|
||||
"_blank",
|
||||
"toolbar=0,location=0,menubar=0"
|
||||
);
|
||||
// window.open(
|
||||
// media ? media.fullsize : item.fullsize,
|
||||
// "_blank",
|
||||
// "toolbar=0,location=0,menubar=0"
|
||||
// );
|
||||
}}
|
||||
/>
|
||||
</Card>
|
||||
<Card title={t("jobs.labels.documents-other")}>
|
||||
<Gallery
|
||||
images={jobMedia.other}
|
||||
backdropClosesModal={true}
|
||||
enableLightbox={false}
|
||||
thumbnailStyle={() => {
|
||||
return {
|
||||
backgroundImage: <FileExcelFilled />,
|
||||
@@ -159,18 +169,48 @@ export function JobsDocumentsLocalGallery({
|
||||
cursor: "pointer",
|
||||
};
|
||||
}}
|
||||
onClickThumbnail={(index) => {
|
||||
onClick={(index) => {
|
||||
window.open(
|
||||
jobMedia.other[index].src,
|
||||
jobMedia.other[index].fullsize,
|
||||
"_blank",
|
||||
"toolbar=0,location=0,menubar=0"
|
||||
);
|
||||
}}
|
||||
onSelectImage={(index, image) => {
|
||||
onSelect={(index, image) => {
|
||||
toggleMediaSelected({ jobid: job.id, filename: image.filename });
|
||||
}}
|
||||
/>
|
||||
</Card>
|
||||
{modalState.open && (
|
||||
<Lightbox
|
||||
mainSrc={jobMedia.images[modalState.index].fullsize}
|
||||
nextSrc={
|
||||
jobMedia.images[(modalState.index + 1) % jobMedia.images.length]
|
||||
.fullsize
|
||||
}
|
||||
prevSrc={
|
||||
jobMedia.images[
|
||||
(modalState.index + jobMedia.images.length - 1) %
|
||||
jobMedia.images.length
|
||||
].fullsize
|
||||
}
|
||||
onCloseRequest={() => setModalState({ open: false, index: 0 })}
|
||||
onMovePrevRequest={() =>
|
||||
setModalState({
|
||||
...modalState,
|
||||
index:
|
||||
(modalState.index + jobMedia.images.length - 1) %
|
||||
jobMedia.images.length,
|
||||
})
|
||||
}
|
||||
onMoveNextRequest={() =>
|
||||
setModalState({
|
||||
...modalState,
|
||||
index: (modalState.index + 1) % jobMedia.images.length,
|
||||
})
|
||||
}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import React, { useEffect } from "react";
|
||||
import Gallery from "react-grid-gallery";
|
||||
import { Gallery } from "react-grid-gallery";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { connect } from "react-redux";
|
||||
import { createStructuredSelector } from "reselect";
|
||||
@@ -38,7 +38,7 @@ function JobDocumentsLocalGalleryExternal({
|
||||
const { t } = useTranslation();
|
||||
|
||||
useEffect(() => {
|
||||
if ( jobId) {
|
||||
if (jobId) {
|
||||
getJobMedia(jobId);
|
||||
}
|
||||
}, [jobId, getJobMedia]);
|
||||
@@ -52,11 +52,15 @@ function JobDocumentsLocalGalleryExternal({
|
||||
val.type.mime &&
|
||||
val.type.mime.startsWith("image")
|
||||
) {
|
||||
acc.push(val);
|
||||
acc.push({ ...val, src: val.thumbnail });
|
||||
}
|
||||
return acc;
|
||||
}, [])
|
||||
: [];
|
||||
console.log(
|
||||
"🚀 ~ file: jobs-documents-local-gallery.external.component.jsx:48 ~ useEffect ~ documents:",
|
||||
documents
|
||||
);
|
||||
|
||||
setgalleryImages(documents);
|
||||
}, [allMedia, jobId, setgalleryImages, t]);
|
||||
@@ -65,8 +69,7 @@ function JobDocumentsLocalGalleryExternal({
|
||||
<div className="clearfix">
|
||||
<Gallery
|
||||
images={galleryImages}
|
||||
backdropClosesModal={true}
|
||||
onSelectImage={(index, image) => {
|
||||
onSelect={(index, image) => {
|
||||
setgalleryImages(
|
||||
galleryImages.map((g, idx) =>
|
||||
index === idx ? { ...g, isSelected: !g.isSelected } : g
|
||||
|
||||
@@ -260,6 +260,19 @@ export function JobsList({ bodyshop }) {
|
||||
dataIndex: "ins_co_nm",
|
||||
key: "ins_co_nm",
|
||||
ellipsis: true,
|
||||
filters:
|
||||
(jobs &&
|
||||
jobs
|
||||
.map((j) => j.ins_co_nm)
|
||||
.filter(onlyUnique)
|
||||
.map((s) => {
|
||||
return {
|
||||
text: s,
|
||||
value: [s],
|
||||
};
|
||||
})) ||
|
||||
[],
|
||||
onFilter: (value, record) => value.includes(record.ins_co_nm),
|
||||
responsive: ["md"],
|
||||
},
|
||||
{
|
||||
|
||||
@@ -272,6 +272,19 @@ export function JobsReadyList({ bodyshop }) {
|
||||
dataIndex: "ins_co_nm",
|
||||
key: "ins_co_nm",
|
||||
ellipsis: true,
|
||||
filters:
|
||||
(jobs &&
|
||||
jobs
|
||||
.map((j) => j.ins_co_nm)
|
||||
.filter(onlyUnique)
|
||||
.map((s) => {
|
||||
return {
|
||||
text: s,
|
||||
value: [s],
|
||||
};
|
||||
})) ||
|
||||
[],
|
||||
onFilter: (value, record) => value.includes(record.ins_co_nm),
|
||||
responsive: ["md"],
|
||||
},
|
||||
{
|
||||
|
||||
@@ -6,10 +6,6 @@ export const CalculateAllocationsTotals = (
|
||||
timetickets,
|
||||
adjustments = []
|
||||
) => {
|
||||
console.log(
|
||||
"🚀 ~ file: labor-allocations-table.utility.js ~ line 9 ~ adjustments",
|
||||
adjustments
|
||||
);
|
||||
const responsibilitycenters = bodyshop.md_responsibility_centers;
|
||||
const jobCodes = joblines.map((item) => item.mod_lbr_ty);
|
||||
//.filter((value, index, self) => self.indexOf(value) === index && !!value);
|
||||
|
||||
@@ -34,7 +34,7 @@ export function NoteUpsertModalContainer({
|
||||
const [updateNote] = useMutation(UPDATE_NOTE);
|
||||
|
||||
const { visible, context, actions } = noteUpsertModal;
|
||||
const { jobId, existingNote } = context;
|
||||
const { jobId, existingNote, text } = context;
|
||||
const { refetch } = actions;
|
||||
|
||||
const [form] = Form.useForm();
|
||||
@@ -45,8 +45,12 @@ export function NoteUpsertModalContainer({
|
||||
form.setFieldsValue(existingNote);
|
||||
} else if (!existingNote && visible) {
|
||||
form.resetFields();
|
||||
|
||||
if (text) {
|
||||
form.setFieldValue("text", text);
|
||||
}
|
||||
}
|
||||
}, [existingNote, form, visible]);
|
||||
}, [existingNote, form, visible, text]);
|
||||
|
||||
const handleFinish = async (formValues) => {
|
||||
const { relatedros, ...values } = formValues;
|
||||
@@ -82,6 +86,7 @@ export function NoteUpsertModalContainer({
|
||||
{ ...values, jobid: jobId, created_by: currentUser.email },
|
||||
],
|
||||
},
|
||||
refetchQueries: ["QUERY_NOTES_BY_JOB_PK"],
|
||||
});
|
||||
|
||||
if (AdditionalNoteInserts.length > 0) {
|
||||
|
||||
@@ -1,15 +1,40 @@
|
||||
import { Button, Form, notification, PageHeader } from "antd";
|
||||
import { Button, Form, notification, PageHeader, Popconfirm } from "antd";
|
||||
import React, { useState } from "react";
|
||||
import { useHistory } from "react-router-dom";
|
||||
import { useMutation } from "@apollo/client";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { UPDATE_OWNER } from "../../graphql/owners.queries";
|
||||
import { DELETE_OWNER, UPDATE_OWNER } from "../../graphql/owners.queries";
|
||||
import OwnerDetailFormComponent from "./owner-detail-form.component";
|
||||
|
||||
function OwnerDetailFormContainer({ owner, refetch }) {
|
||||
const { t } = useTranslation();
|
||||
const [form] = Form.useForm();
|
||||
const history = useHistory();
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [updateOwner] = useMutation(UPDATE_OWNER);
|
||||
const [deleteOwner] = useMutation(DELETE_OWNER);
|
||||
|
||||
const handleDelete = async () => {
|
||||
setLoading(true);
|
||||
const result = await deleteOwner({
|
||||
variables: { id: owner.id },
|
||||
});
|
||||
console.log(result);
|
||||
if (result.errors) {
|
||||
notification["error"]({
|
||||
message: t("owners.errors.deleting", {
|
||||
error: JSON.stringify(result.errors),
|
||||
}),
|
||||
});
|
||||
setLoading(false);
|
||||
} else {
|
||||
notification["success"]({
|
||||
message: t("owners.successes.delete"),
|
||||
});
|
||||
setLoading(false);
|
||||
history.push(`/manage/owners`);
|
||||
}
|
||||
};
|
||||
|
||||
const handleFinish = async (values) => {
|
||||
setLoading(true);
|
||||
@@ -41,15 +66,29 @@ function OwnerDetailFormContainer({ owner, refetch }) {
|
||||
<>
|
||||
<PageHeader
|
||||
title={t("menus.header.owners")}
|
||||
extra={
|
||||
extra={[
|
||||
<Popconfirm
|
||||
trigger="click"
|
||||
onConfirm={handleDelete}
|
||||
disabled={owner.jobs.length !== 0}
|
||||
title={t("owners.labels.deleteconfirm")}
|
||||
>
|
||||
<Button
|
||||
type="danger"
|
||||
loading={loading}
|
||||
disabled={owner.jobs.length !== 0}
|
||||
>
|
||||
{t("general.actions.delete")}
|
||||
</Button>
|
||||
</Popconfirm>,
|
||||
<Button
|
||||
type="primary"
|
||||
loading={loading}
|
||||
onClick={() => form.submit()}
|
||||
>
|
||||
{t("general.actions.save")}
|
||||
</Button>
|
||||
}
|
||||
</Button>,
|
||||
]}
|
||||
/>
|
||||
<Form
|
||||
form={form}
|
||||
|
||||
@@ -201,6 +201,7 @@ export function PartsOrderListTableComponent({
|
||||
subject: record.return
|
||||
? Templates.parts_return_slip.subject
|
||||
: Templates.parts_order.subject,
|
||||
to: record.vendor.email,
|
||||
}}
|
||||
id={job.id}
|
||||
/>
|
||||
@@ -296,7 +297,6 @@ export function PartsOrderListTableComponent({
|
||||
sortOrder:
|
||||
state.sortedInfo.columnKey === "quantity" && state.sortedInfo.order,
|
||||
},
|
||||
|
||||
{
|
||||
title: t("parts_orders.fields.act_price"),
|
||||
dataIndex: "act_price",
|
||||
|
||||
@@ -91,11 +91,13 @@ const r = ({ technician, state, activeStatuses, bodyshop }) => {
|
||||
b.v_make_desc + b.v_model_desc
|
||||
),
|
||||
sortOrder:
|
||||
state.sortedInfo.columnKey === "ownr" && state.sortedInfo.order,
|
||||
state.sortedInfo.columnKey === "vehicle" && state.sortedInfo.order,
|
||||
render: (text, record) => (
|
||||
<span>{`${record.v_model_yr || ""} ${record.v_make_desc || ""} ${
|
||||
record.v_model_desc || ""
|
||||
} ${record.v_color || ""} ${record.plate_no || ""}`}</span>
|
||||
<Link to={`/manage/vehicles/${record.vehicleid}`}>{`${
|
||||
record.v_model_yr || ""
|
||||
} ${record.v_make_desc || ""} ${record.v_model_desc || ""} ${
|
||||
record.v_color || ""
|
||||
} ${record.plate_no || ""}`}</Link>
|
||||
),
|
||||
},
|
||||
{
|
||||
|
||||
@@ -1,12 +1,23 @@
|
||||
import Icon from "@ant-design/icons";
|
||||
import { useMutation } from "@apollo/client";
|
||||
import { Button, Input, Popover } from "antd";
|
||||
import { Button, Input, Popover, Space } from "antd";
|
||||
import React, { useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { FaRegStickyNote } from "react-icons/fa";
|
||||
import { logImEXEvent } from "../../firebase/firebase.utils";
|
||||
import { UPDATE_JOB } from "../../graphql/jobs.queries";
|
||||
export default function ProductionListColumnProductionNote({ record }) {
|
||||
import { setModalContext } from "../../redux/modals/modals.actions";
|
||||
import { connect } from "react-redux";
|
||||
import { createStructuredSelector } from "reselect";
|
||||
|
||||
const mapStateToProps = createStructuredSelector({});
|
||||
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
setNoteUpsertContext: (context) =>
|
||||
dispatch(setModalContext({ context: context, modal: "noteUpsert" })),
|
||||
});
|
||||
|
||||
function ProductionListColumnProductionNote({ record, setNoteUpsertContext }) {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const [note, setNote] = useState(
|
||||
@@ -60,12 +71,26 @@ export default function ProductionListColumnProductionNote({ record }) {
|
||||
// onPressEnter={handleSaveNote}
|
||||
autoFocus
|
||||
allowClear
|
||||
style={{ marginBottom: "1em" }}
|
||||
/>
|
||||
<div>
|
||||
<Button onClick={handleSaveNote}>
|
||||
<Space>
|
||||
<Button onClick={handleSaveNote} type="primary">
|
||||
{t("general.actions.save")}
|
||||
</Button>
|
||||
</div>
|
||||
<Button
|
||||
onClick={() => {
|
||||
setVisible(false);
|
||||
setNoteUpsertContext({
|
||||
context: {
|
||||
jobId: record.id,
|
||||
text: note,
|
||||
},
|
||||
});
|
||||
}}
|
||||
>
|
||||
{t("notes.actions.savetojobnotes")}
|
||||
</Button>
|
||||
</Space>
|
||||
</div>
|
||||
}
|
||||
trigger={["click"]}
|
||||
@@ -85,3 +110,8 @@ export default function ProductionListColumnProductionNote({ record }) {
|
||||
</Popover>
|
||||
);
|
||||
}
|
||||
|
||||
export default connect(
|
||||
mapStateToProps,
|
||||
mapDispatchToProps
|
||||
)(ProductionListColumnProductionNote);
|
||||
|
||||
@@ -81,7 +81,7 @@ export function ProductionListTable({
|
||||
state,
|
||||
activeStatuses: bodyshop.md_ro_statuses.active_statuses,
|
||||
}).find((e) => e.key === k.key),
|
||||
width: k.width,
|
||||
width: k.width ?? 100,
|
||||
};
|
||||
})) ||
|
||||
[]
|
||||
@@ -267,6 +267,8 @@ export function ProductionListTable({
|
||||
sortOrder:
|
||||
state.sortedInfo.columnKey === c.key && state.sortedInfo.order,
|
||||
title: headerItem(c),
|
||||
ellipsis: true,
|
||||
width: c.width ?? 100,
|
||||
onHeaderCell: (column) => ({
|
||||
width: column.width,
|
||||
onResize: handleResize(index),
|
||||
@@ -276,11 +278,12 @@ export function ProductionListTable({
|
||||
rowKey="id"
|
||||
loading={loading}
|
||||
dataSource={dataSource}
|
||||
// scroll={{ x: true }}
|
||||
scroll={{ x: 1000 }}
|
||||
onChange={handleTableChange}
|
||||
/>
|
||||
</ReactDragListView.DragColumn>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default connect(mapStateToProps, null)(ProductionListTable);
|
||||
|
||||
@@ -3,8 +3,26 @@ import { Resizable } from "react-resizable";
|
||||
|
||||
export default function ResizableComponent(props) {
|
||||
const { onResize, width, ...restProps } = props;
|
||||
|
||||
if (!width) {
|
||||
return <th {...restProps} />;
|
||||
}
|
||||
|
||||
return (
|
||||
<Resizable width={width || 200} height={0} onResize={onResize}>
|
||||
<Resizable
|
||||
width={width || 200}
|
||||
height={0}
|
||||
onResize={onResize}
|
||||
draggableOpts={{ enableUserSelectHack: false }}
|
||||
handle={
|
||||
<span
|
||||
className="react-resizable-handle"
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
}}
|
||||
/>
|
||||
}
|
||||
>
|
||||
<th {...restProps} />
|
||||
</Resizable>
|
||||
);
|
||||
|
||||
@@ -92,7 +92,11 @@ export function ReportCenterModalComponent({ reportCenterModal }) {
|
||||
to: values.to,
|
||||
subject: Templates[values.key]?.subject,
|
||||
},
|
||||
values.sendby === "email" ? "e" : "p",
|
||||
values.sendbyexcel === "excel"
|
||||
? "x"
|
||||
: values.sendby === "email"
|
||||
? "e"
|
||||
: "p",
|
||||
id
|
||||
);
|
||||
setLoading(false);
|
||||
@@ -250,15 +254,38 @@ export function ReportCenterModalComponent({ reportCenterModal }) {
|
||||
ranges={DatePIckerRanges}
|
||||
/>
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("general.labels.sendby")}
|
||||
name="sendby"
|
||||
initialValue="print"
|
||||
>
|
||||
<Radio.Group>
|
||||
<Radio value="email">{t("general.labels.email")}</Radio>
|
||||
<Radio value="print">{t("general.labels.print")}</Radio>
|
||||
</Radio.Group>
|
||||
<Form.Item style={{ margin: 0, padding: 0 }} dependencies={["key"]}>
|
||||
{() => {
|
||||
const key = form.getFieldValue("key");
|
||||
//Kind of Id
|
||||
const reporttype = Templates[key] && Templates[key].reporttype;
|
||||
|
||||
if (reporttype === "excel")
|
||||
return (
|
||||
<Form.Item
|
||||
label={t("general.labels.sendby")}
|
||||
name="sendbyexcel"
|
||||
initialValue="excel"
|
||||
>
|
||||
<Radio.Group>
|
||||
<Radio value="excel">{t("general.labels.excel")}</Radio>
|
||||
</Radio.Group>
|
||||
</Form.Item>
|
||||
);
|
||||
if (reporttype !== "excel")
|
||||
return (
|
||||
<Form.Item
|
||||
label={t("general.labels.sendby")}
|
||||
name="sendby"
|
||||
initialValue="print"
|
||||
>
|
||||
<Radio.Group>
|
||||
<Radio value="email">{t("general.labels.email")}</Radio>
|
||||
<Radio value="print">{t("general.labels.print")}</Radio>
|
||||
</Radio.Group>
|
||||
</Form.Item>
|
||||
);
|
||||
}}
|
||||
</Form.Item>
|
||||
|
||||
<div
|
||||
|
||||
@@ -1,5 +1,14 @@
|
||||
import { SyncOutlined } from "@ant-design/icons";
|
||||
import { Button, Card, Checkbox, Col, PageHeader, Row, Space } from "antd";
|
||||
import {
|
||||
Button,
|
||||
Card,
|
||||
Checkbox,
|
||||
Col,
|
||||
PageHeader,
|
||||
Row,
|
||||
Select,
|
||||
Space,
|
||||
} from "antd";
|
||||
import { t } from "i18next";
|
||||
import React, { useMemo } from "react";
|
||||
import useLocalStorage from "../../utils/useLocalStorage";
|
||||
@@ -9,22 +18,39 @@ 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";
|
||||
import ScheduleVerifyIntegrity from "../schedule-verify-integrity/schedule-verify-integrity.component";
|
||||
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
|
||||
)(ScheduleCalendarComponent);
|
||||
|
||||
export default function ScheduleCalendarComponent({ data, refetch }) {
|
||||
export function ScheduleCalendarComponent({ data, refetch, bodyshop }) {
|
||||
const [filter, setFilter] = useLocalStorage("filter_events", {
|
||||
intake: true,
|
||||
manual: true,
|
||||
employeevacation: true,
|
||||
ins_co_nm: null,
|
||||
});
|
||||
const filteredData = useMemo(() => {
|
||||
return data.filter(
|
||||
(d) =>
|
||||
d.block ||
|
||||
(filter.intake && d.isintake) ||
|
||||
(filter.manual && !d.isintake && d.block === false) ||
|
||||
(d.__typename === "employee_vacation" &&
|
||||
filter.employeevacation &&
|
||||
!!d.employee)
|
||||
(d.block ||
|
||||
(filter.intake && d.isintake) ||
|
||||
(filter.manual && !d.isintake && d.block === false) ||
|
||||
(d.__typename === "employee_vacation" &&
|
||||
filter.employeevacation &&
|
||||
!!d.employee)) &&
|
||||
(filter.ins_co_nm && filter.ins_co_nm.length > 0
|
||||
? filter.ins_co_nm.includes(d.job?.ins_co_nm)
|
||||
: true)
|
||||
);
|
||||
}, [data, filter]);
|
||||
|
||||
@@ -37,6 +63,21 @@ export default function ScheduleCalendarComponent({ data, refetch }) {
|
||||
extra={
|
||||
<Space wrap>
|
||||
<ScheduleAtsSummary appointments={filteredData} />
|
||||
<Select
|
||||
style={{ minWidth: "15rem" }}
|
||||
mode="multiple"
|
||||
placeholder={t("schedule.labels.ins_co_nm_filter")}
|
||||
allowClear
|
||||
onClear={() => setFilter({ ...filter, ins_co_nm: [] })}
|
||||
value={filter?.ins_co_nm ? filter.ins_co_nm : []}
|
||||
onChange={(e) => {
|
||||
setFilter({ ...filter, ins_co_nm: e });
|
||||
}}
|
||||
options={bodyshop.md_ins_cos.map((i) => ({
|
||||
label: i.name,
|
||||
value: i.name,
|
||||
}))}
|
||||
/>
|
||||
<Checkbox
|
||||
checked={filter?.intake}
|
||||
onChange={(e) => {
|
||||
|
||||
@@ -148,6 +148,7 @@ export function ScheduleJobModalContainer({
|
||||
date_scheduled: new Date(),
|
||||
scheduled_in: values.start,
|
||||
scheduled_completion: values.scheduled_completion,
|
||||
lost_sale_reason: null,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
import Dinero from "dinero.js";
|
||||
|
||||
const CustomTooltip = ({ active, payload, label }) => {
|
||||
if (active && payload && payload.length) {
|
||||
return (
|
||||
<div
|
||||
style={{
|
||||
backgroundColor: "white",
|
||||
border: "1px solid gray",
|
||||
padding: "0.5rem",
|
||||
}}
|
||||
>
|
||||
<p style={{ margin: "0" }}>{label}</p>
|
||||
{payload.map((data, index) => {
|
||||
if (data.dataKey === "sales" || data.dataKey === "accSales")
|
||||
return (
|
||||
<p
|
||||
style={{ margin: "10px 0", color: data.color }}
|
||||
key={index}
|
||||
>{`${data.name} : ${Dinero({
|
||||
amount: Math.round(data.value * 100),
|
||||
}).toFormat()}`}</p>
|
||||
);
|
||||
|
||||
return (
|
||||
<p
|
||||
style={{ margin: "10px 0", color: data.color }}
|
||||
key={index}
|
||||
>{`${data.name} : ${data.value}`}</p>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
export default CustomTooltip;
|
||||
@@ -1,3 +1,4 @@
|
||||
import Dinero from "dinero.js";
|
||||
import { Card } from "antd";
|
||||
import moment from "moment";
|
||||
import React from "react";
|
||||
@@ -18,6 +19,7 @@ import { createStructuredSelector } from "reselect";
|
||||
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||
import * as Utils from "../scoreboard-targets-table/scoreboard-targets-table.util";
|
||||
import _ from "lodash";
|
||||
import CustomTooltip from "./chart-custom-tooltip";
|
||||
|
||||
const graphProps = {
|
||||
strokeWidth: 3,
|
||||
@@ -44,14 +46,19 @@ export function ScoreboardChart({ sbEntriesByDate, bodyshop }) {
|
||||
return {
|
||||
bodyhrs: dayAcc.bodyhrs + dayVal.bodyhrs,
|
||||
painthrs: dayAcc.painthrs + dayVal.painthrs,
|
||||
sales:
|
||||
dayAcc.painthrs +
|
||||
dayVal.job.job_totals.totals.subtotal.amount / 100 +
|
||||
2500,
|
||||
};
|
||||
},
|
||||
{ bodyhrs: 0, painthrs: 0 }
|
||||
{ bodyhrs: 0, painthrs: 0, sales: 0 }
|
||||
);
|
||||
} else {
|
||||
dayhrs = {
|
||||
bodyhrs: 0,
|
||||
painthrs: 0,
|
||||
sales: 0,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -73,6 +80,13 @@ export function ScoreboardChart({ sbEntriesByDate, bodyshop }) {
|
||||
: dayhrs.painthrs + dayhrs.bodyhrs,
|
||||
1
|
||||
),
|
||||
sales: _.round(dayhrs.sales, 2),
|
||||
accSales: _.round(
|
||||
acc.length > 0
|
||||
? acc[acc.length - 1].accSales + dayhrs.sales
|
||||
: dayhrs.sales,
|
||||
2
|
||||
),
|
||||
};
|
||||
|
||||
return [...acc, theValue];
|
||||
@@ -87,22 +101,27 @@ export function ScoreboardChart({ sbEntriesByDate, bodyshop }) {
|
||||
>
|
||||
<CartesianGrid stroke="#f5f5f5" />
|
||||
<XAxis dataKey="date" strokeWidth={graphProps.strokeWidth} />
|
||||
<YAxis strokeWidth={graphProps.strokeWidth} />
|
||||
<Tooltip />
|
||||
<Legend />
|
||||
<Area
|
||||
type="monotone"
|
||||
name="Accumulated Hours"
|
||||
dataKey="accHrs"
|
||||
fill="lightgreen"
|
||||
stroke="green"
|
||||
<YAxis
|
||||
strokeWidth={graphProps.strokeWidth}
|
||||
// allowDataOverflow
|
||||
dataKey="sales"
|
||||
yAxisId="right"
|
||||
tickFormatter={(value) =>
|
||||
Dinero({ amount: Math.round(value * 100) }).toFormat()
|
||||
}
|
||||
orientation="right"
|
||||
/>
|
||||
<YAxis yAxisId="left" strokeWidth={graphProps.strokeWidth} />
|
||||
<Tooltip content={<CustomTooltip />} />
|
||||
<Legend />
|
||||
|
||||
<Bar
|
||||
name="Body Hours"
|
||||
dataKey="bodyHrs"
|
||||
stackId="day"
|
||||
barSize={20}
|
||||
fill="darkblue"
|
||||
yAxisId="left"
|
||||
/>
|
||||
<Bar
|
||||
name="Paint Hours"
|
||||
@@ -110,12 +129,42 @@ export function ScoreboardChart({ sbEntriesByDate, bodyshop }) {
|
||||
stackId="day"
|
||||
barSize={20}
|
||||
fill="darkred"
|
||||
yAxisId="left"
|
||||
/>
|
||||
<Line
|
||||
name="Target Hours"
|
||||
type="monotone"
|
||||
dataKey="accTargetHrs"
|
||||
stroke="#ff7300"
|
||||
yAxisId="left"
|
||||
strokeWidth={graphProps.strokeWidth}
|
||||
/>
|
||||
|
||||
<Area
|
||||
type="monotone"
|
||||
name="MTD Hours"
|
||||
dataKey="accHrs"
|
||||
fill="lightblue"
|
||||
stroke="blue"
|
||||
yAxisId="left"
|
||||
/>
|
||||
{
|
||||
// <Area
|
||||
// type="monotone"
|
||||
// name="MTD Sales"
|
||||
// dataKey="accSales"
|
||||
// fill="lightgreen"
|
||||
// stroke="green"
|
||||
// yAxisId="right"
|
||||
// />
|
||||
}
|
||||
<Bar
|
||||
name="Sales"
|
||||
dataKey="sales"
|
||||
stackId="day"
|
||||
barSize={20}
|
||||
fill="darkgreen"
|
||||
yAxisId="right"
|
||||
strokeWidth={graphProps.strokeWidth}
|
||||
/>
|
||||
</ComposedChart>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Card, Statistic } from "antd";
|
||||
import { Card, Divider, Statistic } from "antd";
|
||||
import moment from "moment";
|
||||
import React from "react";
|
||||
import { connect } from "react-redux";
|
||||
@@ -41,6 +41,9 @@ export function ScoreboardDayStats({ bodyshop, date, entries }) {
|
||||
label="P"
|
||||
value={paintHrs.toFixed(1)}
|
||||
/>
|
||||
<Divider style={{ margin: 0 }} />
|
||||
|
||||
<Statistic value={(bodyHrs + paintHrs).toFixed(1)} />
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { CalendarOutlined } from "@ant-design/icons";
|
||||
import { Card, Col, Row, Statistic } from "antd";
|
||||
import { Card, Col, Divider, Row, Statistic } from "antd";
|
||||
import _ from "lodash";
|
||||
import moment from "moment";
|
||||
import React, { useMemo } from "react";
|
||||
@@ -177,6 +177,9 @@ export function ScoreboardTargetsTable({ bodyshop, scoreBoardlist }) {
|
||||
<Statistic value={values.toDatePaint.toFixed(1)} />
|
||||
</Col>
|
||||
</Row>
|
||||
<Row>
|
||||
<Divider style={{ margin: 5 }} />
|
||||
</Row>
|
||||
<Row>
|
||||
<Col {...statSpans}></Col>
|
||||
<Col {...statSpans}>
|
||||
@@ -184,14 +187,53 @@ export function ScoreboardTargetsTable({ bodyshop, scoreBoardlist }) {
|
||||
value={(values.todayPaint + values.todayBody).toFixed(1)}
|
||||
/>
|
||||
</Col>
|
||||
<Col {...statSpans}></Col>
|
||||
<Col {...statSpans}>
|
||||
<Statistic
|
||||
value={(
|
||||
Util.WeeklyTargetHrs(
|
||||
bodyshop.scoreboard_target.dailyBodyTarget,
|
||||
bodyshop
|
||||
) +
|
||||
Util.WeeklyTargetHrs(
|
||||
bodyshop.scoreboard_target.dailyPaintTarget,
|
||||
bodyshop
|
||||
)
|
||||
).toFixed(1)}
|
||||
/>
|
||||
</Col>
|
||||
<Col {...statSpans}>
|
||||
<Statistic
|
||||
value={(values.weeklyPaint + values.weeklyBody).toFixed(1)}
|
||||
/>
|
||||
</Col>
|
||||
<Col {...statSpans}></Col>
|
||||
<Col {...statSpans}></Col>
|
||||
<Col {...statSpans}>
|
||||
<Statistic
|
||||
value={(
|
||||
Util.MonthlyTargetHrs(
|
||||
bodyshop.scoreboard_target.dailyBodyTarget,
|
||||
bodyshop
|
||||
) +
|
||||
Util.MonthlyTargetHrs(
|
||||
bodyshop.scoreboard_target.dailyPaintTarget,
|
||||
bodyshop
|
||||
)
|
||||
).toFixed(1)}
|
||||
/>
|
||||
</Col>
|
||||
<Col {...statSpans}>
|
||||
<Statistic
|
||||
value={(
|
||||
Util.AsOfTodayTargetHrs(
|
||||
bodyshop.scoreboard_target.dailyBodyTarget,
|
||||
bodyshop
|
||||
) +
|
||||
Util.AsOfTodayTargetHrs(
|
||||
bodyshop.scoreboard_target.dailyPaintTarget,
|
||||
bodyshop
|
||||
)
|
||||
).toFixed(1)}
|
||||
/>
|
||||
</Col>
|
||||
<Col {...statSpans}>
|
||||
<Statistic
|
||||
value={(values.toDatePaint + values.toDateBody).toFixed(1)}
|
||||
|
||||
@@ -81,6 +81,7 @@ export default function ScoreboardTimeTickets() {
|
||||
totalLastMonth: 0,
|
||||
totalOverPeriod: 0,
|
||||
actualTotalOverPeriod: 0,
|
||||
totalEffieciencyOverPeriod: 0,
|
||||
employees: {},
|
||||
};
|
||||
data.fixedperiod.forEach((ticket) => {
|
||||
@@ -94,6 +95,7 @@ export default function ScoreboardTimeTickets() {
|
||||
totalLastMonth: 0,
|
||||
totalOverPeriod: 0,
|
||||
actualTotalOverPeriod: 0,
|
||||
totalEffieciencyOverPeriod: 0,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -221,6 +223,30 @@ export default function ScoreboardTimeTickets() {
|
||||
|
||||
ret2.push(r);
|
||||
});
|
||||
|
||||
// Add total efficiency of employees
|
||||
const totalActualAndProductive = Object.keys(ret.employees)
|
||||
.map((key) => {
|
||||
return { employee_number: key, ...ret.employees[key] };
|
||||
})
|
||||
.reduce(
|
||||
(acc, e) => {
|
||||
return {
|
||||
totalOverPeriod: acc.totalOverPeriod + e.totalOverPeriod,
|
||||
actualTotalOverPeriod:
|
||||
acc.actualTotalOverPeriod + e.actualTotalOverPeriod,
|
||||
};
|
||||
},
|
||||
{ totalOverPeriod: 0, actualTotalOverPeriod: 0 }
|
||||
);
|
||||
|
||||
ret.totalEffieciencyOverPeriod =
|
||||
totalActualAndProductive.actualTotalOverPeriod
|
||||
? (totalActualAndProductive.totalOverPeriod /
|
||||
totalActualAndProductive.actualTotalOverPeriod) *
|
||||
100
|
||||
: 0;
|
||||
|
||||
roundObject(ret);
|
||||
roundObject(totals);
|
||||
roundObject(ret2);
|
||||
|
||||
@@ -62,7 +62,7 @@ export function ScoreboardTicketsStats({ data, bodyshop }) {
|
||||
key: "efficiencyoverperiod",
|
||||
render: (text, record) =>
|
||||
`${(
|
||||
(record.totalOverPeriod / (record.actualTotalOverPeriod || .1)) *
|
||||
(record.totalOverPeriod / (record.actualTotalOverPeriod || 0.1)) *
|
||||
100
|
||||
).toFixed(1)} %`,
|
||||
},
|
||||
@@ -113,6 +113,12 @@ export function ScoreboardTicketsStats({ data, bodyshop }) {
|
||||
value={data.totalOverPeriod}
|
||||
/>
|
||||
</Col>
|
||||
<Col span={12}>
|
||||
<Statistic
|
||||
title={t("scoreboard.labels.efficiencyoverperiod")}
|
||||
value={`${data.totalEffieciencyOverPeriod || 0}%`}
|
||||
/>
|
||||
</Col>
|
||||
</Row>
|
||||
<Typography.Text type="secondary">
|
||||
{t("scoreboard.labels.calendarperiod")}
|
||||
@@ -121,7 +127,7 @@ export function ScoreboardTicketsStats({ data, bodyshop }) {
|
||||
<Col md={24} lg={20}>
|
||||
<Table
|
||||
columns={columns}
|
||||
rowKey='employee_number'
|
||||
rowKey="employee_number"
|
||||
dataSource={tableData}
|
||||
id="employee_number"
|
||||
scroll={{ y: "300px" }}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { useTreatments } from "@splitsoftware/splitio-react";
|
||||
import { Button, Card, Tabs } from "antd";
|
||||
import React from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
@@ -8,6 +9,7 @@ import ShopInfoGeneral from "./shop-info.general.component";
|
||||
import ShopInfoIntakeChecklistComponent from "./shop-info.intake.component";
|
||||
import ShopInfoLaborRates from "./shop-info.laborrates.component";
|
||||
import ShopInfoOrderStatusComponent from "./shop-info.orderstatus.component";
|
||||
import ShopInfoPartsScan from "./shop-info.parts-scan";
|
||||
import ShopInfoRbacComponent from "./shop-info.rbac.component";
|
||||
import ShopInfoResponsibilityCenterComponent from "./shop-info.responsibilitycenters.component";
|
||||
import ShopInfoROStatusComponent from "./shop-info.rostatus.component";
|
||||
@@ -23,6 +25,11 @@ const mapDispatchToProps = (dispatch) => ({
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(ShopInfoComponent);
|
||||
|
||||
export function ShopInfoComponent({ bodyshop, form, saveLoading }) {
|
||||
const { CriticalPartsScanning } = useTreatments(
|
||||
["CriticalPartsScanning"],
|
||||
{},
|
||||
bodyshop.imexshopid
|
||||
);
|
||||
const { t } = useTranslation();
|
||||
return (
|
||||
<Card
|
||||
@@ -71,6 +78,11 @@ export function ShopInfoComponent({ bodyshop, form, saveLoading }) {
|
||||
<Tabs.TabPane key="laborrates" tab={t("bodyshop.labels.laborrates")}>
|
||||
<ShopInfoLaborRates form={form} />
|
||||
</Tabs.TabPane>
|
||||
{CriticalPartsScanning.treatment === "on" && (
|
||||
<Tabs.TabPane key="partsscan" tab={t("bodyshop.labels.partsscan")}>
|
||||
<ShopInfoPartsScan form={form} />
|
||||
</Tabs.TabPane>
|
||||
)}
|
||||
</Tabs>
|
||||
</Card>
|
||||
);
|
||||
|
||||
@@ -11,13 +11,13 @@ import {
|
||||
} from "antd";
|
||||
import React from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import CurrencyInput from "../form-items-formatted/currency-form-item.component";
|
||||
import FormItemEmail from "../form-items-formatted/email-form-item.component";
|
||||
import PhoneFormItem, {
|
||||
PhoneItemFormatterValidation,
|
||||
} from "../form-items-formatted/phone-form-item.component";
|
||||
import FormListMoveArrows from "../form-list-move-arrows/form-list-move-arrows.component";
|
||||
import LayoutFormRow from "../layout-form-row/layout-form-row.component";
|
||||
import CurrencyInput from "../form-items-formatted/currency-form-item.component";
|
||||
import FormItemEmail from "../form-items-formatted/email-form-item.component";
|
||||
|
||||
import momentTZ from "moment-timezone";
|
||||
const timeZonesList = momentTZ.tz.names();
|
||||
@@ -473,6 +473,13 @@ export default function ShopInfoGeneral({ form }) {
|
||||
>
|
||||
<Switch />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
name={["enforce_conversion_category"]}
|
||||
label={t("bodyshop.fields.enforce_conversion_category")}
|
||||
valuePropName="checked"
|
||||
>
|
||||
<Switch />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
name={["target_touchtime"]}
|
||||
label={t("bodyshop.fields.target_touchtime")}
|
||||
@@ -544,6 +551,13 @@ export default function ShopInfoGeneral({ form }) {
|
||||
>
|
||||
<CurrencyInput />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
name={["use_paint_scale_data"]}
|
||||
label={t("bodyshop.fields.use_paint_scale_data")}
|
||||
valuePropName="checked"
|
||||
>
|
||||
<Switch />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
name={["attach_pdf_to_email"]}
|
||||
label={t("bodyshop.fields.attach_pdf_to_email")}
|
||||
@@ -582,6 +596,13 @@ export default function ShopInfoGeneral({ form }) {
|
||||
>
|
||||
<Switch />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
name={["tt_enforce_hours_for_tech_console"]}
|
||||
label={t("bodyshop.fields.tt_enforce_hours_for_tech_console")}
|
||||
valuePropName="checked"
|
||||
>
|
||||
<Switch />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
name={["bill_allow_post_to_closed"]}
|
||||
label={t("bodyshop.fields.bill_allow_post_to_closed")}
|
||||
|
||||
81
client/src/components/shop-info/shop-info.parts-scan.jsx
Normal file
81
client/src/components/shop-info/shop-info.parts-scan.jsx
Normal file
@@ -0,0 +1,81 @@
|
||||
import { DeleteFilled } from "@ant-design/icons";
|
||||
import { Button, Form, Input, Space } from "antd";
|
||||
import React from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import FormListMoveArrows from "../form-list-move-arrows/form-list-move-arrows.component";
|
||||
import LayoutFormRow from "../layout-form-row/layout-form-row.component";
|
||||
|
||||
export default function ShopInfoPartsScan({ form }) {
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
<div>
|
||||
<LayoutFormRow header={t("bodyshop.labels.md_parts_scan")}>
|
||||
<Form.List name={["md_parts_scan"]}>
|
||||
{(fields, { add, remove, move }) => {
|
||||
return (
|
||||
<div>
|
||||
{fields.map((field, index) => (
|
||||
<Form.Item key={field.key}>
|
||||
<LayoutFormRow noDivider>
|
||||
<Form.Item
|
||||
label={t("bodyshop.fields.md_parts_scan.expression")}
|
||||
key={`${index}expression`}
|
||||
name={[field.name, "expression"]}
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
//message: t("general.validation.required"),
|
||||
},
|
||||
]}
|
||||
>
|
||||
<Input />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("bodyshop.fields.md_parts_scan.flags")}
|
||||
key={`${index}flags`}
|
||||
name={[field.name, "flags"]}
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
//message: t("general.validation.required"),
|
||||
},
|
||||
]}
|
||||
>
|
||||
<Input />
|
||||
</Form.Item>
|
||||
|
||||
<Space wrap>
|
||||
<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("bodyshop.actions.addpartsrule")}
|
||||
</Button>
|
||||
</Form.Item>
|
||||
</div>
|
||||
);
|
||||
}}
|
||||
</Form.List>
|
||||
</LayoutFormRow>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -76,6 +76,19 @@ export default function ShopInfoSchedulingComponent({ form }) {
|
||||
>
|
||||
<InputNumber min={0} />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
name={["md_lost_sale_reasons"]}
|
||||
label={t("bodyshop.fields.md_lost_sale_reasons")}
|
||||
rules={[
|
||||
{
|
||||
// required: true,
|
||||
//message: t("general.validation.required"),
|
||||
type: "array",
|
||||
},
|
||||
]}
|
||||
>
|
||||
<Select mode="tags" />
|
||||
</Form.Item>
|
||||
</LayoutFormRow>
|
||||
<Divider orientation="left">{t("bodyshop.labels.workingdays")}</Divider>
|
||||
<Space wrap size="large">
|
||||
|
||||
@@ -26,6 +26,10 @@ export function TechClockInContainer({
|
||||
technician,
|
||||
bodyshop,
|
||||
}) {
|
||||
console.log(
|
||||
"🚀 ~ file: tech-job-clock-in-form.container.jsx:29 ~ technician:",
|
||||
technician
|
||||
);
|
||||
const [form] = Form.useForm();
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [insertTimeTicket] = useMutation(INSERT_NEW_TIME_TICKET, {
|
||||
@@ -33,6 +37,10 @@ export function TechClockInContainer({
|
||||
});
|
||||
const { t } = useTranslation();
|
||||
|
||||
const emps = bodyshop.employees.filter(
|
||||
(e) => e.id === (technician && technician.id)
|
||||
)[0];
|
||||
|
||||
const handleFinish = async (values) => {
|
||||
setLoading(true);
|
||||
const theTime = (await axios.post("/utils/time")).data;
|
||||
@@ -87,7 +95,12 @@ export function TechClockInContainer({
|
||||
onClick={() => {
|
||||
setTimeTicketContext({
|
||||
actions: {},
|
||||
context: { timeticket: { employeeid: technician.id } },
|
||||
context: {
|
||||
timeticket: {
|
||||
employeeid: technician.id,
|
||||
flat_rate: emps.flat_rate,
|
||||
},
|
||||
},
|
||||
});
|
||||
}}
|
||||
>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { useMutation } from "@apollo/client";
|
||||
import { useMutation, useQuery } from "@apollo/client";
|
||||
import {
|
||||
Button,
|
||||
Card,
|
||||
@@ -21,6 +21,8 @@ import { selectTechnician } from "../../redux/tech/tech.selectors";
|
||||
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||
import TechJobClockoutDelete from "../tech-job-clock-out-delete/tech-job-clock-out-delete.component";
|
||||
import { LaborAllocationContainer } from "../time-ticket-modal/time-ticket-modal.component";
|
||||
import { GET_LINE_TICKET_BY_PK } from "../../graphql/jobs-lines.queries";
|
||||
import { CalculateAllocationsTotals } from "../labor-allocations-table/labor-allocations-table.utility";
|
||||
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
bodyshop: selectBodyshop,
|
||||
@@ -39,7 +41,17 @@ export function TechClockOffButton({
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [updateTimeticket] = useMutation(UPDATE_TIME_TICKET);
|
||||
const [form] = Form.useForm();
|
||||
|
||||
const { queryLoading, data: lineTicketData } = useQuery(
|
||||
GET_LINE_TICKET_BY_PK,
|
||||
{
|
||||
variables: {
|
||||
id: jobId,
|
||||
},
|
||||
skip: !jobId,
|
||||
fetchPolicy: "network-only",
|
||||
nextFetchPolicy: "network-only",
|
||||
}
|
||||
);
|
||||
const { t } = useTranslation();
|
||||
const emps = bodyshop.employees.filter(
|
||||
(e) => e.id === (technician && technician.id)
|
||||
@@ -59,6 +71,7 @@ export function TechClockOffButton({
|
||||
emps &&
|
||||
emps.rates.filter((r) => r.cost_center === values.cost_center)[0]
|
||||
?.rate,
|
||||
flat_rate: emps && emps.flat_rate,
|
||||
ciecacode:
|
||||
bodyshop.cdk_dealerid || bodyshop.pbs_serialnumber
|
||||
? values.cost_center
|
||||
@@ -128,6 +141,54 @@ export function TechClockOffButton({
|
||||
required: true,
|
||||
//message: t("general.validation.required"),
|
||||
},
|
||||
({ getFieldValue }) => ({
|
||||
validator(rule, value) {
|
||||
console.log(
|
||||
bodyshop.tt_enforce_hours_for_tech_console
|
||||
);
|
||||
if (!bodyshop.tt_enforce_hours_for_tech_console) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
if (
|
||||
!value ||
|
||||
getFieldValue("cost_center") === null ||
|
||||
!lineTicketData
|
||||
)
|
||||
return Promise.resolve();
|
||||
|
||||
//Check the cost center,
|
||||
const totals = CalculateAllocationsTotals(
|
||||
bodyshop,
|
||||
lineTicketData.joblines,
|
||||
lineTicketData.timetickets,
|
||||
lineTicketData.jobs_by_pk.lbr_adjustments
|
||||
);
|
||||
|
||||
const fieldTypeToCheck =
|
||||
bodyshop.cdk_dealerid || bodyshop.pbs_serialnumber
|
||||
? "mod_lbr_ty"
|
||||
: "cost_center";
|
||||
|
||||
const costCenterDiff =
|
||||
Math.round(
|
||||
totals.find(
|
||||
(total) =>
|
||||
total[fieldTypeToCheck] ===
|
||||
getFieldValue("cost_center")
|
||||
)?.difference * 10
|
||||
) / 10;
|
||||
|
||||
if (value > costCenterDiff)
|
||||
return Promise.reject(
|
||||
t(
|
||||
"timetickets.validation.hoursenteredmorethanavailable"
|
||||
)
|
||||
);
|
||||
else {
|
||||
return Promise.resolve();
|
||||
}
|
||||
},
|
||||
}),
|
||||
]}
|
||||
>
|
||||
<InputNumber min={0} precision={1} />
|
||||
@@ -177,7 +238,11 @@ export function TechClockOffButton({
|
||||
</Col>
|
||||
{!isShiftTicket && (
|
||||
<Col span={16}>
|
||||
<LaborAllocationContainer jobid={jobId} />
|
||||
<LaborAllocationContainer
|
||||
jobid={jobId || null}
|
||||
loading={queryLoading}
|
||||
lineTicketData={lineTicketData}
|
||||
/>
|
||||
</Col>
|
||||
)}
|
||||
</Row>
|
||||
|
||||
@@ -0,0 +1,131 @@
|
||||
import { useQuery } from "@apollo/client";
|
||||
import { Card, Col, Space, Statistic, Typography } from "antd";
|
||||
import moment from "moment";
|
||||
import { useMemo } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { connect } from "react-redux";
|
||||
import { createStructuredSelector } from "reselect";
|
||||
import { QUERY_TIME_TICKETS_TECHNICIAN_IN_RANGE } from "../../graphql/timetickets.queries";
|
||||
import { selectTechnician } from "../../redux/tech/tech.selectors";
|
||||
import AlertComponent from "../alert/alert.component";
|
||||
import LoadingSpinner from "../loading-spinner/loading-spinner.component";
|
||||
const { Title } = Typography;
|
||||
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
technician: selectTechnician,
|
||||
});
|
||||
const mapDispatchToProps = (dispatch) => ({});
|
||||
|
||||
const TechJobStatistics = ({ technician }) => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const startDate = moment().startOf("week");
|
||||
const endDate = moment().endOf("week");
|
||||
|
||||
const { loading, error, data } = useQuery(
|
||||
QUERY_TIME_TICKETS_TECHNICIAN_IN_RANGE,
|
||||
{
|
||||
variables: {
|
||||
start: startDate.format("YYYY-MM-DD"),
|
||||
end: endDate.format("YYYY-MM-DD"),
|
||||
fixedStart: moment().startOf("month").format("YYYY-MM-DD"),
|
||||
fixedEnd: moment().endOf("month").format("YYYY-MM-DD"),
|
||||
employeeid: technician.id,
|
||||
},
|
||||
fetchPolicy: "network-only",
|
||||
nextFetchPolicy: "network-only",
|
||||
}
|
||||
);
|
||||
|
||||
const totals = useMemo(() => {
|
||||
if (data && data.timetickets && data.fixedperiod) {
|
||||
const week = data.timetickets.reduce(
|
||||
(acc, val) => {
|
||||
acc.productivehrs = acc.productivehrs + val.productivehrs;
|
||||
acc.actualhrs = acc.actualhrs + val.actualhrs;
|
||||
return acc;
|
||||
},
|
||||
{ productivehrs: 0, actualhrs: 0 }
|
||||
);
|
||||
|
||||
const month = data.fixedperiod.reduce(
|
||||
(acc, val) => {
|
||||
acc.productivehrs = acc.productivehrs + val.productivehrs;
|
||||
acc.actualhrs = acc.actualhrs + val.actualhrs;
|
||||
return acc;
|
||||
},
|
||||
{ productivehrs: 0, actualhrs: 0 }
|
||||
);
|
||||
|
||||
return {
|
||||
week,
|
||||
month,
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
week: { productivehrs: 0, actualhrs: 0 },
|
||||
month: { productivehrs: 0, actualhrs: 0 },
|
||||
};
|
||||
}, [data]);
|
||||
|
||||
if (loading) return <LoadingSpinner />;
|
||||
if (error) return <AlertComponent message={error.message} type="error" />;
|
||||
|
||||
return (
|
||||
<Card title={t("scoreboard.labels.productivestatistics")}>
|
||||
<Space size={100}>
|
||||
<Col>
|
||||
<Title level={5}>{t("scoreboard.labels.thisweek")}</Title>
|
||||
<Space size={20}>
|
||||
<Statistic
|
||||
title={t("timetickets.fields.productivehrs")}
|
||||
value={totals.week.productivehrs.toFixed(2)}
|
||||
/>
|
||||
<Statistic
|
||||
title={t("timetickets.fields.actualhrs")}
|
||||
value={totals.week.actualhrs.toFixed(2)}
|
||||
/>
|
||||
<Statistic
|
||||
title={t("timetickets.labels.efficiency")}
|
||||
value={
|
||||
totals.week.actualhrs
|
||||
? `${(
|
||||
(totals.week.productivehrs / totals.week.actualhrs) *
|
||||
100
|
||||
).toFixed(2)}%`
|
||||
: "0%"
|
||||
}
|
||||
/>
|
||||
</Space>
|
||||
</Col>
|
||||
<Col>
|
||||
<Title level={5}>{t("scoreboard.labels.thismonth")}</Title>
|
||||
<Space size={20}>
|
||||
<Statistic
|
||||
title={t("timetickets.fields.productivehrs")}
|
||||
value={totals.month.productivehrs.toFixed(2)}
|
||||
/>
|
||||
<Statistic
|
||||
title={t("timetickets.fields.actualhrs")}
|
||||
value={totals.month.actualhrs.toFixed(2)}
|
||||
/>
|
||||
<Statistic
|
||||
title={t("timetickets.labels.efficiency")}
|
||||
value={
|
||||
totals.month.actualhrs
|
||||
? `${(
|
||||
(totals.month.productivehrs / totals.month.actualhrs) *
|
||||
100
|
||||
).toFixed(2)}%`
|
||||
: "0%"
|
||||
}
|
||||
/>
|
||||
</Space>
|
||||
</Col>
|
||||
</Space>
|
||||
</Card>
|
||||
);
|
||||
};
|
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(TechJobStatistics);
|
||||
@@ -29,7 +29,17 @@ export function TechSider({ technician, techLogout }) {
|
||||
};
|
||||
|
||||
return (
|
||||
<Sider collapsible collapsed={collapsed} onCollapse={onCollapse}>
|
||||
<Sider
|
||||
style={{
|
||||
height: "100vh",
|
||||
position: "sticky",
|
||||
top: 0,
|
||||
left: 0,
|
||||
}}
|
||||
collapsible
|
||||
collapsed={collapsed}
|
||||
onCollapse={onCollapse}
|
||||
>
|
||||
<Menu theme="dark" defaultSelectedKeys={["1"]} mode="inline">
|
||||
<Menu.Item
|
||||
key="1"
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { useQuery } from "@apollo/client";
|
||||
import { useLazyQuery } from "@apollo/client";
|
||||
import { Form, Input, InputNumber, Select, Switch } from "antd";
|
||||
import React from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
@@ -14,6 +14,7 @@ import FormDatePicker from "../form-date-picker/form-date-picker.component";
|
||||
import FormDateTimePicker from "../form-date-time-picker/form-date-time-picker.component";
|
||||
import JobSearchSelect from "../job-search-select/job-search-select.component";
|
||||
import LaborAllocationsTable from "../labor-allocations-table/labor-allocations-table.component";
|
||||
import { CalculateAllocationsTotals } from "../labor-allocations-table/labor-allocations-table.utility";
|
||||
import LayoutFormRow from "../layout-form-row/layout-form-row.component";
|
||||
import LoadingSkeleton from "../loading-skeleton/loading-skeleton.component";
|
||||
import { HasRbacAccess } from "../rbac-wrapper/rbac-wrapper.component";
|
||||
@@ -38,7 +39,11 @@ export function TimeTicketModalComponent({
|
||||
employeeSelectDisabled,
|
||||
}) {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const [loadLineTicketData, { called, loading, data: lineTicketData }] =
|
||||
useLazyQuery(GET_LINE_TICKET_BY_PK, {
|
||||
fetchPolicy: "network-only",
|
||||
nextFetchPolicy: "network-only",
|
||||
});
|
||||
const CostCenterSelect = ({ emps, value, ...props }) => {
|
||||
return (
|
||||
<Select
|
||||
@@ -82,9 +87,10 @@ export function TimeTicketModalComponent({
|
||||
label={t("timetickets.fields.ro_number")}
|
||||
rules={[
|
||||
{
|
||||
required:
|
||||
!form.getFieldValue("cost_center") ===
|
||||
"timetickets.labels.shift",
|
||||
required: !(
|
||||
form.getFieldValue("cost_center") ===
|
||||
"timetickets.labels.shift"
|
||||
),
|
||||
//message: t("general.validation.required"),
|
||||
},
|
||||
]}
|
||||
@@ -175,6 +181,51 @@ export function TimeTicketModalComponent({
|
||||
label={t("timetickets.fields.productivehrs")}
|
||||
name="productivehrs"
|
||||
rules={[
|
||||
({ getFieldValue }) => ({
|
||||
validator(rule, value) {
|
||||
if (!bodyshop.tt_enforce_hours_for_tech_console) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
if (
|
||||
!value ||
|
||||
getFieldValue("cost_center") === null ||
|
||||
!lineTicketData
|
||||
)
|
||||
return Promise.resolve();
|
||||
|
||||
//Check the cost center,
|
||||
const totals = CalculateAllocationsTotals(
|
||||
bodyshop,
|
||||
lineTicketData.joblines,
|
||||
lineTicketData.timetickets,
|
||||
lineTicketData.jobs_by_pk.lbr_adjustments
|
||||
);
|
||||
|
||||
const fieldTypeToCheck =
|
||||
bodyshop.cdk_dealerid || bodyshop.pbs_serialnumber
|
||||
? "mod_lbr_ty"
|
||||
: "cost_center";
|
||||
|
||||
const costCenterDiff =
|
||||
Math.round(
|
||||
totals.find(
|
||||
(total) =>
|
||||
total[fieldTypeToCheck] ===
|
||||
getFieldValue("cost_center")
|
||||
)?.difference * 10
|
||||
) / 10;
|
||||
|
||||
if (value > costCenterDiff)
|
||||
return Promise.reject(
|
||||
t(
|
||||
"timetickets.validation.hoursenteredmorethanavailable"
|
||||
)
|
||||
);
|
||||
else {
|
||||
return Promise.resolve();
|
||||
}
|
||||
},
|
||||
}),
|
||||
{
|
||||
required:
|
||||
form.getFieldValue("cost_center") !==
|
||||
@@ -290,23 +341,28 @@ export function TimeTicketModalComponent({
|
||||
</Form.Item>
|
||||
</LayoutFormRow>
|
||||
<Form.Item dependencies={["jobid"]}>
|
||||
{() => (
|
||||
<LaborAllocationContainer
|
||||
jobid={form.getFieldValue("jobid") || null}
|
||||
/>
|
||||
)}
|
||||
{() => {
|
||||
const jobid = form.getFieldValue("jobid");
|
||||
if (
|
||||
(!called && jobid) ||
|
||||
(jobid && lineTicketData?.jobs_by_pk?.id !== jobid && !loading)
|
||||
) {
|
||||
loadLineTicketData({ variables: { id: jobid } });
|
||||
}
|
||||
return (
|
||||
<LaborAllocationContainer
|
||||
jobid={jobid || null}
|
||||
loading={loading}
|
||||
lineTicketData={lineTicketData}
|
||||
/>
|
||||
);
|
||||
}}
|
||||
</Form.Item>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export function LaborAllocationContainer({ jobid }) {
|
||||
const { loading, data: lineTicketData } = useQuery(GET_LINE_TICKET_BY_PK, {
|
||||
variables: { id: jobid },
|
||||
skip: !jobid,
|
||||
fetchPolicy: "network-only",
|
||||
nextFetchPolicy: "network-only",
|
||||
});
|
||||
export function LaborAllocationContainer({ jobid, loading, lineTicketData }) {
|
||||
if (loading) return <LoadingSkeleton />;
|
||||
if (!lineTicketData) return null;
|
||||
return (
|
||||
|
||||
@@ -1,16 +1,41 @@
|
||||
import React, { useState } from "react";
|
||||
import { Button, Form, notification, PageHeader } from "antd";
|
||||
import { Button, Form, notification, PageHeader, Popconfirm } from "antd";
|
||||
import { useMutation } from "@apollo/client";
|
||||
import VehicleDetailFormComponent from "./vehicle-detail-form.component";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import moment from "moment";
|
||||
import { UPDATE_VEHICLE } from "../../graphql/vehicles.queries";
|
||||
import { DELETE_VEHICLE, UPDATE_VEHICLE } from "../../graphql/vehicles.queries";
|
||||
import { useHistory } from "react-router-dom";
|
||||
|
||||
function VehicleDetailFormContainer({ vehicle, refetch }) {
|
||||
const { t } = useTranslation();
|
||||
const [updateVehicle] = useMutation(UPDATE_VEHICLE);
|
||||
const [deleteVehicle] = useMutation(DELETE_VEHICLE);
|
||||
const [form] = Form.useForm();
|
||||
const [loading, setLoading] = useState(false);
|
||||
const history = useHistory();
|
||||
|
||||
const handleDelete = async () => {
|
||||
setLoading(true);
|
||||
const result = await deleteVehicle({
|
||||
variables: { id: vehicle.id },
|
||||
});
|
||||
console.log(result);
|
||||
if (result.errors) {
|
||||
notification["error"]({
|
||||
message: t("vehicles.errors.deleting", {
|
||||
error: JSON.stringify(result.errors),
|
||||
}),
|
||||
});
|
||||
setLoading(false);
|
||||
} else {
|
||||
notification["success"]({
|
||||
message: t("vehicles.successes.delete"),
|
||||
});
|
||||
setLoading(false);
|
||||
history.push(`/manage/vehicles`);
|
||||
}
|
||||
};
|
||||
|
||||
const handleFinish = async (values) => {
|
||||
setLoading(true);
|
||||
@@ -40,15 +65,29 @@ function VehicleDetailFormContainer({ vehicle, refetch }) {
|
||||
<>
|
||||
<PageHeader
|
||||
title={t("menus.header.vehicles")}
|
||||
extra={
|
||||
extra={[
|
||||
<Popconfirm
|
||||
trigger="click"
|
||||
onConfirm={handleDelete}
|
||||
disabled={vehicle.jobs.length !== 0}
|
||||
title={t("vehicles.labels.deleteconfirm")}
|
||||
>
|
||||
<Button
|
||||
type="danger"
|
||||
loading={loading}
|
||||
disabled={vehicle.jobs.length !== 0}
|
||||
>
|
||||
{t("general.actions.delete")}
|
||||
</Button>
|
||||
</Popconfirm>,
|
||||
<Button
|
||||
type="primary"
|
||||
loading={loading}
|
||||
onClick={() => form.submit()}
|
||||
>
|
||||
{t("general.actions.save")}
|
||||
</Button>
|
||||
}
|
||||
</Button>,
|
||||
]}
|
||||
/>
|
||||
<Form
|
||||
onFinish={handleFinish}
|
||||
|
||||
@@ -268,6 +268,7 @@ export const CANCEL_APPOINTMENTS_BY_JOB_ID = gql`
|
||||
scheduled_in
|
||||
scheduled_completion
|
||||
status
|
||||
lost_sale_reason
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
@@ -69,6 +69,7 @@ export const QUERY_BILLS_BY_JOBID = gql`
|
||||
vendor {
|
||||
id
|
||||
name
|
||||
email
|
||||
}
|
||||
order_date
|
||||
deliver_by
|
||||
@@ -104,6 +105,7 @@ export const QUERY_BILLS_BY_JOBID = gql`
|
||||
vendor {
|
||||
id
|
||||
name
|
||||
email
|
||||
}
|
||||
total
|
||||
invoice_number
|
||||
|
||||
@@ -113,6 +113,11 @@ export const QUERY_BODYSHOP = gql`
|
||||
localmediaservernetwork
|
||||
localmediatoken
|
||||
enforce_conversion_csr
|
||||
md_lost_sale_reasons
|
||||
md_parts_scan
|
||||
enforce_conversion_category
|
||||
tt_enforce_hours_for_tech_console
|
||||
use_paint_scale_data
|
||||
employees {
|
||||
user_email
|
||||
id
|
||||
@@ -122,6 +127,7 @@ export const QUERY_BODYSHOP = gql`
|
||||
employee_number
|
||||
rates
|
||||
external_id
|
||||
flat_rate
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -223,6 +229,10 @@ export const UPDATE_SHOP = gql`
|
||||
localmediaservernetwork
|
||||
localmediatoken
|
||||
enforce_conversion_csr
|
||||
md_lost_sale_reasons
|
||||
md_parts_scan
|
||||
enforce_conversion_category
|
||||
tt_enforce_hours_for_tech_console
|
||||
employees {
|
||||
id
|
||||
first_name
|
||||
|
||||
@@ -284,6 +284,7 @@ export const QUERY_JOBS_IN_PRODUCTION = gql`
|
||||
clm_no
|
||||
v_make_desc
|
||||
v_color
|
||||
vehicleid
|
||||
plate_no
|
||||
actual_in
|
||||
scheduled_completion
|
||||
@@ -536,6 +537,7 @@ export const GET_JOB_BY_PK = gql`
|
||||
driveable
|
||||
towin
|
||||
loss_of_use
|
||||
lost_sale_reason
|
||||
vehicle {
|
||||
id
|
||||
plate_no
|
||||
@@ -721,6 +723,7 @@ export const GET_JOB_BY_PK = gql`
|
||||
ioucreated
|
||||
convertedtolbr
|
||||
ah_detail_line
|
||||
critical
|
||||
billlines(limit: 1, order_by: { bill: { date: desc } }) {
|
||||
id
|
||||
quantity
|
||||
@@ -1275,7 +1278,7 @@ export const SEARCH_JOBS_BY_ID_FOR_AUTOCOMPLETE = gql`
|
||||
|
||||
export const SEARCH_FOR_JOBS = gql`
|
||||
query SEARCH_FOR_JOBS($search: String!) {
|
||||
search_jobs(args: { search: $search }) {
|
||||
search_jobs(args: { search: $search }, limit: 25) {
|
||||
id
|
||||
ro_number
|
||||
ownr_fn
|
||||
|
||||
@@ -4,7 +4,15 @@ export const INSERT_NEW_NOTE = gql`
|
||||
mutation INSERT_NEW_NOTE($noteInput: [notes_insert_input!]!) {
|
||||
insert_notes(objects: $noteInput) {
|
||||
returning {
|
||||
created_at
|
||||
created_by
|
||||
critical
|
||||
id
|
||||
jobid
|
||||
private
|
||||
text
|
||||
updated_at
|
||||
audit
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -15,8 +23,8 @@ export const QUERY_NOTES_BY_JOB_PK = gql`
|
||||
jobs_by_pk(id: $id) {
|
||||
id
|
||||
ro_number
|
||||
vehicle{
|
||||
jobs{
|
||||
vehicle {
|
||||
jobs {
|
||||
id
|
||||
ro_number
|
||||
status
|
||||
|
||||
@@ -94,6 +94,14 @@ export const UPDATE_OWNER = gql`
|
||||
}
|
||||
`;
|
||||
|
||||
export const DELETE_OWNER = gql`
|
||||
mutation DELETE_OWNER($id: uuid!) {
|
||||
delete_owners_by_pk(id: $id) {
|
||||
id
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
export const QUERY_ALL_OWNERS = gql`
|
||||
query QUERY_ALL_OWNERS {
|
||||
owners {
|
||||
|
||||
@@ -19,6 +19,7 @@ export const QUERY_SCOREBOARD = gql`
|
||||
v_make_desc
|
||||
v_model_desc
|
||||
v_model_yr
|
||||
job_totals
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,22 +2,20 @@ import { gql } from "@apollo/client";
|
||||
|
||||
export const GLOBAL_SEARCH_QUERY = gql`
|
||||
query GLOBAL_SEARCH_QUERY($search: String) {
|
||||
search_jobs(args: { search: $search }) {
|
||||
search_jobs(args: { search: $search }, limit: 25) {
|
||||
id
|
||||
ro_number
|
||||
status
|
||||
|
||||
clm_no
|
||||
v_model_yr
|
||||
v_model_desc
|
||||
v_make_desc
|
||||
v_color
|
||||
|
||||
ownr_fn
|
||||
ownr_ln
|
||||
ownr_co_nm
|
||||
}
|
||||
search_owners(args: { search: $search }) {
|
||||
search_owners(args: { search: $search }, limit: 25) {
|
||||
id
|
||||
ownr_fn
|
||||
ownr_ln
|
||||
@@ -25,7 +23,7 @@ export const GLOBAL_SEARCH_QUERY = gql`
|
||||
ownr_ph1
|
||||
ownr_ph2
|
||||
}
|
||||
search_vehicles(args: { search: $search }) {
|
||||
search_vehicles(args: { search: $search }, limit: 25) {
|
||||
id
|
||||
v_model_yr
|
||||
v_model_desc
|
||||
@@ -34,7 +32,7 @@ export const GLOBAL_SEARCH_QUERY = gql`
|
||||
v_vin
|
||||
plate_no
|
||||
}
|
||||
search_payments(args: { search: $search }) {
|
||||
search_payments(args: { search: $search }, limit: 25) {
|
||||
id
|
||||
amount
|
||||
paymentnum
|
||||
@@ -45,7 +43,7 @@ export const GLOBAL_SEARCH_QUERY = gql`
|
||||
memo
|
||||
transactionid
|
||||
}
|
||||
search_bills(args: { search: $search }) {
|
||||
search_bills(args: { search: $search }, limit: 25) {
|
||||
id
|
||||
date
|
||||
invoice_number
|
||||
@@ -54,7 +52,7 @@ export const GLOBAL_SEARCH_QUERY = gql`
|
||||
name
|
||||
}
|
||||
}
|
||||
search_phonebook(args: { search: $search }) {
|
||||
search_phonebook(args: { search: $search }, limit: 25) {
|
||||
id
|
||||
firstname
|
||||
lastname
|
||||
|
||||
@@ -59,6 +59,81 @@ export const QUERY_TIME_TICKETS_IN_RANGE = gql`
|
||||
}
|
||||
`;
|
||||
|
||||
export const QUERY_TIME_TICKETS_TECHNICIAN_IN_RANGE = gql`
|
||||
query QUERY_TIME_TICKETS_TECHNICIAN_IN_RANGE(
|
||||
$employeeid: uuid!
|
||||
$start: date!
|
||||
$end: date!
|
||||
$fixedStart: date!
|
||||
$fixedEnd: date!
|
||||
) {
|
||||
timetickets(
|
||||
where: {
|
||||
date: { _gte: $start, _lte: $end }
|
||||
employeeid: { _eq: $employeeid }
|
||||
}
|
||||
order_by: { date: desc_nulls_first }
|
||||
) {
|
||||
actualhrs
|
||||
ciecacode
|
||||
clockoff
|
||||
clockon
|
||||
cost_center
|
||||
created_at
|
||||
date
|
||||
id
|
||||
rate
|
||||
productivehrs
|
||||
memo
|
||||
jobid
|
||||
flat_rate
|
||||
job {
|
||||
id
|
||||
ro_number
|
||||
}
|
||||
employeeid
|
||||
employee {
|
||||
id
|
||||
employee_number
|
||||
first_name
|
||||
last_name
|
||||
}
|
||||
}
|
||||
fixedperiod: timetickets(
|
||||
where: {
|
||||
date: { _gte: $fixedStart, _lte: $fixedEnd }
|
||||
employeeid: { _eq: $employeeid }
|
||||
}
|
||||
order_by: { date: desc_nulls_first }
|
||||
) {
|
||||
actualhrs
|
||||
ciecacode
|
||||
clockoff
|
||||
clockon
|
||||
cost_center
|
||||
created_at
|
||||
date
|
||||
id
|
||||
rate
|
||||
productivehrs
|
||||
memo
|
||||
jobid
|
||||
flat_rate
|
||||
job {
|
||||
id
|
||||
ro_number
|
||||
}
|
||||
employeeid
|
||||
employee {
|
||||
id
|
||||
employee_number
|
||||
first_name
|
||||
last_name
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
export const QUERY_TIME_TICKETS_IN_RANGE_SB = gql`
|
||||
query QUERY_TIME_TICKETS_IN_RANGE_SB(
|
||||
$start: date!
|
||||
|
||||
@@ -55,6 +55,14 @@ export const UPDATE_VEHICLE = gql`
|
||||
}
|
||||
`;
|
||||
|
||||
export const DELETE_VEHICLE = gql`
|
||||
mutation DELETE_VEHICLE($id: uuid!) {
|
||||
delete_vehicles_by_pk(id: $id) {
|
||||
id
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
export const QUERY_ALL_VEHICLES = gql`
|
||||
query QUERY_ALL_VEHICLES {
|
||||
vehicles {
|
||||
|
||||
@@ -39,7 +39,7 @@ export function AllJobs({ setBreadcrumbs, setSelectedHeader }) {
|
||||
...(statusFilters ? { statusList: JSON.parse(statusFilters) } : {}),
|
||||
order: [
|
||||
{
|
||||
[sortcolumn || "created_at"]:
|
||||
[sortcolumn || "ro_number"]:
|
||||
sortorder && sortorder !== "false"
|
||||
? sortorder === "descend"
|
||||
? "desc"
|
||||
|
||||
@@ -52,6 +52,7 @@ import AuditTrailMapping from "../../utils/AuditTrailMappings";
|
||||
import { insertAuditTrail } from "../../redux/application/application.actions";
|
||||
import JobsDocumentsLocalGallery from "../../components/jobs-documents-local-gallery/jobs-documents-local-gallery.container";
|
||||
import UndefinedToNull from "../../utils/undefinedtonull";
|
||||
import NoteUpsertModalComponent from "../../components/note-upsert-modal/note-upsert-modal.container";
|
||||
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
bodyshop: selectBodyshop,
|
||||
@@ -97,7 +98,11 @@ export function JobsDetailPage({
|
||||
variables: {
|
||||
jobId: job.id,
|
||||
job: {
|
||||
...UndefinedToNull(values, ["alt_transport", "category", "referral_source"]),
|
||||
...UndefinedToNull(values, [
|
||||
"alt_transport",
|
||||
"category",
|
||||
"referral_source",
|
||||
]),
|
||||
parts_tax_rates: {
|
||||
...job.parts_tax_rates,
|
||||
...values.parts_tax_rates,
|
||||
@@ -231,6 +236,7 @@ export function JobsDetailPage({
|
||||
<ScheduleJobModalContainer />
|
||||
<JobReconciliationModal />
|
||||
<JobLineUpsertModalContainer />
|
||||
<NoteUpsertModalComponent />
|
||||
<Form
|
||||
form={form}
|
||||
name="JobDetailForm"
|
||||
|
||||
@@ -2,10 +2,12 @@ import { Divider } from "antd";
|
||||
import React from "react";
|
||||
import TechClockInFormContainer from "../../components/tech-job-clock-in-form/tech-job-clock-in-form.container";
|
||||
import TechClockedInList from "../../components/tech-job-clocked-in-list/tech-job-clocked-in-list.component";
|
||||
import TechJobStatistics from "../../components/tech-job-statistics/tech-job-statistics.component";
|
||||
|
||||
export default function TechClockComponent() {
|
||||
return (
|
||||
<div>
|
||||
<TechJobStatistics />
|
||||
<TechClockInFormContainer />
|
||||
<Divider />
|
||||
<TechClockedInList />
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
.tech-content-container {
|
||||
overflow-y: auto;
|
||||
overflow-y: visible;
|
||||
padding: 1rem;
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
.tech-layout-container {
|
||||
height: 100vh;
|
||||
position: relative;
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
@@ -230,6 +230,7 @@
|
||||
"addapptcolor": "Add Appointment Color",
|
||||
"addbucket": "Add Definition",
|
||||
"addpartslocation": "Add Parts Location",
|
||||
"addpartsrule": "Add Parts Scan Rule",
|
||||
"addspeedprint": "Add Speed Print",
|
||||
"addtemplate": "Add Template",
|
||||
"newlaborrate": "New Labor Rate",
|
||||
@@ -282,6 +283,7 @@
|
||||
},
|
||||
"email": "General Shop Email",
|
||||
"enforce_class": "Enforce Class on Conversion?",
|
||||
"enforce_conversion_category": "Enforce Category on Conversion?",
|
||||
"enforce_conversion_csr": "Enforce CSR on Conversion?",
|
||||
"enforce_referral": "Enforce Referrals",
|
||||
"federal_tax_id": "Federal Tax ID (GST/HST)",
|
||||
@@ -329,7 +331,12 @@
|
||||
"zip": "Zip/Postal Code"
|
||||
},
|
||||
"md_jobline_presets": "Jobline Presets",
|
||||
"md_lost_sale_reasons": "Lost Sale Reasons",
|
||||
"md_parts_order_comment": "Parts Orders Comments",
|
||||
"md_parts_scan": {
|
||||
"expression": "RegEX Expression",
|
||||
"flags": "Flags"
|
||||
},
|
||||
"md_payment_types": "Payment Types",
|
||||
"md_referral_sources": "Referral Sources",
|
||||
"messaginglabel": "Messaging Preset Label",
|
||||
@@ -534,7 +541,9 @@
|
||||
"target_touchtime": "Target Touch Time",
|
||||
"timezone": "Timezone",
|
||||
"tt_allow_post_to_invoiced": "Allow Time Tickets to be posted to Invoiced & Exported Jobs",
|
||||
"tt_enforce_hours_for_tech_console": "Restrict Claimable hours from Tech Console",
|
||||
"use_fippa": "Use FIPPA for Names on Generated Documents?",
|
||||
"use_paint_scale_data": "Use Paint Scale Data for Job Costing?",
|
||||
"uselocalmediaserver": "Use Local Media Server?",
|
||||
"website": "Website",
|
||||
"zip_post": "Zip/Postal Code"
|
||||
@@ -580,6 +589,7 @@
|
||||
"notespresets": "Notes Presets",
|
||||
"orderstatuses": "Order Statuses",
|
||||
"partslocations": "Parts Locations",
|
||||
"partsscan": "Critical Parts Scanning",
|
||||
"printlater": "Print Later",
|
||||
"qbo": "Use QuickBooks Online?",
|
||||
"qbo_departmentid": "QBO Department ID",
|
||||
@@ -1006,6 +1016,7 @@
|
||||
"created_at": "Created At",
|
||||
"email": "Email",
|
||||
"errors": "Errors",
|
||||
"excel": "Excel",
|
||||
"exceptiontitle": "An error has occurred.",
|
||||
"friday": "Friday",
|
||||
"globalsearch": "Global Search",
|
||||
@@ -1055,6 +1066,7 @@
|
||||
"sunday": "Sunday",
|
||||
"text": "Text",
|
||||
"thursday": "Thursday",
|
||||
"total": "Total",
|
||||
"totals": "Totals",
|
||||
"tuesday": "Tuesday",
|
||||
"unknown": "Unknown",
|
||||
@@ -1273,6 +1285,7 @@
|
||||
"removefromproduction": "Remove from Production",
|
||||
"schedule": "Schedule",
|
||||
"sendcsi": "Send CSI",
|
||||
"sendpartspricechange": "",
|
||||
"sendtodms": "Send to DMS",
|
||||
"sync": "Sync",
|
||||
"uninvoice": "Uninvoice",
|
||||
@@ -1296,6 +1309,7 @@
|
||||
"nojobselected": "No job is selected.",
|
||||
"noowner": "No owner associated.",
|
||||
"novehicle": "No vehicle associated.",
|
||||
"partspricechange": "",
|
||||
"saving": "Error encountered while saving record.",
|
||||
"scanimport": "Error importing job. {{message}}",
|
||||
"totalscalc": "Error while calculating new job totals.",
|
||||
@@ -1447,6 +1461,7 @@
|
||||
"loss_date": "Loss Date",
|
||||
"loss_desc": "Loss Description",
|
||||
"loss_of_use": "Loss of Use",
|
||||
"lost_sale_reason": "Lost Sale Reason",
|
||||
"ma2s": "2 Stage Paint",
|
||||
"ma3s": "3 Stage Pain",
|
||||
"mabl": "MABL?",
|
||||
@@ -1947,7 +1962,8 @@
|
||||
"actions": "Actions",
|
||||
"deletenote": "Delete Note",
|
||||
"edit": "Edit Note",
|
||||
"new": "New Note"
|
||||
"new": "New Note",
|
||||
"savetojobnotes": "Save to Job Notes"
|
||||
},
|
||||
"errors": {
|
||||
"inserting": "Error inserting note. {{error}}"
|
||||
@@ -1982,6 +1998,7 @@
|
||||
"update": "Update Selected Records"
|
||||
},
|
||||
"errors": {
|
||||
"deleting": "Error deleting owner. {{error}}.",
|
||||
"noaccess": "The record does not exist or you do not have access to it. ",
|
||||
"saving": "Error saving owner. {{error}}.",
|
||||
"selectexistingornew": "Select an existing owner record or create a new one. "
|
||||
@@ -2014,6 +2031,7 @@
|
||||
},
|
||||
"labels": {
|
||||
"create_new": "Create a new owner record.",
|
||||
"deleteconfirm": "Are you sure you want to delete this owner? This cannot be undone.",
|
||||
"existing_owners": "Existing Owners",
|
||||
"fromclaim": "Current Claim",
|
||||
"fromowner": "Historical Owner Record",
|
||||
@@ -2021,6 +2039,7 @@
|
||||
"updateowner": "Update Owner"
|
||||
},
|
||||
"successes": {
|
||||
"delete": "Owner deleted successfully.",
|
||||
"save": "Owner saved successfully."
|
||||
}
|
||||
},
|
||||
@@ -2210,6 +2229,7 @@
|
||||
"csi_invitation": "CSI Invitation",
|
||||
"csi_invitation_action": "CSI Invite",
|
||||
"diagnostic_authorization": "Diagnostic Authorization",
|
||||
"dms_posting_sheet": "DMS Posting Sheet",
|
||||
"envelope_return_address": "#10 Envelope Return Address Label",
|
||||
"estimate": "Estimate Only",
|
||||
"estimate_detail": "Estimate Details",
|
||||
@@ -2420,6 +2440,7 @@
|
||||
"credits_not_received_date": "Credits not Received by Date",
|
||||
"credits_not_received_date_vendorid": "Credits not Received by Vendor",
|
||||
"csi": "CSI Responses",
|
||||
"customer_list": "Customer List",
|
||||
"cycle_time_analysis": "Cycle Time Analysis",
|
||||
"estimates_written_converted": "Estimates Written/Converted",
|
||||
"estimator_detail": "Jobs by Estimator (Detail)",
|
||||
@@ -2427,6 +2448,8 @@
|
||||
"export_payables": "Export Log - Payables",
|
||||
"export_payments": "Export Log - Payments",
|
||||
"export_receivables": "Export Log - Receivables",
|
||||
"exported_gsr_by_ro": "Exported Gross Sales - Excel",
|
||||
"exported_gsr_by_ro_labor": "Exported Gross Sales (Labor) - Excel",
|
||||
"gsr_by_atp": "",
|
||||
"gsr_by_ats": "Gross Sales by ATS",
|
||||
"gsr_by_category": "Gross Sales by Category",
|
||||
@@ -2519,6 +2542,7 @@
|
||||
"labels": {
|
||||
"atssummary": "ATS Summary",
|
||||
"employeevacation": "Employee Vacations",
|
||||
"ins_co_nm_filter": "Filter by Insurance Company",
|
||||
"intake": "Intake Events",
|
||||
"manual": "Manual Events",
|
||||
"manualevent": "Add Manual Event"
|
||||
@@ -2645,7 +2669,8 @@
|
||||
},
|
||||
"validation": {
|
||||
"clockoffmustbeafterclockon": "Clock off time must be the same or after clock in time.",
|
||||
"clockoffwithoutclockon": "Clock off time cannot be set without a clock in time."
|
||||
"clockoffwithoutclockon": "Clock off time cannot be set without a clock in time.",
|
||||
"hoursenteredmorethanavailable": "The number of hours entered is more than what is available for this cost center."
|
||||
}
|
||||
},
|
||||
"titles": {
|
||||
@@ -2779,6 +2804,7 @@
|
||||
},
|
||||
"vehicles": {
|
||||
"errors": {
|
||||
"deleting": "Error deleting vehicle. {{error}}.",
|
||||
"noaccess": "The vehicle does not exist or you do not have access to it.",
|
||||
"selectexistingornew": "Select an existing vehicle record or create a new one. ",
|
||||
"validation": "Please ensure all fields are entered correctly.",
|
||||
@@ -2814,12 +2840,14 @@
|
||||
"registration": "Registration"
|
||||
},
|
||||
"labels": {
|
||||
"deleteconfirm": "Are you sure you want to delete this vehicle? This cannot be undone.",
|
||||
"fromvehicle": "Historical Vehicle Record",
|
||||
"novehinfo": "No Vehicle Information",
|
||||
"relatedjobs": "Related Jobs",
|
||||
"updatevehicle": "Update Vehicle Information"
|
||||
},
|
||||
"successes": {
|
||||
"delete": "Vehicle deleted successfully.",
|
||||
"save": "Vehicle saved successfully."
|
||||
}
|
||||
},
|
||||
|
||||
@@ -230,6 +230,7 @@
|
||||
"addapptcolor": "",
|
||||
"addbucket": "",
|
||||
"addpartslocation": "",
|
||||
"addpartsrule": "",
|
||||
"addspeedprint": "",
|
||||
"addtemplate": "",
|
||||
"newlaborrate": "",
|
||||
@@ -282,6 +283,7 @@
|
||||
},
|
||||
"email": "",
|
||||
"enforce_class": "",
|
||||
"enforce_conversion_category": "",
|
||||
"enforce_conversion_csr": "",
|
||||
"enforce_referral": "",
|
||||
"federal_tax_id": "",
|
||||
@@ -329,7 +331,12 @@
|
||||
"zip": ""
|
||||
},
|
||||
"md_jobline_presets": "",
|
||||
"md_lost_sale_reasons": "",
|
||||
"md_parts_order_comment": "",
|
||||
"md_parts_scan": {
|
||||
"expression": "",
|
||||
"flags": ""
|
||||
},
|
||||
"md_payment_types": "",
|
||||
"md_referral_sources": "",
|
||||
"messaginglabel": "",
|
||||
@@ -534,7 +541,9 @@
|
||||
"target_touchtime": "",
|
||||
"timezone": "",
|
||||
"tt_allow_post_to_invoiced": "",
|
||||
"tt_enforce_hours_for_tech_console": "",
|
||||
"use_fippa": "",
|
||||
"use_paint_scale_data": "",
|
||||
"uselocalmediaserver": "",
|
||||
"website": "",
|
||||
"zip_post": ""
|
||||
@@ -580,6 +589,7 @@
|
||||
"notespresets": "",
|
||||
"orderstatuses": "",
|
||||
"partslocations": "",
|
||||
"partsscan": "",
|
||||
"printlater": "",
|
||||
"qbo": "",
|
||||
"qbo_departmentid": "",
|
||||
@@ -1006,6 +1016,7 @@
|
||||
"created_at": "",
|
||||
"email": "",
|
||||
"errors": "",
|
||||
"excel": "",
|
||||
"exceptiontitle": "",
|
||||
"friday": "",
|
||||
"globalsearch": "",
|
||||
@@ -1055,6 +1066,7 @@
|
||||
"sunday": "",
|
||||
"text": "",
|
||||
"thursday": "",
|
||||
"total": "",
|
||||
"totals": "",
|
||||
"tuesday": "",
|
||||
"unknown": "Desconocido",
|
||||
@@ -1273,6 +1285,7 @@
|
||||
"removefromproduction": "",
|
||||
"schedule": "Programar",
|
||||
"sendcsi": "",
|
||||
"sendpartspricechange": "",
|
||||
"sendtodms": "",
|
||||
"sync": "",
|
||||
"uninvoice": "",
|
||||
@@ -1296,6 +1309,7 @@
|
||||
"nojobselected": "No hay trabajo seleccionado.",
|
||||
"noowner": "Ningún propietario asociado.",
|
||||
"novehicle": "No hay vehículo asociado.",
|
||||
"partspricechange": "",
|
||||
"saving": "Se encontró un error al guardar el registro.",
|
||||
"scanimport": "",
|
||||
"totalscalc": "",
|
||||
@@ -1447,6 +1461,7 @@
|
||||
"loss_date": "Fecha de pérdida",
|
||||
"loss_desc": "",
|
||||
"loss_of_use": "",
|
||||
"lost_sale_reason": "",
|
||||
"ma2s": "",
|
||||
"ma3s": "",
|
||||
"mabl": "",
|
||||
@@ -1947,7 +1962,8 @@
|
||||
"actions": "Comportamiento",
|
||||
"deletenote": "Borrar nota",
|
||||
"edit": "Editar nota",
|
||||
"new": "Nueva nota"
|
||||
"new": "Nueva nota",
|
||||
"savetojobnotes": ""
|
||||
},
|
||||
"errors": {
|
||||
"inserting": ""
|
||||
@@ -1982,6 +1998,7 @@
|
||||
"update": ""
|
||||
},
|
||||
"errors": {
|
||||
"deleting": "",
|
||||
"noaccess": "El registro no existe o no tiene acceso a él.",
|
||||
"saving": "",
|
||||
"selectexistingornew": ""
|
||||
@@ -2014,6 +2031,7 @@
|
||||
},
|
||||
"labels": {
|
||||
"create_new": "Crea un nuevo registro de propietario.",
|
||||
"deleteconfirm": "",
|
||||
"existing_owners": "Propietarios existentes",
|
||||
"fromclaim": "",
|
||||
"fromowner": "",
|
||||
@@ -2021,6 +2039,7 @@
|
||||
"updateowner": ""
|
||||
},
|
||||
"successes": {
|
||||
"delete": "",
|
||||
"save": "Propietario guardado con éxito."
|
||||
}
|
||||
},
|
||||
@@ -2210,6 +2229,7 @@
|
||||
"csi_invitation": "",
|
||||
"csi_invitation_action": "",
|
||||
"diagnostic_authorization": "",
|
||||
"dms_posting_sheet": "",
|
||||
"envelope_return_address": "",
|
||||
"estimate": "",
|
||||
"estimate_detail": "",
|
||||
@@ -2420,6 +2440,7 @@
|
||||
"credits_not_received_date": "",
|
||||
"credits_not_received_date_vendorid": "",
|
||||
"csi": "",
|
||||
"customer_list": "",
|
||||
"cycle_time_analysis": "",
|
||||
"estimates_written_converted": "",
|
||||
"estimator_detail": "",
|
||||
@@ -2427,6 +2448,8 @@
|
||||
"export_payables": "",
|
||||
"export_payments": "",
|
||||
"export_receivables": "",
|
||||
"exported_gsr_by_ro": "",
|
||||
"exported_gsr_by_ro_labor": "",
|
||||
"gsr_by_atp": "",
|
||||
"gsr_by_ats": "",
|
||||
"gsr_by_category": "",
|
||||
@@ -2519,6 +2542,7 @@
|
||||
"labels": {
|
||||
"atssummary": "",
|
||||
"employeevacation": "",
|
||||
"ins_co_nm_filter": "",
|
||||
"intake": "",
|
||||
"manual": "",
|
||||
"manualevent": ""
|
||||
@@ -2645,7 +2669,8 @@
|
||||
},
|
||||
"validation": {
|
||||
"clockoffmustbeafterclockon": "",
|
||||
"clockoffwithoutclockon": ""
|
||||
"clockoffwithoutclockon": "",
|
||||
"hoursenteredmorethanavailable": ""
|
||||
}
|
||||
},
|
||||
"titles": {
|
||||
@@ -2779,6 +2804,7 @@
|
||||
},
|
||||
"vehicles": {
|
||||
"errors": {
|
||||
"deleting": "",
|
||||
"noaccess": "El vehículo no existe o usted no tiene acceso a él.",
|
||||
"selectexistingornew": "",
|
||||
"validation": "Asegúrese de que todos los campos se ingresen correctamente.",
|
||||
@@ -2814,12 +2840,14 @@
|
||||
"registration": ""
|
||||
},
|
||||
"labels": {
|
||||
"deleteconfirm": "",
|
||||
"fromvehicle": "",
|
||||
"novehinfo": "",
|
||||
"relatedjobs": "",
|
||||
"updatevehicle": ""
|
||||
},
|
||||
"successes": {
|
||||
"delete": "",
|
||||
"save": "Vehículo guardado con éxito."
|
||||
}
|
||||
},
|
||||
|
||||
@@ -230,6 +230,7 @@
|
||||
"addapptcolor": "",
|
||||
"addbucket": "",
|
||||
"addpartslocation": "",
|
||||
"addpartsrule": "",
|
||||
"addspeedprint": "",
|
||||
"addtemplate": "",
|
||||
"newlaborrate": "",
|
||||
@@ -282,6 +283,7 @@
|
||||
},
|
||||
"email": "",
|
||||
"enforce_class": "",
|
||||
"enforce_conversion_category": "",
|
||||
"enforce_conversion_csr": "",
|
||||
"enforce_referral": "",
|
||||
"federal_tax_id": "",
|
||||
@@ -329,7 +331,12 @@
|
||||
"zip": ""
|
||||
},
|
||||
"md_jobline_presets": "",
|
||||
"md_lost_sale_reasons": "",
|
||||
"md_parts_order_comment": "",
|
||||
"md_parts_scan": {
|
||||
"expression": "",
|
||||
"flags": ""
|
||||
},
|
||||
"md_payment_types": "",
|
||||
"md_referral_sources": "",
|
||||
"messaginglabel": "",
|
||||
@@ -534,7 +541,9 @@
|
||||
"target_touchtime": "",
|
||||
"timezone": "",
|
||||
"tt_allow_post_to_invoiced": "",
|
||||
"tt_enforce_hours_for_tech_console": "",
|
||||
"use_fippa": "",
|
||||
"use_paint_scale_data": "",
|
||||
"uselocalmediaserver": "",
|
||||
"website": "",
|
||||
"zip_post": ""
|
||||
@@ -580,6 +589,7 @@
|
||||
"notespresets": "",
|
||||
"orderstatuses": "",
|
||||
"partslocations": "",
|
||||
"partsscan": "",
|
||||
"printlater": "",
|
||||
"qbo": "",
|
||||
"qbo_departmentid": "",
|
||||
@@ -1006,6 +1016,7 @@
|
||||
"created_at": "",
|
||||
"email": "",
|
||||
"errors": "",
|
||||
"excel": "",
|
||||
"exceptiontitle": "",
|
||||
"friday": "",
|
||||
"globalsearch": "",
|
||||
@@ -1055,6 +1066,7 @@
|
||||
"sunday": "",
|
||||
"text": "",
|
||||
"thursday": "",
|
||||
"total": "",
|
||||
"totals": "",
|
||||
"tuesday": "",
|
||||
"unknown": "Inconnu",
|
||||
@@ -1273,6 +1285,7 @@
|
||||
"removefromproduction": "",
|
||||
"schedule": "Programme",
|
||||
"sendcsi": "",
|
||||
"sendpartspricechange": "",
|
||||
"sendtodms": "",
|
||||
"sync": "",
|
||||
"uninvoice": "",
|
||||
@@ -1296,6 +1309,7 @@
|
||||
"nojobselected": "Aucun travail n'est sélectionné.",
|
||||
"noowner": "Aucun propriétaire associé.",
|
||||
"novehicle": "Aucun véhicule associé.",
|
||||
"partspricechange": "",
|
||||
"saving": "Erreur rencontrée lors de la sauvegarde de l'enregistrement.",
|
||||
"scanimport": "",
|
||||
"totalscalc": "",
|
||||
@@ -1447,6 +1461,7 @@
|
||||
"loss_date": "Date de perte",
|
||||
"loss_desc": "",
|
||||
"loss_of_use": "",
|
||||
"lost_sale_reason": "",
|
||||
"ma2s": "",
|
||||
"ma3s": "",
|
||||
"mabl": "",
|
||||
@@ -1947,7 +1962,8 @@
|
||||
"actions": "actes",
|
||||
"deletenote": "Supprimer la note",
|
||||
"edit": "Note éditée",
|
||||
"new": "Nouvelle note"
|
||||
"new": "Nouvelle note",
|
||||
"savetojobnotes": ""
|
||||
},
|
||||
"errors": {
|
||||
"inserting": ""
|
||||
@@ -1982,6 +1998,7 @@
|
||||
"update": ""
|
||||
},
|
||||
"errors": {
|
||||
"deleting": "",
|
||||
"noaccess": "L'enregistrement n'existe pas ou vous n'y avez pas accès.",
|
||||
"saving": "",
|
||||
"selectexistingornew": ""
|
||||
@@ -2014,6 +2031,7 @@
|
||||
},
|
||||
"labels": {
|
||||
"create_new": "Créez un nouvel enregistrement de propriétaire.",
|
||||
"deleteconfirm": "",
|
||||
"existing_owners": "Propriétaires existants",
|
||||
"fromclaim": "",
|
||||
"fromowner": "",
|
||||
@@ -2021,6 +2039,7 @@
|
||||
"updateowner": ""
|
||||
},
|
||||
"successes": {
|
||||
"delete": "",
|
||||
"save": "Le propriétaire a bien enregistré."
|
||||
}
|
||||
},
|
||||
@@ -2210,6 +2229,7 @@
|
||||
"csi_invitation": "",
|
||||
"csi_invitation_action": "",
|
||||
"diagnostic_authorization": "",
|
||||
"dms_posting_sheet": "",
|
||||
"envelope_return_address": "",
|
||||
"estimate": "",
|
||||
"estimate_detail": "",
|
||||
@@ -2420,6 +2440,7 @@
|
||||
"credits_not_received_date": "",
|
||||
"credits_not_received_date_vendorid": "",
|
||||
"csi": "",
|
||||
"customer_list": "",
|
||||
"cycle_time_analysis": "",
|
||||
"estimates_written_converted": "",
|
||||
"estimator_detail": "",
|
||||
@@ -2427,6 +2448,8 @@
|
||||
"export_payables": "",
|
||||
"export_payments": "",
|
||||
"export_receivables": "",
|
||||
"exported_gsr_by_ro": "",
|
||||
"exported_gsr_by_ro_labor": "",
|
||||
"gsr_by_atp": "",
|
||||
"gsr_by_ats": "",
|
||||
"gsr_by_category": "",
|
||||
@@ -2519,6 +2542,7 @@
|
||||
"labels": {
|
||||
"atssummary": "",
|
||||
"employeevacation": "",
|
||||
"ins_co_nm_filter": "",
|
||||
"intake": "",
|
||||
"manual": "",
|
||||
"manualevent": ""
|
||||
@@ -2645,7 +2669,8 @@
|
||||
},
|
||||
"validation": {
|
||||
"clockoffmustbeafterclockon": "",
|
||||
"clockoffwithoutclockon": ""
|
||||
"clockoffwithoutclockon": "",
|
||||
"hoursenteredmorethanavailable": ""
|
||||
}
|
||||
},
|
||||
"titles": {
|
||||
@@ -2779,6 +2804,7 @@
|
||||
},
|
||||
"vehicles": {
|
||||
"errors": {
|
||||
"deleting": "",
|
||||
"noaccess": "Le véhicule n'existe pas ou vous n'y avez pas accès.",
|
||||
"selectexistingornew": "",
|
||||
"validation": "Veuillez vous assurer que tous les champs sont correctement entrés.",
|
||||
@@ -2814,12 +2840,14 @@
|
||||
"registration": ""
|
||||
},
|
||||
"labels": {
|
||||
"deleteconfirm": "",
|
||||
"fromvehicle": "",
|
||||
"novehinfo": "",
|
||||
"relatedjobs": "",
|
||||
"updatevehicle": ""
|
||||
},
|
||||
"successes": {
|
||||
"delete": "",
|
||||
"save": "Le véhicule a été enregistré avec succès."
|
||||
}
|
||||
},
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import { gql } from "@apollo/client";
|
||||
import { notification } from "antd";
|
||||
import axios from "axios";
|
||||
import jsreport from "@jsreport/browser-client";
|
||||
import _ from "lodash";
|
||||
import moment from "moment";
|
||||
@@ -9,7 +8,8 @@ import { setEmailOptions } from "../redux/email/email.actions";
|
||||
import { store } from "../redux/store";
|
||||
import client from "../utils/GraphQLClient";
|
||||
import { TemplateList } from "./TemplateConstants";
|
||||
|
||||
import cleanAxios from "./CleanAxios";
|
||||
import axios from "axios";
|
||||
const server = process.env.REACT_APP_REPORTS_SERVER_URL;
|
||||
|
||||
jsreport.serverUrl = server;
|
||||
@@ -26,10 +26,14 @@ export default async function RenderTemplate(
|
||||
if (window.jsr3) {
|
||||
jsreport.serverUrl = "https://reports3.test.imex.online/";
|
||||
}
|
||||
const jsrAuth = (await axios.post("/utils/jsr")).data;
|
||||
|
||||
jsreport.headers["Authorization"] = jsrAuth;
|
||||
|
||||
//Query assets that match the template name. Must be in format <<templateName>>.query
|
||||
let { contextData, useShopSpecificTemplate } = await fetchContextData(
|
||||
templateObject
|
||||
templateObject,
|
||||
jsrAuth
|
||||
);
|
||||
|
||||
const { ignoreCustomMargins } = Templates[templateObject.name];
|
||||
@@ -137,11 +141,15 @@ export async function RenderTemplates(
|
||||
//Query assets that match the template name. Must be in format <<templateName>>.query
|
||||
let unsortedTemplatesAndData = [];
|
||||
let proms = [];
|
||||
const jsrAuth = (await axios.post("/utils/jsr")).data;
|
||||
jsreport.headers["Authorization"] = jsrAuth;
|
||||
|
||||
templateObjects.forEach((template) => {
|
||||
proms.push(
|
||||
(async () => {
|
||||
let { contextData, useShopSpecificTemplate } = await fetchContextData(
|
||||
template
|
||||
template,
|
||||
jsrAuth
|
||||
);
|
||||
unsortedTemplatesAndData.push({
|
||||
templateObject: template,
|
||||
@@ -298,19 +306,22 @@ export const GenerateDocuments = async (templates) => {
|
||||
await RenderTemplates(templates, bodyshop);
|
||||
};
|
||||
|
||||
const fetchContextData = async (templateObject) => {
|
||||
const fetchContextData = async (templateObject, jsrAuth) => {
|
||||
const bodyshop = store.getState().user.bodyshop;
|
||||
|
||||
jsreport.headers["Authorization"] =
|
||||
jsreport.headers["FirebaseAuthorization"] =
|
||||
"Bearer " + (await auth.currentUser.getIdToken());
|
||||
|
||||
const folders = await axios.get(`${server}/odata/folders`);
|
||||
const folders = await cleanAxios.get(`${server}/odata/folders`, {
|
||||
headers: { Authorization: jsrAuth },
|
||||
});
|
||||
const shopSpecificFolder = folders.data.value.find(
|
||||
(f) => f.name === bodyshop.imexshopid
|
||||
);
|
||||
|
||||
const jsReportQueries = await axios.get(
|
||||
`${server}/odata/assets?$filter=name eq '${templateObject.name}.query'`
|
||||
const jsReportQueries = await cleanAxios.get(
|
||||
`${server}/odata/assets?$filter=name eq '${templateObject.name}.query'`,
|
||||
{ headers: { Authorization: jsrAuth } }
|
||||
);
|
||||
|
||||
let templateQueryToExecute;
|
||||
|
||||
@@ -20,7 +20,6 @@ export const TemplateList = (type, context) => {
|
||||
disabled: false,
|
||||
group: "authorization",
|
||||
},
|
||||
|
||||
fippa_authorization: {
|
||||
title: i18n.t("printcenter.jobs.fippa_authorization"),
|
||||
description: "CASL Authorization",
|
||||
@@ -101,7 +100,6 @@ export const TemplateList = (type, context) => {
|
||||
disabled: false,
|
||||
group: "ro",
|
||||
},
|
||||
|
||||
job_notes: {
|
||||
title: i18n.t("printcenter.jobs.job_notes"),
|
||||
description: "All Jobs Notes",
|
||||
@@ -330,7 +328,6 @@ export const TemplateList = (type, context) => {
|
||||
disabled: false,
|
||||
group: "post",
|
||||
},
|
||||
|
||||
vehicle_delivery_check: {
|
||||
title: i18n.t("printcenter.jobs.vehicle_delivery_check"),
|
||||
description: "All Jobs Notes",
|
||||
@@ -339,7 +336,6 @@ export const TemplateList = (type, context) => {
|
||||
disabled: false,
|
||||
group: "post",
|
||||
},
|
||||
|
||||
guarantee: {
|
||||
title: i18n.t("printcenter.jobs.guarantee"),
|
||||
description: "All Jobs Notes",
|
||||
@@ -496,6 +492,14 @@ export const TemplateList = (type, context) => {
|
||||
disabled: false,
|
||||
group: "financial",
|
||||
},
|
||||
dms_posting_sheet: {
|
||||
title: i18n.t("printcenter.jobs.dms_posting_sheet"),
|
||||
description: "DMS Posting Sheet",
|
||||
subject: i18n.t("printcenter.jobs.dms_posting_sheet"),
|
||||
key: "dms_posting_sheet",
|
||||
disabled: false,
|
||||
group: "financial",
|
||||
},
|
||||
}
|
||||
: {}),
|
||||
...(!type || type === "job_special"
|
||||
@@ -622,7 +626,6 @@ export const TemplateList = (type, context) => {
|
||||
},
|
||||
group: "sales",
|
||||
},
|
||||
|
||||
hours_sold_detail_closed_ins_co: {
|
||||
title: i18n.t(
|
||||
"reportcenter.templates.hours_sold_detail_closed_ins_co"
|
||||
@@ -640,7 +643,6 @@ export const TemplateList = (type, context) => {
|
||||
},
|
||||
group: "sales",
|
||||
},
|
||||
|
||||
hours_sold_summary_closed: {
|
||||
title: i18n.t("reportcenter.templates.hours_sold_summary_closed"),
|
||||
description: "",
|
||||
@@ -654,7 +656,6 @@ export const TemplateList = (type, context) => {
|
||||
},
|
||||
group: "sales",
|
||||
},
|
||||
|
||||
hours_sold_summary_closed_ins_co: {
|
||||
title: i18n.t(
|
||||
"reportcenter.templates.hours_sold_summary_closed_ins_co"
|
||||
@@ -672,7 +673,6 @@ export const TemplateList = (type, context) => {
|
||||
},
|
||||
group: "sales",
|
||||
},
|
||||
|
||||
hours_sold_detail_open: {
|
||||
title: i18n.t("reportcenter.templates.hours_sold_detail_open"),
|
||||
description: "",
|
||||
@@ -686,7 +686,6 @@ export const TemplateList = (type, context) => {
|
||||
},
|
||||
group: "sales",
|
||||
},
|
||||
|
||||
hours_sold_detail_open_ins_co: {
|
||||
title: i18n.t(
|
||||
"reportcenter.templates.hours_sold_detail_open_ins_co"
|
||||
@@ -704,7 +703,6 @@ export const TemplateList = (type, context) => {
|
||||
},
|
||||
group: "sales",
|
||||
},
|
||||
|
||||
hours_sold_summary_open: {
|
||||
title: i18n.t("reportcenter.templates.hours_sold_summary_open"),
|
||||
description: "",
|
||||
@@ -718,7 +716,6 @@ export const TemplateList = (type, context) => {
|
||||
},
|
||||
group: "sales",
|
||||
},
|
||||
|
||||
hours_sold_summary_open_ins_co: {
|
||||
title: i18n.t(
|
||||
"reportcenter.templates.hours_sold_summary_open_ins_co"
|
||||
@@ -736,7 +733,6 @@ export const TemplateList = (type, context) => {
|
||||
},
|
||||
group: "sales",
|
||||
},
|
||||
|
||||
hours_sold_detail_closed_csr: {
|
||||
title: i18n.t(
|
||||
"reportcenter.templates.hours_sold_detail_closed_csr"
|
||||
@@ -1090,7 +1086,6 @@ export const TemplateList = (type, context) => {
|
||||
},
|
||||
group: "customers",
|
||||
},
|
||||
|
||||
schedule: {
|
||||
title: i18n.t("reportcenter.templates.schedule"),
|
||||
subject: i18n.t("reportcenter.templates.schedule"),
|
||||
@@ -1102,7 +1097,6 @@ export const TemplateList = (type, context) => {
|
||||
},
|
||||
group: "customers",
|
||||
},
|
||||
|
||||
timetickets: {
|
||||
title: i18n.t("reportcenter.templates.timetickets"),
|
||||
subject: i18n.t("reportcenter.templates.timetickets"),
|
||||
@@ -1126,7 +1120,6 @@ export const TemplateList = (type, context) => {
|
||||
title: i18n.t("reportcenter.templates.attendance_detail"),
|
||||
subject: i18n.t("reportcenter.templates.attendance_detail"),
|
||||
key: "attendance_detail",
|
||||
|
||||
disabled: false,
|
||||
rangeFilter: {
|
||||
object: i18n.t("reportcenter.labels.objects.timetickets"),
|
||||
@@ -1138,7 +1131,6 @@ export const TemplateList = (type, context) => {
|
||||
title: i18n.t("reportcenter.templates.attendance_summary"),
|
||||
subject: i18n.t("reportcenter.templates.attendance_summary"),
|
||||
key: "attendance_summary",
|
||||
|
||||
disabled: false,
|
||||
rangeFilter: {
|
||||
object: i18n.t("reportcenter.labels.objects.timetickets"),
|
||||
@@ -1158,7 +1150,6 @@ export const TemplateList = (type, context) => {
|
||||
},
|
||||
group: "payroll",
|
||||
},
|
||||
|
||||
timetickets_summary: {
|
||||
title: i18n.t("reportcenter.templates.timetickets_summary"),
|
||||
subject: i18n.t("reportcenter.templates.timetickets_summary"),
|
||||
@@ -1171,7 +1162,6 @@ export const TemplateList = (type, context) => {
|
||||
},
|
||||
group: "payroll",
|
||||
},
|
||||
|
||||
estimator_detail: {
|
||||
title: i18n.t("reportcenter.templates.estimator_detail"),
|
||||
description: "",
|
||||
@@ -1224,7 +1214,6 @@ export const TemplateList = (type, context) => {
|
||||
},
|
||||
group: "purchases",
|
||||
},
|
||||
|
||||
void_ros: {
|
||||
title: i18n.t("reportcenter.templates.void_ros"),
|
||||
description: "",
|
||||
@@ -1329,7 +1318,6 @@ export const TemplateList = (type, context) => {
|
||||
},
|
||||
group: "sales",
|
||||
},
|
||||
|
||||
gsr_by_estimator: {
|
||||
title: i18n.t("reportcenter.templates.gsr_by_estimator"),
|
||||
description: "",
|
||||
@@ -1814,7 +1802,9 @@ export const TemplateList = (type, context) => {
|
||||
group: "jobs",
|
||||
},
|
||||
purchase_return_ratio_grouped_by_vendor_detail: {
|
||||
title: i18n.t("reportcenter.templates.purchase_return_ratio_grouped_by_vendor_detail"),
|
||||
title: i18n.t(
|
||||
"reportcenter.templates.purchase_return_ratio_grouped_by_vendor_detail"
|
||||
),
|
||||
subject: i18n.t(
|
||||
"reportcenter.templates.purchase_return_ratio_grouped_by_vendor_detail"
|
||||
),
|
||||
@@ -1828,7 +1818,9 @@ export const TemplateList = (type, context) => {
|
||||
group: "purchases",
|
||||
},
|
||||
purchase_return_ratio_grouped_by_vendor_summary: {
|
||||
title: i18n.t("reportcenter.templates.purchase_return_ratio_grouped_by_vendor_summary"),
|
||||
title: i18n.t(
|
||||
"reportcenter.templates.purchase_return_ratio_grouped_by_vendor_summary"
|
||||
),
|
||||
subject: i18n.t(
|
||||
"reportcenter.templates.purchase_return_ratio_grouped_by_vendor_summary"
|
||||
),
|
||||
@@ -1843,9 +1835,7 @@ export const TemplateList = (type, context) => {
|
||||
},
|
||||
production_over_time: {
|
||||
title: i18n.t("reportcenter.templates.production_over_time"),
|
||||
subject: i18n.t(
|
||||
"reportcenter.templates.production_over_time"
|
||||
),
|
||||
subject: i18n.t("reportcenter.templates.production_over_time"),
|
||||
key: "production_over_time",
|
||||
//idtype: "vendor",
|
||||
disabled: false,
|
||||
@@ -1855,6 +1845,44 @@ export const TemplateList = (type, context) => {
|
||||
},
|
||||
group: "jobs",
|
||||
},
|
||||
customer_list: {
|
||||
title: i18n.t("reportcenter.templates.customer_list"),
|
||||
subject: i18n.t("reportcenter.templates.customer_list"),
|
||||
key: "customer_list",
|
||||
//idtype: "vendor",
|
||||
disabled: false,
|
||||
rangeFilter: {
|
||||
object: i18n.t("reportcenter.labels.objects.jobs"),
|
||||
field: i18n.t("jobs.fields.date_invoiced"),
|
||||
},
|
||||
group: "customers",
|
||||
},
|
||||
exported_gsr_by_ro: {
|
||||
title: i18n.t("reportcenter.templates.exported_gsr_by_ro"),
|
||||
subject: i18n.t("reportcenter.templates.exported_gsr_by_ro"),
|
||||
key: "exported_gsr_by_ro",
|
||||
//idtype: "vendor",
|
||||
reporttype: "excel",
|
||||
disabled: false,
|
||||
rangeFilter: {
|
||||
object: i18n.t("reportcenter.labels.objects.jobs"),
|
||||
field: i18n.t("jobs.fields.date_exported"),
|
||||
},
|
||||
group: "sales",
|
||||
},
|
||||
exported_gsr_by_ro_labor: {
|
||||
title: i18n.t("reportcenter.templates.exported_gsr_by_ro_labor"),
|
||||
subject: i18n.t("reportcenter.templates.exported_gsr_by_ro_labor"),
|
||||
key: "exported_gsr_by_ro_labor",
|
||||
//idtype: "vendor",
|
||||
reporttype: "excel",
|
||||
disabled: false,
|
||||
rangeFilter: {
|
||||
object: i18n.t("reportcenter.labels.objects.jobs"),
|
||||
field: i18n.t("jobs.fields.date_exported"),
|
||||
},
|
||||
group: "sales",
|
||||
},
|
||||
}
|
||||
: {}),
|
||||
...(!type || type === "courtesycarcontract"
|
||||
@@ -1949,7 +1977,6 @@ export const TemplateList = (type, context) => {
|
||||
},
|
||||
}
|
||||
: {}),
|
||||
|
||||
...(!type || type === "production"
|
||||
? {
|
||||
production_by_last_name: {
|
||||
|
||||
12
client/src/utils/criticalPartsScan.js
Normal file
12
client/src/utils/criticalPartsScan.js
Normal file
@@ -0,0 +1,12 @@
|
||||
import axios from "axios";
|
||||
import { notification } from "antd";
|
||||
|
||||
async function CriticalPartsScan(jobid) {
|
||||
try {
|
||||
await axios.post("/job/partsscan", { jobid });
|
||||
} catch (error) {
|
||||
notification.open({ type: "error", message: JSON.stringify(error) });
|
||||
}
|
||||
}
|
||||
|
||||
export default CriticalPartsScan;
|
||||
@@ -3924,14 +3924,6 @@ anymatch@^3.0.3, anymatch@~3.1.2:
|
||||
normalize-path "^3.0.0"
|
||||
picomatch "^2.0.4"
|
||||
|
||||
aphrodite@^0.5.0:
|
||||
version "0.5.0"
|
||||
resolved "https://registry.yarnpkg.com/aphrodite/-/aphrodite-0.5.0.tgz#a4b9a8902662395d2702e70ac7a2b4ca66f25703"
|
||||
integrity sha512-bPcR/68OdLg+16XDYAaKnlGJufxrbOw86ucElS7qHm7Y6ZNvMT/D/VDEEfodjFgfyRyoTeKmSaikULG92kSYvQ==
|
||||
dependencies:
|
||||
asap "^2.0.3"
|
||||
inline-style-prefixer "^2.0.0"
|
||||
|
||||
apollo-link-logger@^2.0.1:
|
||||
version "2.0.1"
|
||||
resolved "https://registry.yarnpkg.com/apollo-link-logger/-/apollo-link-logger-2.0.1.tgz#63d86b4fe550c147c5a52108710075675694905c"
|
||||
@@ -4073,7 +4065,7 @@ array.prototype.tosorted@^1.1.1:
|
||||
es-shim-unscopables "^1.0.0"
|
||||
get-intrinsic "^1.1.3"
|
||||
|
||||
asap@^2.0.3, asap@~2.0.6:
|
||||
asap@~2.0.6:
|
||||
version "2.0.6"
|
||||
resolved "https://registry.yarnpkg.com/asap/-/asap-2.0.6.tgz#e50347611d7e690943208bbdafebcbc2fb866d46"
|
||||
integrity sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==
|
||||
@@ -4484,11 +4476,6 @@ boolbase@^1.0.0, boolbase@~1.0.0:
|
||||
resolved "https://registry.yarnpkg.com/boolbase/-/boolbase-1.0.0.tgz#68dff5fbe60c51eb37725ea9e3ed310dcc1e776e"
|
||||
integrity sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==
|
||||
|
||||
bowser@^1.0.0:
|
||||
version "1.9.4"
|
||||
resolved "https://registry.yarnpkg.com/bowser/-/bowser-1.9.4.tgz#890c58a2813a9d3243704334fa81b96a5c150c9a"
|
||||
integrity sha512-9IdMmj2KjigRq6oWhmwv1W36pDuA4STQZ8q6YO9um+x07xgYNCD3Oou+WP/3L1HNz7iqythGet3/p4wvc8AAwQ==
|
||||
|
||||
brace-expansion@^1.1.7:
|
||||
version "1.1.11"
|
||||
resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd"
|
||||
@@ -6504,7 +6491,7 @@ executable@^4.1.1:
|
||||
dependencies:
|
||||
pify "^2.2.0"
|
||||
|
||||
exenv@^1.2.2:
|
||||
exenv@^1.2.0:
|
||||
version "1.2.2"
|
||||
resolved "https://registry.yarnpkg.com/exenv/-/exenv-1.2.2.tgz#2ae78e85d9894158670b03d47bec1f03bd91bb9d"
|
||||
integrity sha512-Z+ktTxTwv9ILfgKCk32OX3n/doe+OcLTRtqK9pcL+JsP3J1/VW8Uvl4ZjLlKqeW4rzK4oesDOGMEMRIZqtP4Iw==
|
||||
@@ -7404,11 +7391,6 @@ human-signals@^2.1.0:
|
||||
resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-2.1.0.tgz#dc91fcba42e4d06e4abaed33b3e7a3c02f514ea0"
|
||||
integrity sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==
|
||||
|
||||
hyphenate-style-name@^1.0.1:
|
||||
version "1.0.4"
|
||||
resolved "https://registry.yarnpkg.com/hyphenate-style-name/-/hyphenate-style-name-1.0.4.tgz#691879af8e220aea5750e8827db4ef62a54e361d"
|
||||
integrity sha512-ygGZLjmXfPHj+ZWh6LwbC37l43MhfztxetbFCoYTM2VjkIUpeHgSNn7QIyVFj7YQ1Wl9Cbw5sholVJPzWvC2MQ==
|
||||
|
||||
i18next-browser-languagedetector@^7.0.1:
|
||||
version "7.0.1"
|
||||
resolved "https://registry.yarnpkg.com/i18next-browser-languagedetector/-/i18next-browser-languagedetector-7.0.1.tgz#ead34592edc96c6c3a618a51cb57ad027c5b5d87"
|
||||
@@ -7543,14 +7525,6 @@ ini@^1.3.5:
|
||||
resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.8.tgz#a29da425b48806f34767a4efce397269af28432c"
|
||||
integrity sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==
|
||||
|
||||
inline-style-prefixer@^2.0.0:
|
||||
version "2.0.5"
|
||||
resolved "https://registry.yarnpkg.com/inline-style-prefixer/-/inline-style-prefixer-2.0.5.tgz#c153c7e88fd84fef5c602e95a8168b2770671fe7"
|
||||
integrity sha512-rlgoApaAAv66JeDFqGYaXwglnlxjyIr7RMaFZqJw4opT0WKnZVXhXMC9SAfuMpN7NELPaImUhJmOO3hAamSlfw==
|
||||
dependencies:
|
||||
bowser "^1.0.0"
|
||||
hyphenate-style-name "^1.0.1"
|
||||
|
||||
internal-slot@^1.0.3:
|
||||
version "1.0.3"
|
||||
resolved "https://registry.yarnpkg.com/internal-slot/-/internal-slot-1.0.3.tgz#7347e307deeea2faac2ac6205d4bc7d34967f59c"
|
||||
@@ -11157,13 +11131,10 @@ react-error-overlay@6.0.9, react-error-overlay@^6.0.11:
|
||||
resolved "https://registry.yarnpkg.com/react-error-overlay/-/react-error-overlay-6.0.9.tgz#3c743010c9359608c375ecd6bc76f35d93995b0a"
|
||||
integrity sha512-nQTTcUu+ATDbrSD1BZHr5kgSD4oF8OFjxun8uAaL8RwPBacGBNPf/yAuVVdx17N8XNzRDMrZ9XcKZHCjPW+9ew==
|
||||
|
||||
react-grid-gallery@^0.5.5:
|
||||
version "0.5.5"
|
||||
resolved "https://registry.yarnpkg.com/react-grid-gallery/-/react-grid-gallery-0.5.5.tgz#1b3f3c23a190834e587ab613c96d53ec3af4f0a2"
|
||||
integrity sha512-DkKg2/Am+VZPDG39fazelTcsZSQrfM/YllnIcWToyUEfOZcrzHxUoqCziCkuTPmCuMbHnrjidBFuDbAFgvSnvQ==
|
||||
dependencies:
|
||||
prop-types "^15.5.8"
|
||||
react-images "^0.5.16"
|
||||
react-grid-gallery@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/react-grid-gallery/-/react-grid-gallery-1.0.0.tgz#31604b9488dfa75a899aa39bc8884138f0409a12"
|
||||
integrity sha512-S1gr6WXBlPFVrE0x2BHuu7jhyaB61mabcMQyx+8KCgAyKzGza0WF67AfAnS9Q00aurFEq4IP8eqb2Bk5F4RAmQ==
|
||||
|
||||
react-grid-layout@^1.3.4:
|
||||
version "1.3.4"
|
||||
@@ -11189,15 +11160,13 @@ react-icons@^4.7.1:
|
||||
resolved "https://registry.yarnpkg.com/react-icons/-/react-icons-4.7.1.tgz#0f4b25a5694e6972677cb189d2a72eabea7a8345"
|
||||
integrity sha512-yHd3oKGMgm7zxo3EA7H2n7vxSoiGmHk5t6Ou4bXsfcgWyhfDKMpyKfhHR6Bjnn63c+YXBLBPUql9H4wPJM6sXw==
|
||||
|
||||
react-images@^0.5.16:
|
||||
version "0.5.19"
|
||||
resolved "https://registry.yarnpkg.com/react-images/-/react-images-0.5.19.tgz#9339570029e065f9f28a19f03fdb5d9d5aa109d3"
|
||||
integrity sha512-B3d4W1uFJj+m17K8S65iAyEJShKGBjPk7n7N1YsPiAydEm8mIq9a6CoeQFMY1d7N2QMs6FBCjT9vELyc5jP5JA==
|
||||
react-image-lightbox@^5.1.4:
|
||||
version "5.1.4"
|
||||
resolved "https://registry.yarnpkg.com/react-image-lightbox/-/react-image-lightbox-5.1.4.tgz#5b847dcb79e9efdf9d7cd5621a92e0f156d2cf30"
|
||||
integrity sha512-kTiAODz091bgT7SlWNHab0LSMZAPJtlNWDGKv7pLlLY1krmf7FuG1zxE0wyPpeA8gPdwfr3cu6sPwZRqWsc3Eg==
|
||||
dependencies:
|
||||
aphrodite "^0.5.0"
|
||||
prop-types "^15.6.0"
|
||||
react-scrolllock "^2.0.1"
|
||||
react-transition-group "2"
|
||||
prop-types "^15.7.2"
|
||||
react-modal "^3.11.1"
|
||||
|
||||
react-is@^16.10.2, react-is@^16.12.0, react-is@^16.13.1, react-is@^16.6.0, react-is@^16.7.0:
|
||||
version "16.13.1"
|
||||
@@ -11214,11 +11183,21 @@ react-is@^18.0.0:
|
||||
resolved "https://registry.yarnpkg.com/react-is/-/react-is-18.2.0.tgz#199431eeaaa2e09f86427efbb4f1473edb47609b"
|
||||
integrity sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==
|
||||
|
||||
react-lifecycles-compat@^3.0.4:
|
||||
react-lifecycles-compat@^3.0.0, react-lifecycles-compat@^3.0.4:
|
||||
version "3.0.4"
|
||||
resolved "https://registry.yarnpkg.com/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz#4f1a273afdfc8f3488a8c516bfda78f872352362"
|
||||
integrity sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA==
|
||||
|
||||
react-modal@^3.11.1:
|
||||
version "3.16.1"
|
||||
resolved "https://registry.yarnpkg.com/react-modal/-/react-modal-3.16.1.tgz#34018528fc206561b1a5467fc3beeaddafb39b2b"
|
||||
integrity sha512-VStHgI3BVcGo7OXczvnJN7yT2TWHJPDXZWyI/a0ssFNhGZWsPmB8cF0z33ewDXq4VfYMO1vXgiv/g8Nj9NDyWg==
|
||||
dependencies:
|
||||
exenv "^1.2.0"
|
||||
prop-types "^15.7.2"
|
||||
react-lifecycles-compat "^3.0.0"
|
||||
warning "^4.0.3"
|
||||
|
||||
react-number-format@^5.1.3:
|
||||
version "5.1.3"
|
||||
resolved "https://registry.yarnpkg.com/react-number-format/-/react-number-format-5.1.3.tgz#5534f5141cea29e0fe889f76c73dfad11ddf2e17"
|
||||
@@ -11240,11 +11219,6 @@ react-overlays@^5.2.1:
|
||||
uncontrollable "^7.2.1"
|
||||
warning "^4.0.3"
|
||||
|
||||
react-prop-toggle@^1.0.2:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/react-prop-toggle/-/react-prop-toggle-1.0.2.tgz#8b0b7e74653606b1427cfcf6c4eaa9198330568e"
|
||||
integrity sha512-JmerjAXs7qJ959+d0Ygt7Cb2+4fG+n3I2VXO6JO0AcAY1vkRN/JpZKAN67CMXY889xEJcfylmMPhzvf6nWO68Q==
|
||||
|
||||
react-redux@^7.2.0:
|
||||
version "7.2.8"
|
||||
resolved "https://registry.yarnpkg.com/react-redux/-/react-redux-7.2.8.tgz#a894068315e65de5b1b68899f9c6ee0923dd28de"
|
||||
@@ -11373,14 +11347,6 @@ react-scripts@^5.0.1:
|
||||
optionalDependencies:
|
||||
fsevents "^2.3.2"
|
||||
|
||||
react-scrolllock@^2.0.1:
|
||||
version "2.0.7"
|
||||
resolved "https://registry.yarnpkg.com/react-scrolllock/-/react-scrolllock-2.0.7.tgz#3b879e1fe308fc900ab76e226e9be594c41226fd"
|
||||
integrity sha512-Gzpu8+ulxdYcybAgJOFTXc70xs7SBZDQbZNpKzchZUgLCJKjz6lrgESx6LHHZgfELx1xYL4yHu3kYQGQPFas/g==
|
||||
dependencies:
|
||||
exenv "^1.2.2"
|
||||
react-prop-toggle "^1.0.2"
|
||||
|
||||
react-smooth@^2.0.1:
|
||||
version "2.0.1"
|
||||
resolved "https://registry.yarnpkg.com/react-smooth/-/react-smooth-2.0.1.tgz#74c7309916d6ccca182c4b30c8992f179e6c5a05"
|
||||
@@ -11405,7 +11371,7 @@ react-sublime-video@^0.2.5:
|
||||
prop-types "^15.5.10"
|
||||
rc-tween-one "^1.2.5"
|
||||
|
||||
react-transition-group@2, react-transition-group@2.9.0:
|
||||
react-transition-group@2.9.0:
|
||||
version "2.9.0"
|
||||
resolved "https://registry.yarnpkg.com/react-transition-group/-/react-transition-group-2.9.0.tgz#df9cdb025796211151a436c69a8f3b97b5b07c8d"
|
||||
integrity sha512-+HzNTCHpeQyl4MJ/bdE0u6XRMe9+XG/+aL4mCxVN4DnPBQ0/5bfHWPDuOZUzYdMj94daZaZdCCc1Dzt9R/xSSg==
|
||||
|
||||
@@ -673,6 +673,36 @@
|
||||
_eq: true
|
||||
- exported:
|
||||
_eq: false
|
||||
event_triggers:
|
||||
- name: os_bills
|
||||
definition:
|
||||
delete:
|
||||
columns: '*'
|
||||
enable_manual: false
|
||||
insert:
|
||||
columns: '*'
|
||||
update:
|
||||
columns:
|
||||
- jobid
|
||||
- invoice_number
|
||||
- due_date
|
||||
- vendorid
|
||||
- id
|
||||
- date
|
||||
retry_conf:
|
||||
interval_sec: 10
|
||||
num_retries: 3
|
||||
timeout_sec: 60
|
||||
webhook_from_env: HASURA_API_URL
|
||||
headers:
|
||||
- name: event-secret
|
||||
value_from_env: EVENT_SECRET
|
||||
request_transform:
|
||||
method: POST
|
||||
query_params: {}
|
||||
template_engine: Kriti
|
||||
url: '{{$base_url}}/opensearch'
|
||||
version: 2
|
||||
- table:
|
||||
name: bodyshops
|
||||
schema: public
|
||||
@@ -761,6 +791,13 @@
|
||||
table:
|
||||
name: email_audit_trail
|
||||
schema: public
|
||||
- name: employee_teams
|
||||
using:
|
||||
foreign_key_constraint_on:
|
||||
column: bodyshopid
|
||||
table:
|
||||
name: employee_teams
|
||||
schema: public
|
||||
- name: employees
|
||||
using:
|
||||
foreign_key_constraint_on:
|
||||
@@ -796,6 +833,13 @@
|
||||
table:
|
||||
name: owners
|
||||
schema: public
|
||||
- name: payment_responses
|
||||
using:
|
||||
foreign_key_constraint_on:
|
||||
column: bodyshopid
|
||||
table:
|
||||
name: payment_response
|
||||
schema: public
|
||||
- name: phonebooks
|
||||
using:
|
||||
foreign_key_constraint_on:
|
||||
@@ -817,6 +861,13 @@
|
||||
table:
|
||||
name: transitions
|
||||
schema: public
|
||||
- name: tt_approval_queues
|
||||
using:
|
||||
foreign_key_constraint_on:
|
||||
column: bodyshopid
|
||||
table:
|
||||
name: tt_approval_queue
|
||||
schema: public
|
||||
- name: vehicles
|
||||
using:
|
||||
foreign_key_constraint_on:
|
||||
@@ -853,6 +904,7 @@
|
||||
- deliverchecklist
|
||||
- email
|
||||
- enforce_class
|
||||
- enforce_conversion_category
|
||||
- enforce_conversion_csr
|
||||
- enforce_referral
|
||||
- entegral_configuration
|
||||
@@ -883,11 +935,13 @@
|
||||
- md_ins_cos
|
||||
- md_jobline_presets
|
||||
- md_labor_rates
|
||||
- md_lost_sale_reasons
|
||||
- md_messaging_presets
|
||||
- md_notes_presets
|
||||
- md_order_statuses
|
||||
- md_parts_locations
|
||||
- md_parts_order_comment
|
||||
- md_parts_scan
|
||||
- md_payment_types
|
||||
- md_rbac
|
||||
- md_referral_sources
|
||||
@@ -918,8 +972,10 @@
|
||||
- textid
|
||||
- timezone
|
||||
- tt_allow_post_to_invoiced
|
||||
- tt_enforce_hours_for_tech_console
|
||||
- updated_at
|
||||
- use_fippa
|
||||
- use_paint_scale_data
|
||||
- uselocalmediaserver
|
||||
- website
|
||||
- workingdays
|
||||
@@ -950,6 +1006,7 @@
|
||||
- deliverchecklist
|
||||
- email
|
||||
- enforce_class
|
||||
- enforce_conversion_category
|
||||
- enforce_conversion_csr
|
||||
- enforce_referral
|
||||
- federal_tax_id
|
||||
@@ -975,11 +1032,13 @@
|
||||
- md_ins_cos
|
||||
- md_jobline_presets
|
||||
- md_labor_rates
|
||||
- md_lost_sale_reasons
|
||||
- md_messaging_presets
|
||||
- md_notes_presets
|
||||
- md_order_statuses
|
||||
- md_parts_locations
|
||||
- md_parts_order_comment
|
||||
- md_parts_scan
|
||||
- md_payment_types
|
||||
- md_rbac
|
||||
- md_referral_sources
|
||||
@@ -1003,8 +1062,10 @@
|
||||
- target_touchtime
|
||||
- timezone
|
||||
- tt_allow_post_to_invoiced
|
||||
- tt_enforce_hours_for_tech_console
|
||||
- updated_at
|
||||
- use_fippa
|
||||
- use_paint_scale_data
|
||||
- uselocalmediaserver
|
||||
- website
|
||||
- workingdays
|
||||
@@ -1932,6 +1993,165 @@
|
||||
- active:
|
||||
_eq: true
|
||||
check: null
|
||||
- table:
|
||||
name: employee_team_members
|
||||
schema: public
|
||||
object_relationships:
|
||||
- name: employee
|
||||
using:
|
||||
foreign_key_constraint_on: employeeid
|
||||
- name: employee_team
|
||||
using:
|
||||
foreign_key_constraint_on: teamid
|
||||
insert_permissions:
|
||||
- role: user
|
||||
permission:
|
||||
check:
|
||||
employee_team:
|
||||
bodyshop:
|
||||
associations:
|
||||
_and:
|
||||
- user:
|
||||
authid:
|
||||
_eq: X-Hasura-User-Id
|
||||
- active:
|
||||
_eq: true
|
||||
columns:
|
||||
- labor_rates
|
||||
- percentage
|
||||
- created_at
|
||||
- updated_at
|
||||
- employeeid
|
||||
- id
|
||||
- teamid
|
||||
select_permissions:
|
||||
- role: user
|
||||
permission:
|
||||
columns:
|
||||
- labor_rates
|
||||
- percentage
|
||||
- created_at
|
||||
- updated_at
|
||||
- employeeid
|
||||
- id
|
||||
- teamid
|
||||
filter:
|
||||
employee_team:
|
||||
bodyshop:
|
||||
associations:
|
||||
_and:
|
||||
- user:
|
||||
authid:
|
||||
_eq: X-Hasura-User-Id
|
||||
- active:
|
||||
_eq: true
|
||||
update_permissions:
|
||||
- role: user
|
||||
permission:
|
||||
columns:
|
||||
- labor_rates
|
||||
- percentage
|
||||
- created_at
|
||||
- updated_at
|
||||
- employeeid
|
||||
- id
|
||||
- teamid
|
||||
filter:
|
||||
employee_team:
|
||||
bodyshop:
|
||||
associations:
|
||||
_and:
|
||||
- user:
|
||||
authid:
|
||||
_eq: X-Hasura-User-Id
|
||||
- active:
|
||||
_eq: true
|
||||
check: null
|
||||
delete_permissions:
|
||||
- role: user
|
||||
permission:
|
||||
backend_only: false
|
||||
filter:
|
||||
employee_team:
|
||||
bodyshop:
|
||||
associations:
|
||||
_and:
|
||||
- user:
|
||||
authid:
|
||||
_eq: X-Hasura-User-Id
|
||||
- active:
|
||||
_eq: true
|
||||
- table:
|
||||
name: employee_teams
|
||||
schema: public
|
||||
object_relationships:
|
||||
- name: bodyshop
|
||||
using:
|
||||
foreign_key_constraint_on: bodyshopid
|
||||
array_relationships:
|
||||
- name: employee_team_members
|
||||
using:
|
||||
foreign_key_constraint_on:
|
||||
column: teamid
|
||||
table:
|
||||
name: employee_team_members
|
||||
schema: public
|
||||
insert_permissions:
|
||||
- role: user
|
||||
permission:
|
||||
check:
|
||||
bodyshop:
|
||||
associations:
|
||||
_and:
|
||||
- user:
|
||||
authid:
|
||||
_eq: X-Hasura-User-Id
|
||||
- active:
|
||||
_eq: true
|
||||
columns:
|
||||
- active
|
||||
- name
|
||||
- created_at
|
||||
- updated_at
|
||||
- bodyshopid
|
||||
- id
|
||||
select_permissions:
|
||||
- role: user
|
||||
permission:
|
||||
columns:
|
||||
- active
|
||||
- name
|
||||
- created_at
|
||||
- updated_at
|
||||
- bodyshopid
|
||||
- id
|
||||
filter:
|
||||
bodyshop:
|
||||
associations:
|
||||
_and:
|
||||
- user:
|
||||
authid:
|
||||
_eq: X-Hasura-User-Id
|
||||
- active:
|
||||
_eq: true
|
||||
update_permissions:
|
||||
- role: user
|
||||
permission:
|
||||
columns:
|
||||
- active
|
||||
- bodyshopid
|
||||
- name
|
||||
- updated_at
|
||||
filter:
|
||||
bodyshop:
|
||||
associations:
|
||||
_and:
|
||||
- user:
|
||||
authid:
|
||||
_eq: X-Hasura-User-Id
|
||||
- active:
|
||||
_eq: true
|
||||
check: null
|
||||
- table:
|
||||
name: employee_vacation
|
||||
schema: public
|
||||
@@ -2032,6 +2252,13 @@
|
||||
table:
|
||||
name: allocations
|
||||
schema: public
|
||||
- name: employee_team_members
|
||||
using:
|
||||
foreign_key_constraint_on:
|
||||
column: employeeid
|
||||
table:
|
||||
name: employee_team_members
|
||||
schema: public
|
||||
- name: employee_vacations
|
||||
using:
|
||||
foreign_key_constraint_on:
|
||||
@@ -2074,6 +2301,13 @@
|
||||
table:
|
||||
name: timetickets
|
||||
schema: public
|
||||
- name: tt_approval_queues
|
||||
using:
|
||||
foreign_key_constraint_on:
|
||||
column: employeeid
|
||||
table:
|
||||
name: tt_approval_queue
|
||||
schema: public
|
||||
insert_permissions:
|
||||
- role: user
|
||||
permission:
|
||||
@@ -2205,30 +2439,32 @@
|
||||
- active:
|
||||
_eq: true
|
||||
columns:
|
||||
- id
|
||||
- created_at
|
||||
- updated_at
|
||||
- jobid
|
||||
- billid
|
||||
- bodyshopid
|
||||
- created_at
|
||||
- id
|
||||
- jobid
|
||||
- message
|
||||
- metadata
|
||||
- paymentid
|
||||
- successful
|
||||
- message
|
||||
- bodyshopid
|
||||
- updated_at
|
||||
- useremail
|
||||
select_permissions:
|
||||
- role: user
|
||||
permission:
|
||||
columns:
|
||||
- successful
|
||||
- message
|
||||
- useremail
|
||||
- created_at
|
||||
- updated_at
|
||||
- billid
|
||||
- bodyshopid
|
||||
- created_at
|
||||
- id
|
||||
- jobid
|
||||
- message
|
||||
- metadata
|
||||
- paymentid
|
||||
- successful
|
||||
- updated_at
|
||||
- useremail
|
||||
filter:
|
||||
bodyshop:
|
||||
associations:
|
||||
@@ -2480,6 +2716,7 @@
|
||||
_eq: true
|
||||
columns:
|
||||
- act_price
|
||||
- act_price_before_ppc
|
||||
- ah_detail_line
|
||||
- alt_co_id
|
||||
- alt_overrd
|
||||
@@ -2546,6 +2783,7 @@
|
||||
permission:
|
||||
columns:
|
||||
- act_price
|
||||
- act_price_before_ppc
|
||||
- ah_detail_line
|
||||
- alt_co_id
|
||||
- alt_overrd
|
||||
@@ -2560,6 +2798,7 @@
|
||||
- convertedtolbr
|
||||
- convertedtolbr_data
|
||||
- created_at
|
||||
- critical
|
||||
- db_hrs
|
||||
- db_price
|
||||
- db_ref
|
||||
@@ -2623,6 +2862,7 @@
|
||||
permission:
|
||||
columns:
|
||||
- act_price
|
||||
- act_price_before_ppc
|
||||
- ah_detail_line
|
||||
- alt_co_id
|
||||
- alt_overrd
|
||||
@@ -2637,6 +2877,7 @@
|
||||
- convertedtolbr
|
||||
- convertedtolbr_data
|
||||
- created_at
|
||||
- critical
|
||||
- db_hrs
|
||||
- db_price
|
||||
- db_ref
|
||||
@@ -2901,6 +3142,13 @@
|
||||
table:
|
||||
name: parts_orders
|
||||
schema: public
|
||||
- name: payment_responses
|
||||
using:
|
||||
foreign_key_constraint_on:
|
||||
column: jobid
|
||||
table:
|
||||
name: payment_response
|
||||
schema: public
|
||||
- name: payments
|
||||
using:
|
||||
foreign_key_constraint_on:
|
||||
@@ -2943,6 +3191,13 @@
|
||||
table:
|
||||
name: transitions
|
||||
schema: public
|
||||
- name: tt_approval_queues
|
||||
using:
|
||||
foreign_key_constraint_on:
|
||||
column: jobid
|
||||
table:
|
||||
name: tt_approval_queue
|
||||
schema: public
|
||||
insert_permissions:
|
||||
- role: user
|
||||
permission:
|
||||
@@ -3380,6 +3635,7 @@
|
||||
- loss_desc
|
||||
- loss_of_use
|
||||
- loss_type
|
||||
- lost_sale_reason
|
||||
- materials
|
||||
- other_amount_payable
|
||||
- owner_owing
|
||||
@@ -3654,6 +3910,7 @@
|
||||
- loss_desc
|
||||
- loss_of_use
|
||||
- loss_type
|
||||
- lost_sale_reason
|
||||
- materials
|
||||
- other_amount_payable
|
||||
- owner_owing
|
||||
@@ -3829,6 +4086,44 @@
|
||||
template_engine: Kriti
|
||||
url: '{{$base_url}}/record-handler/arms'
|
||||
version: 2
|
||||
- name: os_jobs
|
||||
definition:
|
||||
delete:
|
||||
columns: '*'
|
||||
enable_manual: false
|
||||
insert:
|
||||
columns: '*'
|
||||
update:
|
||||
columns:
|
||||
- v_color
|
||||
- ownerid
|
||||
- ownr_fn
|
||||
- v_model_desc
|
||||
- ownr_ln
|
||||
- id
|
||||
- v_make_desc
|
||||
- ownr_st
|
||||
- clm_no
|
||||
- voided
|
||||
- status
|
||||
- ownr_co_nm
|
||||
- v_model_yr
|
||||
- v_vin
|
||||
- converted
|
||||
retry_conf:
|
||||
interval_sec: 10
|
||||
num_retries: 3
|
||||
timeout_sec: 60
|
||||
webhook_from_env: HASURA_API_URL
|
||||
headers:
|
||||
- name: event-secret
|
||||
value_from_env: EVENT_SECRET
|
||||
request_transform:
|
||||
method: POST
|
||||
query_params: {}
|
||||
template_engine: Kriti
|
||||
url: '{{$base_url}}/opensearch'
|
||||
version: 2
|
||||
- table:
|
||||
name: masterdata
|
||||
schema: public
|
||||
@@ -4253,6 +4548,35 @@
|
||||
_eq: X-Hasura-User-Id
|
||||
- active:
|
||||
_eq: true
|
||||
event_triggers:
|
||||
- name: os_owners
|
||||
definition:
|
||||
delete:
|
||||
columns: '*'
|
||||
enable_manual: false
|
||||
insert:
|
||||
columns: '*'
|
||||
update:
|
||||
columns:
|
||||
- shopid
|
||||
- ownr_fn
|
||||
- id
|
||||
- ownr_co_nm
|
||||
- ownr_ln
|
||||
retry_conf:
|
||||
interval_sec: 10
|
||||
num_retries: 3
|
||||
timeout_sec: 60
|
||||
webhook_from_env: HASURA_API_URL
|
||||
headers:
|
||||
- name: event-secret
|
||||
value_from_env: EVENT_SECRET
|
||||
request_transform:
|
||||
method: POST
|
||||
query_params: {}
|
||||
template_engine: Kriti
|
||||
url: '{{$base_url}}/opensearch'
|
||||
version: 2
|
||||
- table:
|
||||
name: parts_order_lines
|
||||
schema: public
|
||||
@@ -4556,6 +4880,7 @@
|
||||
_eq: X-Hasura-User-Id
|
||||
- active:
|
||||
_eq: true
|
||||
allow_aggregations: true
|
||||
- table:
|
||||
name: payments
|
||||
schema: public
|
||||
@@ -4571,6 +4896,13 @@
|
||||
table:
|
||||
name: exportlog
|
||||
schema: public
|
||||
- name: payment_responses
|
||||
using:
|
||||
foreign_key_constraint_on:
|
||||
column: paymentid
|
||||
table:
|
||||
name: payment_response
|
||||
schema: public
|
||||
insert_permissions:
|
||||
- role: user
|
||||
permission:
|
||||
@@ -4668,6 +5000,39 @@
|
||||
_eq: X-Hasura-User-Id
|
||||
- active:
|
||||
_eq: true
|
||||
event_triggers:
|
||||
- name: os_payments
|
||||
definition:
|
||||
delete:
|
||||
columns: '*'
|
||||
enable_manual: false
|
||||
insert:
|
||||
columns: '*'
|
||||
update:
|
||||
columns:
|
||||
- paymentnum
|
||||
- type
|
||||
- amount
|
||||
- date
|
||||
- transactionid
|
||||
- memo
|
||||
- payer
|
||||
- id
|
||||
- jobid
|
||||
retry_conf:
|
||||
interval_sec: 10
|
||||
num_retries: 3
|
||||
timeout_sec: 60
|
||||
webhook_from_env: HASURA_API_URL
|
||||
headers:
|
||||
- name: event-secret
|
||||
value_from_env: EVENT_SECRET
|
||||
request_transform:
|
||||
method: POST
|
||||
query_params: {}
|
||||
template_engine: Kriti
|
||||
url: '{{$base_url}}/opensearch'
|
||||
version: 2
|
||||
- table:
|
||||
name: phonebook
|
||||
schema: public
|
||||
@@ -4998,6 +5363,12 @@
|
||||
- name: job
|
||||
using:
|
||||
foreign_key_constraint_on: jobid
|
||||
- name: tt_approval_queue
|
||||
using:
|
||||
foreign_key_constraint_on: ttapprovalqueueid
|
||||
- name: user
|
||||
using:
|
||||
foreign_key_constraint_on: commited_by
|
||||
insert_permissions:
|
||||
- role: user
|
||||
permission:
|
||||
@@ -5016,6 +5387,8 @@
|
||||
- ciecacode
|
||||
- clockoff
|
||||
- clockon
|
||||
- commited_by
|
||||
- committed_at
|
||||
- cost_center
|
||||
- created_at
|
||||
- date
|
||||
@@ -5026,6 +5399,7 @@
|
||||
- memo
|
||||
- productivehrs
|
||||
- rate
|
||||
- ttapprovalqueueid
|
||||
- updated_at
|
||||
select_permissions:
|
||||
- role: user
|
||||
@@ -5036,6 +5410,8 @@
|
||||
- ciecacode
|
||||
- clockoff
|
||||
- clockon
|
||||
- commited_by
|
||||
- committed_at
|
||||
- cost_center
|
||||
- created_at
|
||||
- date
|
||||
@@ -5046,6 +5422,7 @@
|
||||
- memo
|
||||
- productivehrs
|
||||
- rate
|
||||
- ttapprovalqueueid
|
||||
- updated_at
|
||||
filter:
|
||||
bodyshop:
|
||||
@@ -5065,6 +5442,8 @@
|
||||
- ciecacode
|
||||
- clockoff
|
||||
- clockon
|
||||
- commited_by
|
||||
- committed_at
|
||||
- cost_center
|
||||
- created_at
|
||||
- date
|
||||
@@ -5075,6 +5454,7 @@
|
||||
- memo
|
||||
- productivehrs
|
||||
- rate
|
||||
- ttapprovalqueueid
|
||||
- updated_at
|
||||
filter:
|
||||
bodyshop:
|
||||
@@ -5185,6 +5565,119 @@
|
||||
authid:
|
||||
_eq: X-Hasura-User-Id
|
||||
check: {}
|
||||
- table:
|
||||
name: tt_approval_queue
|
||||
schema: public
|
||||
object_relationships:
|
||||
- name: bodyshop
|
||||
using:
|
||||
foreign_key_constraint_on: bodyshopid
|
||||
- name: employee
|
||||
using:
|
||||
foreign_key_constraint_on: employeeid
|
||||
- name: job
|
||||
using:
|
||||
foreign_key_constraint_on: jobid
|
||||
- name: user
|
||||
using:
|
||||
foreign_key_constraint_on: approved_by
|
||||
array_relationships:
|
||||
- name: timetickets
|
||||
using:
|
||||
foreign_key_constraint_on:
|
||||
column: ttapprovalqueueid
|
||||
table:
|
||||
name: timetickets
|
||||
schema: public
|
||||
insert_permissions:
|
||||
- role: user
|
||||
permission:
|
||||
check:
|
||||
bodyshop:
|
||||
associations:
|
||||
_and:
|
||||
- active:
|
||||
_eq: true
|
||||
- user:
|
||||
authid:
|
||||
_eq: X-Hasura-User-Id
|
||||
columns:
|
||||
- actualhrs
|
||||
- approved_at
|
||||
- approved_by
|
||||
- bodyshopid
|
||||
- ciecacode
|
||||
- cost_center
|
||||
- created_at
|
||||
- date
|
||||
- employeeid
|
||||
- flat_rate
|
||||
- id
|
||||
- jobid
|
||||
- memo
|
||||
- productivehrs
|
||||
- rate
|
||||
- updated_at
|
||||
select_permissions:
|
||||
- role: user
|
||||
permission:
|
||||
columns:
|
||||
- actualhrs
|
||||
- approved_at
|
||||
- approved_by
|
||||
- bodyshopid
|
||||
- ciecacode
|
||||
- cost_center
|
||||
- created_at
|
||||
- date
|
||||
- employeeid
|
||||
- flat_rate
|
||||
- id
|
||||
- jobid
|
||||
- memo
|
||||
- productivehrs
|
||||
- rate
|
||||
- updated_at
|
||||
filter:
|
||||
bodyshop:
|
||||
associations:
|
||||
_and:
|
||||
- active:
|
||||
_eq: true
|
||||
- user:
|
||||
authid:
|
||||
_eq: X-Hasura-User-Id
|
||||
allow_aggregations: true
|
||||
update_permissions:
|
||||
- role: user
|
||||
permission:
|
||||
columns:
|
||||
- actualhrs
|
||||
- approved_at
|
||||
- approved_by
|
||||
- bodyshopid
|
||||
- ciecacode
|
||||
- cost_center
|
||||
- created_at
|
||||
- date
|
||||
- employeeid
|
||||
- flat_rate
|
||||
- id
|
||||
- jobid
|
||||
- memo
|
||||
- productivehrs
|
||||
- rate
|
||||
- updated_at
|
||||
filter:
|
||||
bodyshop:
|
||||
associations:
|
||||
_and:
|
||||
- active:
|
||||
_eq: true
|
||||
- user:
|
||||
authid:
|
||||
_eq: X-Hasura-User-Id
|
||||
check: null
|
||||
- table:
|
||||
name: users
|
||||
schema: public
|
||||
@@ -5255,6 +5748,20 @@
|
||||
table:
|
||||
name: parts_orders
|
||||
schema: public
|
||||
- name: timetickets
|
||||
using:
|
||||
foreign_key_constraint_on:
|
||||
column: commited_by
|
||||
table:
|
||||
name: timetickets
|
||||
schema: public
|
||||
- name: tt_approval_queues
|
||||
using:
|
||||
foreign_key_constraint_on:
|
||||
column: approved_by
|
||||
table:
|
||||
name: tt_approval_queue
|
||||
schema: public
|
||||
insert_permissions:
|
||||
- role: user
|
||||
permission:
|
||||
@@ -5440,6 +5947,37 @@
|
||||
_eq: X-Hasura-User-Id
|
||||
- active:
|
||||
_eq: true
|
||||
event_triggers:
|
||||
- name: os_vehicles
|
||||
definition:
|
||||
delete:
|
||||
columns: '*'
|
||||
enable_manual: false
|
||||
insert:
|
||||
columns: '*'
|
||||
update:
|
||||
columns:
|
||||
- v_model_yr
|
||||
- plate_no
|
||||
- id
|
||||
- v_vin
|
||||
- v_model_desc
|
||||
- plate_st
|
||||
- shopid
|
||||
retry_conf:
|
||||
interval_sec: 10
|
||||
num_retries: 3
|
||||
timeout_sec: 60
|
||||
webhook_from_env: HASURA_API_URL
|
||||
headers:
|
||||
- name: event-secret
|
||||
value_from_env: EVENT_SECRET
|
||||
request_transform:
|
||||
method: POST
|
||||
query_params: {}
|
||||
template_engine: Kriti
|
||||
url: '{{$base_url}}/opensearch'
|
||||
version: 2
|
||||
- table:
|
||||
name: vendors
|
||||
schema: public
|
||||
|
||||
@@ -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 "md_lost_sale_reasons" jsonb
|
||||
-- not null default jsonb_build_array();
|
||||
@@ -0,0 +1,2 @@
|
||||
alter table "public"."bodyshops" add column "md_lost_sale_reasons" jsonb
|
||||
not null default jsonb_build_array();
|
||||
@@ -0,0 +1,4 @@
|
||||
-- Could not auto-generate a down migration.
|
||||
-- Please write an appropriate down migration for the SQL below:
|
||||
-- alter table "public"."jobs" add column "lost_sale_reason" text
|
||||
-- null;
|
||||
@@ -0,0 +1,2 @@
|
||||
alter table "public"."jobs" add column "lost_sale_reason" text
|
||||
null;
|
||||
@@ -0,0 +1,4 @@
|
||||
-- Could not auto-generate a down migration.
|
||||
-- Please write an appropriate down migration for the SQL below:
|
||||
-- alter table "public"."bodyshops" add column "md_parts_scan" jsonb
|
||||
-- not null default jsonb_build_array();
|
||||
@@ -0,0 +1,2 @@
|
||||
alter table "public"."bodyshops" add column "md_parts_scan" jsonb
|
||||
not null default jsonb_build_array();
|
||||
@@ -0,0 +1,4 @@
|
||||
-- Could not auto-generate a down migration.
|
||||
-- Please write an appropriate down migration for the SQL below:
|
||||
-- alter table "public"."joblines" add column "critical" boolean
|
||||
-- not null default 'false';
|
||||
@@ -0,0 +1,2 @@
|
||||
alter table "public"."joblines" add column "critical" boolean
|
||||
not null default 'false';
|
||||
@@ -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 "enforce_conversion_category" boolean
|
||||
-- not null default 'false';
|
||||
@@ -0,0 +1,2 @@
|
||||
alter table "public"."bodyshops" add column "enforce_conversion_category" boolean
|
||||
not null default 'false';
|
||||
@@ -0,0 +1 @@
|
||||
alter table "public"."bodyshops" alter column "md_lost_sale_reasons" set default jsonb_build_array();
|
||||
@@ -0,0 +1 @@
|
||||
alter table "public"."bodyshops" alter column "md_lost_sale_reasons" set default '["Scheduling Delay", "Backordered Parts", "Price", "Unknown"]';
|
||||
@@ -0,0 +1,4 @@
|
||||
-- Could not auto-generate a down migration.
|
||||
-- Please write an appropriate down migration for the SQL below:
|
||||
-- alter table "public"."joblines" add column "act_price_before_ppc" numeric
|
||||
-- null;
|
||||
@@ -0,0 +1,2 @@
|
||||
alter table "public"."joblines" add column "act_price_before_ppc" numeric
|
||||
null;
|
||||
@@ -0,0 +1 @@
|
||||
DROP TABLE "public"."employee_teams";
|
||||
@@ -0,0 +1,18 @@
|
||||
CREATE TABLE "public"."employee_teams" ("id" uuid NOT NULL DEFAULT gen_random_uuid(), "created_at" timestamptz NOT NULL DEFAULT now(), "updated_at" timestamptz NOT NULL DEFAULT now(), "bodyshopid" uuid NOT NULL, "name" text NOT NULL, "active" boolean NOT NULL DEFAULT true, PRIMARY KEY ("id") , FOREIGN KEY ("bodyshopid") REFERENCES "public"."bodyshops"("id") ON UPDATE cascade ON DELETE cascade);
|
||||
CREATE OR REPLACE FUNCTION "public"."set_current_timestamp_updated_at"()
|
||||
RETURNS TRIGGER AS $$
|
||||
DECLARE
|
||||
_new record;
|
||||
BEGIN
|
||||
_new := NEW;
|
||||
_new."updated_at" = NOW();
|
||||
RETURN _new;
|
||||
END;
|
||||
$$ LANGUAGE plpgsql;
|
||||
CREATE TRIGGER "set_public_employee_teams_updated_at"
|
||||
BEFORE UPDATE ON "public"."employee_teams"
|
||||
FOR EACH ROW
|
||||
EXECUTE PROCEDURE "public"."set_current_timestamp_updated_at"();
|
||||
COMMENT ON TRIGGER "set_public_employee_teams_updated_at" ON "public"."employee_teams"
|
||||
IS 'trigger to set value of column "updated_at" to current timestamp on row update';
|
||||
CREATE EXTENSION IF NOT EXISTS pgcrypto;
|
||||
@@ -0,0 +1 @@
|
||||
DROP TABLE "public"."employee_team_members";
|
||||
@@ -0,0 +1,18 @@
|
||||
CREATE TABLE "public"."employee_team_members" ("id" uuid NOT NULL DEFAULT gen_random_uuid(), "created_at" timestamptz NOT NULL DEFAULT now(), "updated_at" timestamptz NOT NULL DEFAULT now(), "teamid" uuid NOT NULL, "employeeid" uuid NOT NULL, "labor_rates" jsonb NOT NULL DEFAULT jsonb_build_object(), "percentage" numeric NOT NULL DEFAULT 0, PRIMARY KEY ("id") , FOREIGN KEY ("teamid") REFERENCES "public"."employee_teams"("id") ON UPDATE cascade ON DELETE cascade, FOREIGN KEY ("employeeid") REFERENCES "public"."employees"("id") ON UPDATE cascade ON DELETE cascade);
|
||||
CREATE OR REPLACE FUNCTION "public"."set_current_timestamp_updated_at"()
|
||||
RETURNS TRIGGER AS $$
|
||||
DECLARE
|
||||
_new record;
|
||||
BEGIN
|
||||
_new := NEW;
|
||||
_new."updated_at" = NOW();
|
||||
RETURN _new;
|
||||
END;
|
||||
$$ LANGUAGE plpgsql;
|
||||
CREATE TRIGGER "set_public_employee_team_members_updated_at"
|
||||
BEFORE UPDATE ON "public"."employee_team_members"
|
||||
FOR EACH ROW
|
||||
EXECUTE PROCEDURE "public"."set_current_timestamp_updated_at"();
|
||||
COMMENT ON TRIGGER "set_public_employee_team_members_updated_at" ON "public"."employee_team_members"
|
||||
IS 'trigger to set value of column "updated_at" to current timestamp on row update';
|
||||
CREATE EXTENSION IF NOT EXISTS pgcrypto;
|
||||
@@ -0,0 +1,4 @@
|
||||
-- Could not auto-generate a down migration.
|
||||
-- Please write an appropriate down migration for the SQL below:
|
||||
-- alter table "public"."exportlog" add column "metadata" jsonb
|
||||
-- null;
|
||||
@@ -0,0 +1,2 @@
|
||||
alter table "public"."exportlog" add column "metadata" jsonb
|
||||
null;
|
||||
@@ -0,0 +1,4 @@
|
||||
-- Could not auto-generate a down migration.
|
||||
-- Please write an appropriate down migration for the SQL below:
|
||||
-- alter table "public"."bodyshops" add column "tt_enforce_hours_for_tech_console" boolean
|
||||
-- not null default 'false';
|
||||
@@ -0,0 +1,2 @@
|
||||
alter table "public"."bodyshops" add column "tt_enforce_hours_for_tech_console" boolean
|
||||
not null default 'false';
|
||||
@@ -0,0 +1,4 @@
|
||||
-- Could not auto-generate a down migration.
|
||||
-- Please write an appropriate down migration for the SQL below:
|
||||
-- alter table "public"."timetickets" add column "committed_at" timestamptz
|
||||
-- null;
|
||||
@@ -0,0 +1,2 @@
|
||||
alter table "public"."timetickets" add column "committed_at" timestamptz
|
||||
null;
|
||||
@@ -0,0 +1 @@
|
||||
DROP TABLE "public"."tt_approval_queue";
|
||||
@@ -0,0 +1,18 @@
|
||||
CREATE TABLE "public"."tt_approval_queue" ("id" uuid NOT NULL DEFAULT gen_random_uuid(), "created_at" timestamptz NOT NULL DEFAULT now(), "updated_at" timestamptz NOT NULL DEFAULT now(), "bodyshopid" uuid NOT NULL, "jobid" uuid NOT NULL, "employeeid" uuid NOT NULL, "timeticketid" uuid, "approved_by" text, "approved_at" timestamptz NOT NULL, "actualhrs" numeric NOT NULL DEFAULT 0, "productivehrs" numeric NOT NULL DEFAULT 0, "rate" numeric NOT NULL DEFAULT 0, "flat_rate" boolean NOT NULL DEFAULT true, "ciecacode" text, "cost_center" text NOT NULL, "date" date NOT NULL DEFAULT now(), "memo" text NOT NULL, PRIMARY KEY ("id") , FOREIGN KEY ("bodyshopid") REFERENCES "public"."bodyshops"("id") ON UPDATE cascade ON DELETE cascade, FOREIGN KEY ("jobid") REFERENCES "public"."jobs"("id") ON UPDATE cascade ON DELETE cascade, FOREIGN KEY ("employeeid") REFERENCES "public"."employees"("id") ON UPDATE cascade ON DELETE cascade, FOREIGN KEY ("timeticketid") REFERENCES "public"."timetickets"("id") ON UPDATE cascade ON DELETE cascade, FOREIGN KEY ("approved_by") REFERENCES "public"."users"("email") ON UPDATE restrict ON DELETE restrict);
|
||||
CREATE OR REPLACE FUNCTION "public"."set_current_timestamp_updated_at"()
|
||||
RETURNS TRIGGER AS $$
|
||||
DECLARE
|
||||
_new record;
|
||||
BEGIN
|
||||
_new := NEW;
|
||||
_new."updated_at" = NOW();
|
||||
RETURN _new;
|
||||
END;
|
||||
$$ LANGUAGE plpgsql;
|
||||
CREATE TRIGGER "set_public_tt_approval_queue_updated_at"
|
||||
BEFORE UPDATE ON "public"."tt_approval_queue"
|
||||
FOR EACH ROW
|
||||
EXECUTE PROCEDURE "public"."set_current_timestamp_updated_at"();
|
||||
COMMENT ON TRIGGER "set_public_tt_approval_queue_updated_at" ON "public"."tt_approval_queue"
|
||||
IS 'trigger to set value of column "updated_at" to current timestamp on row update';
|
||||
CREATE EXTENSION IF NOT EXISTS pgcrypto;
|
||||
@@ -0,0 +1 @@
|
||||
alter table "public"."tt_approval_queue" alter column "approved_at" set not null;
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user