Compare commits
48 Commits
feature/IO
...
feature/IO
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6f2b5e4c55 | ||
|
|
9ac27b6090 | ||
|
|
3d79be06de | ||
|
|
11ab7cd67e | ||
|
|
ec5258a431 | ||
|
|
fbc7168bde | ||
|
|
f2d9626888 | ||
|
|
2304e0bf02 | ||
|
|
289a666b6d | ||
|
|
b8836c7ae1 | ||
|
|
eca31c5618 | ||
|
|
7fdbedefce | ||
|
|
7140b8d585 | ||
|
|
5eed8d9809 | ||
|
|
57fe5b4c46 | ||
|
|
f266ee1cfe | ||
|
|
9550de5131 | ||
|
|
1f76ff882c | ||
|
|
749f73a272 | ||
|
|
9c1774c417 | ||
|
|
e363dca3f0 | ||
|
|
26b3a43ce5 | ||
|
|
78678dd3dc | ||
|
|
9dc4546b2e | ||
|
|
95aa0e45a6 | ||
|
|
ce9a77efcf | ||
|
|
e9e1e820a7 | ||
|
|
b027a4e618 | ||
|
|
c7fc75aa5c | ||
|
|
98d2372daf | ||
|
|
bf51380167 | ||
|
|
1ec827097f | ||
|
|
ff7dd7d3ea | ||
|
|
8cc4f88fa7 | ||
|
|
7e6ab3a5ff | ||
|
|
34f876f838 | ||
|
|
2f3eccf3d8 | ||
|
|
2b3e64d607 | ||
|
|
05b20505bb | ||
|
|
bddeae945c | ||
|
|
5b267f03b9 | ||
|
|
cb80b79e1d | ||
|
|
9aab47d8f8 | ||
|
|
f2f84e2da8 | ||
|
|
94641ae01d | ||
|
|
9bb36d2223 | ||
|
|
1205e71ea6 | ||
|
|
f8e65ada76 |
30
.vscode/settings.json
vendored
30
.vscode/settings.json
vendored
@@ -8,5 +8,35 @@
|
|||||||
"pattern": "**/IMEX.xml",
|
"pattern": "**/IMEX.xml",
|
||||||
"systemId": "logs/IMEX.xsd"
|
"systemId": "logs/IMEX.xsd"
|
||||||
}
|
}
|
||||||
|
],
|
||||||
|
"cSpell.words": [
|
||||||
|
"antd",
|
||||||
|
"appointmentconfirmation",
|
||||||
|
"appt",
|
||||||
|
"autohouse",
|
||||||
|
"autohouseid",
|
||||||
|
"billlines",
|
||||||
|
"bodyshop",
|
||||||
|
"bodyshopid",
|
||||||
|
"bodyshops",
|
||||||
|
"CIECA",
|
||||||
|
"claimscorp",
|
||||||
|
"claimscorpid",
|
||||||
|
"Dinero",
|
||||||
|
"driveable",
|
||||||
|
"IMEX",
|
||||||
|
"imexshopid",
|
||||||
|
"jobid",
|
||||||
|
"joblines",
|
||||||
|
"Kaizen",
|
||||||
|
"labhrs",
|
||||||
|
"larhrs",
|
||||||
|
"mixdata",
|
||||||
|
"ownr",
|
||||||
|
"promanager",
|
||||||
|
"shopname",
|
||||||
|
"smartscheduling",
|
||||||
|
"timetickets",
|
||||||
|
"touchtime"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
<babeledit_project version="1.2" be_version="2.7.1">
|
<babeledit_project be_version="2.7.1" version="1.2">
|
||||||
<!--
|
<!--
|
||||||
|
|
||||||
BabelEdit project file
|
BabelEdit project file
|
||||||
@@ -11156,6 +11156,27 @@
|
|||||||
</translation>
|
</translation>
|
||||||
</translations>
|
</translations>
|
||||||
</concept_node>
|
</concept_node>
|
||||||
|
<concept_node>
|
||||||
|
<name>imexpay</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>
|
<concept_node>
|
||||||
<name>insurancecos</name>
|
<name>insurancecos</name>
|
||||||
<definition_loaded>false</definition_loaded>
|
<definition_loaded>false</definition_loaded>
|
||||||
@@ -11198,27 +11219,6 @@
|
|||||||
</translation>
|
</translation>
|
||||||
</translations>
|
</translations>
|
||||||
</concept_node>
|
</concept_node>
|
||||||
<concept_node>
|
|
||||||
<name>intellipay</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>
|
<concept_node>
|
||||||
<name>intellipay_cash_discount</name>
|
<name>intellipay_cash_discount</name>
|
||||||
<definition_loaded>false</definition_loaded>
|
<definition_loaded>false</definition_loaded>
|
||||||
@@ -11747,6 +11747,48 @@
|
|||||||
</translation>
|
</translation>
|
||||||
</translations>
|
</translations>
|
||||||
</concept_node>
|
</concept_node>
|
||||||
|
<concept_node>
|
||||||
|
<name>ttl_adjustment</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>ttl_tax_adjustment</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>
|
</children>
|
||||||
</folder_node>
|
</folder_node>
|
||||||
<folder_node>
|
<folder_node>
|
||||||
@@ -11775,6 +11817,27 @@
|
|||||||
</concept_node>
|
</concept_node>
|
||||||
</children>
|
</children>
|
||||||
</folder_node>
|
</folder_node>
|
||||||
|
<concept_node>
|
||||||
|
<name>romepay</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>
|
<concept_node>
|
||||||
<name>scheduling</name>
|
<name>scheduling</name>
|
||||||
<definition_loaded>false</definition_loaded>
|
<definition_loaded>false</definition_loaded>
|
||||||
@@ -20639,6 +20702,27 @@
|
|||||||
</translation>
|
</translation>
|
||||||
</translations>
|
</translations>
|
||||||
</concept_node>
|
</concept_node>
|
||||||
|
<concept_node>
|
||||||
|
<name>time</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>
|
<concept_node>
|
||||||
<name>total</name>
|
<name>total</name>
|
||||||
<definition_loaded>false</definition_loaded>
|
<definition_loaded>false</definition_loaded>
|
||||||
@@ -21811,6 +21895,48 @@
|
|||||||
<folder_node>
|
<folder_node>
|
||||||
<name>columns</name>
|
<name>columns</name>
|
||||||
<children>
|
<children>
|
||||||
|
<concept_node>
|
||||||
|
<name>average_human_readable</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>average_value</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>
|
<concept_node>
|
||||||
<name>duration</name>
|
<name>duration</name>
|
||||||
<definition_loaded>false</definition_loaded>
|
<definition_loaded>false</definition_loaded>
|
||||||
@@ -31746,6 +31872,27 @@
|
|||||||
</translation>
|
</translation>
|
||||||
</translations>
|
</translations>
|
||||||
</concept_node>
|
</concept_node>
|
||||||
|
<concept_node>
|
||||||
|
<name>tlos_ind</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>
|
<concept_node>
|
||||||
<name>towin</name>
|
<name>towin</name>
|
||||||
<definition_loaded>false</definition_loaded>
|
<definition_loaded>false</definition_loaded>
|
||||||
@@ -36253,6 +36400,27 @@
|
|||||||
</translation>
|
</translation>
|
||||||
</translations>
|
</translations>
|
||||||
</concept_node>
|
</concept_node>
|
||||||
|
<concept_node>
|
||||||
|
<name>total_cust_payable_cash_discount</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>
|
<concept_node>
|
||||||
<name>total_repairs</name>
|
<name>total_repairs</name>
|
||||||
<definition_loaded>false</definition_loaded>
|
<definition_loaded>false</definition_loaded>
|
||||||
@@ -48360,6 +48528,27 @@
|
|||||||
</translation>
|
</translation>
|
||||||
</translations>
|
</translations>
|
||||||
</concept_node>
|
</concept_node>
|
||||||
|
<concept_node>
|
||||||
|
<name>tasks_in_view</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>
|
<concept_node>
|
||||||
<name>tasks_on_board</name>
|
<name>tasks_on_board</name>
|
||||||
<definition_loaded>false</definition_loaded>
|
<definition_loaded>false</definition_loaded>
|
||||||
@@ -48402,6 +48591,27 @@
|
|||||||
</translation>
|
</translation>
|
||||||
</translations>
|
</translations>
|
||||||
</concept_node>
|
</concept_node>
|
||||||
|
<concept_node>
|
||||||
|
<name>total_amount_in_view</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>
|
<concept_node>
|
||||||
<name>total_amount_on_board</name>
|
<name>total_amount_on_board</name>
|
||||||
<definition_loaded>false</definition_loaded>
|
<definition_loaded>false</definition_loaded>
|
||||||
@@ -48444,6 +48654,27 @@
|
|||||||
</translation>
|
</translation>
|
||||||
</translations>
|
</translations>
|
||||||
</concept_node>
|
</concept_node>
|
||||||
|
<concept_node>
|
||||||
|
<name>total_hours_in_view</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>
|
<concept_node>
|
||||||
<name>total_hours_on_board</name>
|
<name>total_hours_on_board</name>
|
||||||
<definition_loaded>false</definition_loaded>
|
<definition_loaded>false</definition_loaded>
|
||||||
@@ -48465,6 +48696,27 @@
|
|||||||
</translation>
|
</translation>
|
||||||
</translations>
|
</translations>
|
||||||
</concept_node>
|
</concept_node>
|
||||||
|
<concept_node>
|
||||||
|
<name>total_jobs_in_view</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>
|
<concept_node>
|
||||||
<name>total_jobs_on_board</name>
|
<name>total_jobs_on_board</name>
|
||||||
<definition_loaded>false</definition_loaded>
|
<definition_loaded>false</definition_loaded>
|
||||||
@@ -48507,6 +48759,27 @@
|
|||||||
</translation>
|
</translation>
|
||||||
</translations>
|
</translations>
|
||||||
</concept_node>
|
</concept_node>
|
||||||
|
<concept_node>
|
||||||
|
<name>total_lab_in_view</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>
|
<concept_node>
|
||||||
<name>total_lab_on_board</name>
|
<name>total_lab_on_board</name>
|
||||||
<definition_loaded>false</definition_loaded>
|
<definition_loaded>false</definition_loaded>
|
||||||
@@ -48549,6 +48822,27 @@
|
|||||||
</translation>
|
</translation>
|
||||||
</translations>
|
</translations>
|
||||||
</concept_node>
|
</concept_node>
|
||||||
|
<concept_node>
|
||||||
|
<name>total_lar_in_view</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>
|
<concept_node>
|
||||||
<name>total_lar_on_board</name>
|
<name>total_lar_on_board</name>
|
||||||
<definition_loaded>false</definition_loaded>
|
<definition_loaded>false</definition_loaded>
|
||||||
@@ -48724,6 +49018,27 @@
|
|||||||
</translation>
|
</translation>
|
||||||
</translations>
|
</translations>
|
||||||
</concept_node>
|
</concept_node>
|
||||||
|
<concept_node>
|
||||||
|
<name>tasks_in_view</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>
|
<concept_node>
|
||||||
<name>tasks_on_board</name>
|
<name>tasks_on_board</name>
|
||||||
<definition_loaded>false</definition_loaded>
|
<definition_loaded>false</definition_loaded>
|
||||||
@@ -48766,6 +49081,27 @@
|
|||||||
</translation>
|
</translation>
|
||||||
</translations>
|
</translations>
|
||||||
</concept_node>
|
</concept_node>
|
||||||
|
<concept_node>
|
||||||
|
<name>total_amount_in_view</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>
|
<concept_node>
|
||||||
<name>total_amount_on_board</name>
|
<name>total_amount_on_board</name>
|
||||||
<definition_loaded>false</definition_loaded>
|
<definition_loaded>false</definition_loaded>
|
||||||
@@ -48808,6 +49144,27 @@
|
|||||||
</translation>
|
</translation>
|
||||||
</translations>
|
</translations>
|
||||||
</concept_node>
|
</concept_node>
|
||||||
|
<concept_node>
|
||||||
|
<name>total_hours_in_view</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>
|
<concept_node>
|
||||||
<name>total_hours_on_board</name>
|
<name>total_hours_on_board</name>
|
||||||
<definition_loaded>false</definition_loaded>
|
<definition_loaded>false</definition_loaded>
|
||||||
@@ -48829,6 +49186,27 @@
|
|||||||
</translation>
|
</translation>
|
||||||
</translations>
|
</translations>
|
||||||
</concept_node>
|
</concept_node>
|
||||||
|
<concept_node>
|
||||||
|
<name>total_jobs_in_view</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>
|
<concept_node>
|
||||||
<name>total_jobs_on_board</name>
|
<name>total_jobs_on_board</name>
|
||||||
<definition_loaded>false</definition_loaded>
|
<definition_loaded>false</definition_loaded>
|
||||||
@@ -48871,6 +49249,27 @@
|
|||||||
</translation>
|
</translation>
|
||||||
</translations>
|
</translations>
|
||||||
</concept_node>
|
</concept_node>
|
||||||
|
<concept_node>
|
||||||
|
<name>total_lab_in_view</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>
|
<concept_node>
|
||||||
<name>total_lab_on_board</name>
|
<name>total_lab_on_board</name>
|
||||||
<definition_loaded>false</definition_loaded>
|
<definition_loaded>false</definition_loaded>
|
||||||
@@ -48913,6 +49312,27 @@
|
|||||||
</translation>
|
</translation>
|
||||||
</translations>
|
</translations>
|
||||||
</concept_node>
|
</concept_node>
|
||||||
|
<concept_node>
|
||||||
|
<name>total_lar_in_view</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>
|
<concept_node>
|
||||||
<name>total_lar_on_board</name>
|
<name>total_lar_on_board</name>
|
||||||
<definition_loaded>false</definition_loaded>
|
<definition_loaded>false</definition_loaded>
|
||||||
@@ -51761,6 +52181,27 @@
|
|||||||
</translation>
|
</translation>
|
||||||
</translations>
|
</translations>
|
||||||
</concept_node>
|
</concept_node>
|
||||||
|
<concept_node>
|
||||||
|
<name>production_not_production_status</name>
|
||||||
|
<definition_loaded>false</definition_loaded>
|
||||||
|
<description></description>
|
||||||
|
<comment></comment>
|
||||||
|
<default_text></default_text>
|
||||||
|
<translations>
|
||||||
|
<translation>
|
||||||
|
<language>en-US</language>
|
||||||
|
<approved>false</approved>
|
||||||
|
</translation>
|
||||||
|
<translation>
|
||||||
|
<language>es-MX</language>
|
||||||
|
<approved>false</approved>
|
||||||
|
</translation>
|
||||||
|
<translation>
|
||||||
|
<language>fr-CA</language>
|
||||||
|
<approved>false</approved>
|
||||||
|
</translation>
|
||||||
|
</translations>
|
||||||
|
</concept_node>
|
||||||
<concept_node>
|
<concept_node>
|
||||||
<name>production_over_time</name>
|
<name>production_over_time</name>
|
||||||
<definition_loaded>false</definition_loaded>
|
<definition_loaded>false</definition_loaded>
|
||||||
@@ -54225,6 +54666,27 @@
|
|||||||
</translation>
|
</translation>
|
||||||
</translations>
|
</translations>
|
||||||
</concept_node>
|
</concept_node>
|
||||||
|
<concept_node>
|
||||||
|
<name>created_by</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>
|
<concept_node>
|
||||||
<name>description</name>
|
<name>description</name>
|
||||||
<definition_loaded>false</definition_loaded>
|
<definition_loaded>false</definition_loaded>
|
||||||
@@ -54487,6 +54949,27 @@
|
|||||||
</translation>
|
</translation>
|
||||||
</translations>
|
</translations>
|
||||||
</concept_node>
|
</concept_node>
|
||||||
|
<concept_node>
|
||||||
|
<name>related_items</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>
|
<concept_node>
|
||||||
<name>remind_at</name>
|
<name>remind_at</name>
|
||||||
<definition_loaded>false</definition_loaded>
|
<definition_loaded>false</definition_loaded>
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { DatePicker } from "antd";
|
import { DatePicker, Space, TimePicker } from "antd";
|
||||||
import PropTypes from "prop-types";
|
import PropTypes from "prop-types";
|
||||||
import React, { useCallback, useState } from "react";
|
import React, { useCallback, useState } from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
@@ -20,6 +20,7 @@ const DateTimePicker = ({
|
|||||||
onlyFuture,
|
onlyFuture,
|
||||||
onlyToday,
|
onlyToday,
|
||||||
isDateOnly = false,
|
isDateOnly = false,
|
||||||
|
isSeparatedTime = false,
|
||||||
bodyshop,
|
bodyshop,
|
||||||
...restProps
|
...restProps
|
||||||
}) => {
|
}) => {
|
||||||
@@ -87,24 +88,57 @@ const DateTimePicker = ({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div onKeyDown={handleKeyDown} id={id} style={{ width: "100%" }}>
|
<div onKeyDown={handleKeyDown} id={id} style={{ width: "100%" }}>
|
||||||
<DatePicker
|
{isSeparatedTime && (
|
||||||
showTime={
|
<Space direction="vertical" style={{ width: "100%" }}>
|
||||||
isDateOnly
|
<DatePicker
|
||||||
? false
|
showTime={false}
|
||||||
: {
|
format="MM/DD/YYYY"
|
||||||
format: "hh:mm a",
|
value={value ? dayjs(value) : null}
|
||||||
minuteStep: 15,
|
onChange={handleChange}
|
||||||
defaultValue: dayjs(dayjs(), "HH:mm:ss")
|
placeholder={t("general.labels.date")}
|
||||||
}
|
onBlur={handleBlur}
|
||||||
}
|
disabledDate={handleDisabledDate}
|
||||||
format={isDateOnly ? "MM/DD/YYYY" : "MM/DD/YYYY hh:mm a"}
|
isDateOnly={true}
|
||||||
value={value ? dayjs(value) : null}
|
{...restProps}
|
||||||
onChange={handleChange}
|
/>
|
||||||
placeholder={isDateOnly ? t("general.labels.date") : t("general.labels.datetime")}
|
{value && (
|
||||||
onBlur={onBlur || handleBlur}
|
<TimePicker
|
||||||
disabledDate={handleDisabledDate}
|
format="hh:mm a"
|
||||||
{...restProps}
|
minuteStep={15}
|
||||||
/>
|
defaultOpenValue={dayjs(value)
|
||||||
|
.hour(dayjs().hour())
|
||||||
|
.minute(Math.floor(dayjs().minute() / 15) * 15)
|
||||||
|
.second(0)}
|
||||||
|
onChange={(value) => {
|
||||||
|
handleChange(value);
|
||||||
|
onBlur();
|
||||||
|
}}
|
||||||
|
placeholder={t("general.labels.time")}
|
||||||
|
{...restProps}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</Space>
|
||||||
|
)}
|
||||||
|
{!isSeparatedTime && (
|
||||||
|
<DatePicker
|
||||||
|
showTime={
|
||||||
|
isDateOnly
|
||||||
|
? false
|
||||||
|
: {
|
||||||
|
format: "hh:mm a",
|
||||||
|
minuteStep: 15,
|
||||||
|
defaultValue: dayjs(dayjs(), "HH:mm:ss")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
format={isDateOnly ? "MM/DD/YYYY" : "MM/DD/YYYY hh:mm a"}
|
||||||
|
value={value ? dayjs(value) : null}
|
||||||
|
onChange={handleChange}
|
||||||
|
placeholder={isDateOnly ? t("general.labels.date") : t("general.labels.datetime")}
|
||||||
|
onBlur={onBlur || handleBlur}
|
||||||
|
disabledDate={handleDisabledDate}
|
||||||
|
{...restProps}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
@@ -116,7 +150,8 @@ DateTimePicker.propTypes = {
|
|||||||
id: PropTypes.string,
|
id: PropTypes.string,
|
||||||
onlyFuture: PropTypes.bool,
|
onlyFuture: PropTypes.bool,
|
||||||
onlyToday: PropTypes.bool,
|
onlyToday: PropTypes.bool,
|
||||||
isDateOnly: PropTypes.bool
|
isDateOnly: PropTypes.bool,
|
||||||
|
isSeparatedTime: PropTypes.bool
|
||||||
};
|
};
|
||||||
|
|
||||||
export default connect(mapStateToProps, null)(DateTimePicker);
|
export default connect(mapStateToProps, null)(DateTimePicker);
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { useMutation } from "@apollo/client";
|
import { useMutation } from "@apollo/client";
|
||||||
import { Button, Form, notification, Popover, Tooltip } from "antd";
|
import { Button, Checkbox, Form, notification, Popover, Tooltip } from "antd";
|
||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
import { t } from "i18next";
|
import { t } from "i18next";
|
||||||
import React, { useState } from "react";
|
import React, { useState } from "react";
|
||||||
@@ -60,24 +60,26 @@ export function JobLinesPartPriceChange({ job, line, refetch, technician }) {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const popcontent = !technician && InstanceRenderManager({
|
const popcontent =
|
||||||
imex: null,
|
!technician &&
|
||||||
rome: (
|
InstanceRenderManager({
|
||||||
<Form layout="vertical" onFinish={handleFinish} initialValues={{ act_price: line.act_price }}>
|
imex: null,
|
||||||
<Form.Item name="act_price" label={t("jobs.labels.act_price_ppc")} rules={[{ required: true }]}>
|
rome: (
|
||||||
<CurrencyFormItemComponent />
|
<Form layout="vertical" onFinish={handleFinish} initialValues={{ act_price: line.act_price }}>
|
||||||
</Form.Item>
|
<Form.Item name="act_price" label={t("jobs.labels.act_price_ppc")} rules={[{ required: true }]}>
|
||||||
<Button
|
<CurrencyFormItemComponent />
|
||||||
disabled={InstanceRenderManager({ imex: true, rome: false, promanager: true })}
|
</Form.Item>
|
||||||
loading={loading}
|
<Button
|
||||||
htmlType="primary"
|
disabled={InstanceRenderManager({ imex: true, rome: false, promanager: true })}
|
||||||
>
|
loading={loading}
|
||||||
{t("general.actions.save")}
|
htmlType="primary"
|
||||||
</Button>
|
>
|
||||||
</Form>
|
{t("general.actions.save")}
|
||||||
),
|
</Button>
|
||||||
promanager: null
|
</Form>
|
||||||
});
|
),
|
||||||
|
promanager: null
|
||||||
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<JobLineConvertToLabor jobline={line} job={job}>
|
<JobLineConvertToLabor jobline={line} job={job}>
|
||||||
|
|||||||
@@ -118,8 +118,7 @@ export function JobLinesComponent({
|
|||||||
...(record.critical ? { boxShadow: " -.5em 0 0 #FFC107" } : {})
|
...(record.critical ? { boxShadow: " -.5em 0 0 #FFC107" } : {})
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
sortOrder: state.sortedInfo.columnKey === "line_desc" && state.sortedInfo.order,
|
sortOrder: state.sortedInfo.columnKey === "line_desc" && state.sortedInfo.order
|
||||||
ellipsis: true
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: t("joblines.fields.oem_partno"),
|
title: t("joblines.fields.oem_partno"),
|
||||||
|
|||||||
@@ -45,7 +45,8 @@ export default function JobLineNotePopup({ jobline, disabled }) {
|
|||||||
if (editing)
|
if (editing)
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<Input
|
<Input.TextArea
|
||||||
|
autoSize
|
||||||
autoFocus
|
autoFocus
|
||||||
suffix={loading ? <LoadingSpinner /> : null}
|
suffix={loading ? <LoadingSpinner /> : null}
|
||||||
value={note}
|
value={note}
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
|
import { useSplitTreatments } from "@splitsoftware/splitio-react";
|
||||||
import { Form, Input, InputNumber, Modal, Select, Switch } from "antd";
|
import { Form, Input, InputNumber, Modal, Select, Switch } from "antd";
|
||||||
import React, { useEffect } from "react";
|
import React, { useEffect } from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import InputCurrency from "../form-items-formatted/currency-form-item.component";
|
import InputCurrency from "../form-items-formatted/currency-form-item.component";
|
||||||
import LayoutFormRow from "../layout-form-row/layout-form-row.component";
|
|
||||||
import JoblinesPreset from "../job-lines-preset-button/job-lines-preset-button.component";
|
import JoblinesPreset from "../job-lines-preset-button/job-lines-preset-button.component";
|
||||||
import { useSplitTreatments } from "@splitsoftware/splitio-react";
|
import LayoutFormRow from "../layout-form-row/layout-form-row.component";
|
||||||
|
|
||||||
import { connect } from "react-redux";
|
import { connect } from "react-redux";
|
||||||
import { createStructuredSelector } from "reselect";
|
import { createStructuredSelector } from "reselect";
|
||||||
@@ -61,7 +61,7 @@ export function JobLinesUpsertModalComponent({ bodyshop, open, jobLine, handleCa
|
|||||||
]}
|
]}
|
||||||
name="line_desc"
|
name="line_desc"
|
||||||
>
|
>
|
||||||
<Input />
|
<Input.TextArea autoSize />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<JoblinesPreset form={form} />
|
<JoblinesPreset form={form} />
|
||||||
</LayoutFormRow>
|
</LayoutFormRow>
|
||||||
|
|||||||
@@ -22,6 +22,14 @@ export function JobTotalsTableTotals({ bodyshop, job }) {
|
|||||||
|
|
||||||
const data = useMemo(() => {
|
const data = useMemo(() => {
|
||||||
return [
|
return [
|
||||||
|
...(job.job_totals?.totals?.ttl_adjustment
|
||||||
|
? [
|
||||||
|
{
|
||||||
|
key: `Subtotal Adj.`,
|
||||||
|
total: job.job_totals?.totals?.ttl_adjustment
|
||||||
|
}
|
||||||
|
]
|
||||||
|
: []),
|
||||||
{
|
{
|
||||||
key: t("jobs.labels.subtotal"),
|
key: t("jobs.labels.subtotal"),
|
||||||
total: job.job_totals.totals.subtotal,
|
total: job.job_totals.totals.subtotal,
|
||||||
@@ -102,7 +110,7 @@ export function JobTotalsTableTotals({ bodyshop, job }) {
|
|||||||
total: job.job_totals.totals.us_sales_tax_breakdown.ty4Tax
|
total: job.job_totals.totals.us_sales_tax_breakdown.ty4Tax
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: `${bodyshop.md_responsibility_centers.taxes.tax_ty5?.tax_type5 || "TT"} - ${[
|
key: `${bodyshop.md_responsibility_centers.taxes.tax_ty5?.tax_type5 || "Adj."} - ${[
|
||||||
job.cieca_pft.ty5_rate1,
|
job.cieca_pft.ty5_rate1,
|
||||||
job.cieca_pft.ty5_rate2,
|
job.cieca_pft.ty5_rate2,
|
||||||
job.cieca_pft.ty5_rate3,
|
job.cieca_pft.ty5_rate3,
|
||||||
@@ -113,6 +121,14 @@ export function JobTotalsTableTotals({ bodyshop, job }) {
|
|||||||
.join(", ")}%`,
|
.join(", ")}%`,
|
||||||
total: job.job_totals.totals.us_sales_tax_breakdown.ty5Tax
|
total: job.job_totals.totals.us_sales_tax_breakdown.ty5Tax
|
||||||
},
|
},
|
||||||
|
...(job.job_totals?.totals?.ttl_tax_adjustment
|
||||||
|
? [
|
||||||
|
{
|
||||||
|
key: `Tax Adj.`,
|
||||||
|
total: job.job_totals?.totals?.ttl_tax_adjustment
|
||||||
|
}
|
||||||
|
]
|
||||||
|
: []),
|
||||||
{
|
{
|
||||||
key: t("jobs.labels.total_sales_tax"),
|
key: t("jobs.labels.total_sales_tax"),
|
||||||
bold: true,
|
bold: true,
|
||||||
@@ -121,6 +137,7 @@ export function JobTotalsTableTotals({ bodyshop, job }) {
|
|||||||
.add(Dinero(job.job_totals.totals.us_sales_tax_breakdown.ty3Tax))
|
.add(Dinero(job.job_totals.totals.us_sales_tax_breakdown.ty3Tax))
|
||||||
.add(Dinero(job.job_totals.totals.us_sales_tax_breakdown.ty4Tax))
|
.add(Dinero(job.job_totals.totals.us_sales_tax_breakdown.ty4Tax))
|
||||||
.add(Dinero(job.job_totals.totals.us_sales_tax_breakdown.ty5Tax))
|
.add(Dinero(job.job_totals.totals.us_sales_tax_breakdown.ty5Tax))
|
||||||
|
.add(Dinero(job.job_totals.totals.ttl_tax_adjustment))
|
||||||
.toJSON()
|
.toJSON()
|
||||||
}
|
}
|
||||||
].filter((item) => item.total.amount !== 0)
|
].filter((item) => item.total.amount !== 0)
|
||||||
@@ -141,14 +158,16 @@ export function JobTotalsTableTotals({ bodyshop, job }) {
|
|||||||
key: t("jobs.fields.ded_amt"),
|
key: t("jobs.fields.ded_amt"),
|
||||||
total: job.job_totals.totals.custPayable.deductible
|
total: job.job_totals.totals.custPayable.deductible
|
||||||
},
|
},
|
||||||
...(InstanceRenderManager({
|
...InstanceRenderManager({
|
||||||
imex: [{
|
imex: [
|
||||||
key: t("jobs.fields.federal_tax_payable"),
|
{
|
||||||
total: job.job_totals.totals.custPayable.federal_tax
|
key: t("jobs.fields.federal_tax_payable"),
|
||||||
}],
|
total: job.job_totals.totals.custPayable.federal_tax
|
||||||
|
}
|
||||||
|
],
|
||||||
rome: [],
|
rome: [],
|
||||||
promanager: "USE_ROME"
|
promanager: "USE_ROME"
|
||||||
})),
|
}),
|
||||||
{
|
{
|
||||||
key: t("jobs.fields.other_amount_payable"),
|
key: t("jobs.fields.other_amount_payable"),
|
||||||
total: job.job_totals.totals.custPayable.other_customer_amount
|
total: job.job_totals.totals.custPayable.other_customer_amount
|
||||||
@@ -158,11 +177,32 @@ export function JobTotalsTableTotals({ bodyshop, job }) {
|
|||||||
total: job.job_totals.totals.custPayable.dep_taxes
|
total: job.job_totals.totals.custPayable.dep_taxes
|
||||||
},
|
},
|
||||||
|
|
||||||
{
|
...(bodyshop.intellipay_config?.enable_cash_discount
|
||||||
key: t("jobs.labels.total_cust_payable"),
|
? [
|
||||||
total: job.job_totals.totals.custPayable.total,
|
{
|
||||||
bold: true
|
key: t("jobs.labels.total_cust_payable_cash_discount"),
|
||||||
},
|
total: job.job_totals.totals.custPayable.total,
|
||||||
|
bold: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: t("jobs.labels.total_cust_payable"),
|
||||||
|
total: Dinero(job.job_totals.totals.custPayable.total)
|
||||||
|
.add(
|
||||||
|
Dinero(job.job_totals.totals.custPayable.total).percentage(
|
||||||
|
bodyshop.intellipay_config?.cash_discount_percentage || 0
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.toJSON(),
|
||||||
|
bold: true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
: [
|
||||||
|
{
|
||||||
|
key: t("jobs.labels.total_cust_payable"),
|
||||||
|
total: job.job_totals.totals.custPayable.total,
|
||||||
|
bold: true
|
||||||
|
}
|
||||||
|
]),
|
||||||
{
|
{
|
||||||
key: t("jobs.labels.net_repairs"),
|
key: t("jobs.labels.net_repairs"),
|
||||||
total: job.job_totals.totals.net_repairs,
|
total: job.job_totals.totals.net_repairs,
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { gql, useApolloClient, useLazyQuery, useMutation, useQuery } from "@apollo/client";
|
import { gql, useApolloClient, useLazyQuery, useMutation, useQuery } from "@apollo/client";
|
||||||
import { useSplitTreatments } from "@splitsoftware/splitio-react";
|
import { useSplitTreatments } from "@splitsoftware/splitio-react";
|
||||||
import { Col, Row, notification } from "antd";
|
import { Col, Row, notification } from "antd"; //import { Button, Col, Row, notification } from "antd";
|
||||||
import Axios from "axios";
|
import Axios from "axios";
|
||||||
import _ from "lodash";
|
import _ from "lodash";
|
||||||
import queryString from "query-string";
|
import queryString from "query-string";
|
||||||
@@ -408,26 +408,25 @@ export function JobsAvailableContainer({ bodyshop, currentUser, insertAuditTrail
|
|||||||
updateSchComp={updateSchComp}
|
updateSchComp={updateSchComp}
|
||||||
setSchComp={setSchComp}
|
setSchComp={setSchComp}
|
||||||
/>
|
/>
|
||||||
{
|
{/* {
|
||||||
// currentUser.email.includes("@rome.") ||
|
currentUser.email.includes("@rome.") || currentUser.email.includes("@imex.") ? (
|
||||||
// currentUser.email.includes("@imex.") ? (
|
<Button
|
||||||
// <Button
|
onClick={async () => {
|
||||||
// onClick={async () => {
|
for (const record of data.available_jobs) {
|
||||||
// for (const record of data.available_jobs) {
|
//Query the data
|
||||||
// //Query the data
|
console.log("Start Job", record.id);
|
||||||
// console.log("Start Job", record.id);
|
const { data } = await loadEstData({
|
||||||
// const {data} = await loadEstData({
|
variables: { id: record.id }
|
||||||
// variables: {id: record.id},
|
});
|
||||||
// });
|
console.log("Query has been awaited and is complete");
|
||||||
// console.log("Query has been awaited and is complete");
|
await onOwnerFindModalOk(data);
|
||||||
// await onOwnerFindModalOk(data);
|
}
|
||||||
// }
|
}}
|
||||||
// }}
|
>
|
||||||
// >
|
Add all jobs as new.
|
||||||
// Add all jobs as new.
|
</Button>
|
||||||
// </Button>
|
) : null
|
||||||
// ) : null
|
} */}
|
||||||
}
|
|
||||||
<Row gutter={[16, 16]}>
|
<Row gutter={[16, 16]}>
|
||||||
<Col span={24}>
|
<Col span={24}>
|
||||||
<JobsAvailableTableComponent
|
<JobsAvailableTableComponent
|
||||||
@@ -617,6 +616,7 @@ function ResolveCCCLineIssues(estData, bodyshop) {
|
|||||||
// ` | Act Price delete. (prev act price = ${estData.joblines.data[indexInEstData].act_price})`;
|
// ` | Act Price delete. (prev act price = ${estData.joblines.data[indexInEstData].act_price})`;
|
||||||
estData.joblines.data[indexInEstData].act_price = 0;
|
estData.joblines.data[indexInEstData].act_price = 0;
|
||||||
estData.joblines.data[indexInEstData].db_price = 0;
|
estData.joblines.data[indexInEstData].db_price = 0;
|
||||||
|
estData.joblines.data[indexInEstData].part_type = null;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import { connect } from "react-redux";
|
|||||||
import { createStructuredSelector } from "reselect";
|
import { createStructuredSelector } from "reselect";
|
||||||
import { selectJobReadOnly } from "../../redux/application/application.selectors";
|
import { selectJobReadOnly } from "../../redux/application/application.selectors";
|
||||||
import { selectBodyshop } from "../../redux/user/user.selectors";
|
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||||
|
import DateTimePicker from "../form-date-time-picker/form-date-time-picker.component.jsx";
|
||||||
import CurrencyInput from "../form-items-formatted/currency-form-item.component";
|
import CurrencyInput from "../form-items-formatted/currency-form-item.component";
|
||||||
import FormItemEmail from "../form-items-formatted/email-form-item.component";
|
import FormItemEmail from "../form-items-formatted/email-form-item.component";
|
||||||
import FormItemPhone, { PhoneItemFormatterValidation } from "../form-items-formatted/phone-form-item.component";
|
import FormItemPhone, { PhoneItemFormatterValidation } from "../form-items-formatted/phone-form-item.component";
|
||||||
@@ -12,7 +13,6 @@ import Car from "../job-damage-visual/job-damage-visual.component";
|
|||||||
import JobsDetailChangeEstimator from "../jobs-detail-change-estimator/jobs-detail-change-estimator.component";
|
import JobsDetailChangeEstimator from "../jobs-detail-change-estimator/jobs-detail-change-estimator.component";
|
||||||
import JobsDetailChangeFileHandler from "../jobs-detail-change-filehandler/jobs-detail-change-filehandler.component";
|
import JobsDetailChangeFileHandler from "../jobs-detail-change-filehandler/jobs-detail-change-filehandler.component";
|
||||||
import FormRow from "../layout-form-row/layout-form-row.component";
|
import FormRow from "../layout-form-row/layout-form-row.component";
|
||||||
import DateTimePicker from "../form-date-time-picker/form-date-time-picker.component.jsx";
|
|
||||||
|
|
||||||
const mapStateToProps = createStructuredSelector({
|
const mapStateToProps = createStructuredSelector({
|
||||||
jobRO: selectJobReadOnly,
|
jobRO: selectJobReadOnly,
|
||||||
@@ -185,6 +185,9 @@ export function JobsDetailGeneral({ bodyshop, jobRO, job, form }) {
|
|||||||
<Form.Item label={t("jobs.fields.towin")} name="towin" valuePropName="checked">
|
<Form.Item label={t("jobs.fields.towin")} name="towin" valuePropName="checked">
|
||||||
<Switch disabled={jobRO} />
|
<Switch disabled={jobRO} />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
|
<Form.Item label={t("jobs.fields.tlos_ind")} name="tlos_ind" valuePropName="checked">
|
||||||
|
<Switch disabled={jobRO} />
|
||||||
|
</Form.Item>
|
||||||
</FormRow>
|
</FormRow>
|
||||||
</Col>
|
</Col>
|
||||||
<Col {...lossColDamage}>
|
<Col {...lossColDamage}>
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import { Button, Col, Form, Input, Row, Select, Space, Switch, Typography } from "antd";
|
import { Button, Col, Form, Input, Row, Select, Space, Switch, Typography } from "antd";
|
||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
import dayjs from "../../utils/day";
|
|
||||||
import React, { useState } from "react";
|
import React, { useState } from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { connect } from "react-redux";
|
import { connect } from "react-redux";
|
||||||
@@ -8,13 +7,14 @@ import { createStructuredSelector } from "reselect";
|
|||||||
import { calculateScheduleLoad } from "../../redux/application/application.actions";
|
import { calculateScheduleLoad } from "../../redux/application/application.actions";
|
||||||
import { selectBodyshop } from "../../redux/user/user.selectors";
|
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||||
import { DateFormatter } from "../../utils/DateFormatter";
|
import { DateFormatter } from "../../utils/DateFormatter";
|
||||||
|
import dayjs from "../../utils/day";
|
||||||
|
import InstanceRenderManager from "../../utils/instanceRenderMgr";
|
||||||
import DateTimePicker from "../form-date-time-picker/form-date-time-picker.component";
|
import DateTimePicker from "../form-date-time-picker/form-date-time-picker.component";
|
||||||
import EmailInput from "../form-items-formatted/email-form-item.component";
|
import EmailInput from "../form-items-formatted/email-form-item.component";
|
||||||
import LayoutFormRow from "../layout-form-row/layout-form-row.component";
|
import LayoutFormRow from "../layout-form-row/layout-form-row.component";
|
||||||
import ScheduleDayViewContainer from "../schedule-day-view/schedule-day-view.container";
|
import ScheduleDayViewContainer from "../schedule-day-view/schedule-day-view.container";
|
||||||
import ScheduleExistingAppointmentsList from "../schedule-existing-appointments-list/schedule-existing-appointments-list.component";
|
import ScheduleExistingAppointmentsList from "../schedule-existing-appointments-list/schedule-existing-appointments-list.component";
|
||||||
import "./schedule-job-modal.scss";
|
import "./schedule-job-modal.scss";
|
||||||
import InstanceRenderManager from "../../utils/instanceRenderMgr";
|
|
||||||
|
|
||||||
const mapStateToProps = createStructuredSelector({
|
const mapStateToProps = createStructuredSelector({
|
||||||
bodyshop: selectBodyshop
|
bodyshop: selectBodyshop
|
||||||
@@ -84,7 +84,7 @@ export function ScheduleJobModalComponent({
|
|||||||
}
|
}
|
||||||
]}
|
]}
|
||||||
>
|
>
|
||||||
<DateTimePicker onBlur={handleDateBlur} onlyFuture />
|
<DateTimePicker onBlur={handleDateBlur} onlyFuture isSeparatedTime />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<Form.Item
|
<Form.Item
|
||||||
name="scheduled_completion"
|
name="scheduled_completion"
|
||||||
|
|||||||
@@ -142,7 +142,7 @@ export function ShopInfoComponent({ bodyshop, form, saveLoading }) {
|
|||||||
rome: [
|
rome: [
|
||||||
{
|
{
|
||||||
key: "intellipay",
|
key: "intellipay",
|
||||||
label: t("bodyshop.labels.intellipay"),
|
label: InstanceRenderManager({ rome: t("bodyshop.labels.romepay"), imex: t("bodyshop.labels.imexpay") }),
|
||||||
children: <ShopInfoIntellipay form={form} />
|
children: <ShopInfoIntellipay form={form} />
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -676,7 +676,7 @@ export function ShopInfoGeneral({ form, bodyshop }) {
|
|||||||
}
|
}
|
||||||
]}
|
]}
|
||||||
>
|
>
|
||||||
<Input.TextArea rows={3} />
|
<Input.TextArea autoSize />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<Space wrap>
|
<Space wrap>
|
||||||
<DeleteFilled
|
<DeleteFilled
|
||||||
@@ -737,7 +737,7 @@ export function ShopInfoGeneral({ form, bodyshop }) {
|
|||||||
}
|
}
|
||||||
]}
|
]}
|
||||||
>
|
>
|
||||||
<Input.TextArea rows={3} />
|
<Input.TextArea autoSize />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<Space wrap>
|
<Space wrap>
|
||||||
<DeleteFilled
|
<DeleteFilled
|
||||||
@@ -1187,7 +1187,7 @@ export function ShopInfoGeneral({ form, bodyshop }) {
|
|||||||
key={`${index}line_desc`}
|
key={`${index}line_desc`}
|
||||||
name={[field.name, "line_desc"]}
|
name={[field.name, "line_desc"]}
|
||||||
>
|
>
|
||||||
<Input />
|
<Input.TextArea autoSize />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<Form.Item
|
<Form.Item
|
||||||
label={t("joblines.fields.mod_lbr_ty")}
|
label={t("joblines.fields.mod_lbr_ty")}
|
||||||
@@ -1330,7 +1330,7 @@ export function ShopInfoGeneral({ form, bodyshop }) {
|
|||||||
}
|
}
|
||||||
]}
|
]}
|
||||||
>
|
>
|
||||||
<Input />
|
<Input.TextArea autoSize />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
|
|
||||||
<Space wrap>
|
<Space wrap>
|
||||||
|
|||||||
@@ -4334,6 +4334,70 @@ export function ShopInfoResponsibilityCenterComponent({ bodyshop, form }) {
|
|||||||
<Input />
|
<Input />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
</LayoutFormRow>
|
</LayoutFormRow>
|
||||||
|
|
||||||
|
{InstanceRenderManager({
|
||||||
|
promanager: "USE_ROME",
|
||||||
|
rome: (
|
||||||
|
<LayoutFormRow header={<div>Adjustments</div>} id="refund">
|
||||||
|
{bodyshop.cdk_dealerid || bodyshop.pbs_serialnumber ? (
|
||||||
|
<>
|
||||||
|
<Form.Item
|
||||||
|
label={t("bodyshop.labels.responsibilitycenters.ttl_adjustment")}
|
||||||
|
rules={[
|
||||||
|
{
|
||||||
|
required: true
|
||||||
|
//message: t("general.validation.required"),
|
||||||
|
}
|
||||||
|
]}
|
||||||
|
name={["md_responsibility_centers", "ttl_adjustment", "dms_acctnumber"]}
|
||||||
|
>
|
||||||
|
<Input />
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item
|
||||||
|
label={t("bodyshop.labels.responsibilitycenters.ttl_tax_adjustment")}
|
||||||
|
rules={[
|
||||||
|
{
|
||||||
|
required: true
|
||||||
|
//message: t("general.validation.required"),
|
||||||
|
}
|
||||||
|
]}
|
||||||
|
name={["md_responsibility_centers", "ttl_tax_adjustment", "dms_acctnumber"]}
|
||||||
|
>
|
||||||
|
<Input />
|
||||||
|
</Form.Item>
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
<Form.Item
|
||||||
|
label={t("bodyshop.labels.responsibilitycenters.ttl_adjustment")}
|
||||||
|
rules={[
|
||||||
|
{
|
||||||
|
required: true
|
||||||
|
//message: t("general.validation.required"),
|
||||||
|
}
|
||||||
|
]}
|
||||||
|
name={["md_responsibility_centers", "ttl_adjustment", "accountitem"]}
|
||||||
|
>
|
||||||
|
<Input />
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item
|
||||||
|
label={t("bodyshop.labels.responsibilitycenters.ttl_tax_adjustment")}
|
||||||
|
rules={[
|
||||||
|
{
|
||||||
|
required: true
|
||||||
|
//message: t("general.validation.required"),
|
||||||
|
}
|
||||||
|
]}
|
||||||
|
name={["md_responsibility_centers", "ttl_tax_adjustment", "accountitem"]}
|
||||||
|
>
|
||||||
|
<Input />
|
||||||
|
</Form.Item>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</LayoutFormRow>
|
||||||
|
)
|
||||||
|
})}
|
||||||
|
|
||||||
{Qb_Multi_Ar.treatment === "on" && (
|
{Qb_Multi_Ar.treatment === "on" && (
|
||||||
<LayoutFormRow header={<div>Multiple Payers Item</div>} id="accountitem">
|
<LayoutFormRow header={<div>Multiple Payers Item</div>} id="accountitem">
|
||||||
<Form.Item
|
<Form.Item
|
||||||
|
|||||||
@@ -39,14 +39,13 @@ export function ShopInfoIntellipay({ bodyshop, form }) {
|
|||||||
</Form.Item>
|
</Form.Item>
|
||||||
<Form.Item
|
<Form.Item
|
||||||
label={t("bodyshop.fields.intellipay_config.cash_discount_percentage")}
|
label={t("bodyshop.fields.intellipay_config.cash_discount_percentage")}
|
||||||
valuePropName="checked"
|
|
||||||
dependencies={[["intellipay_config", "enable_cash_discount"]]}
|
dependencies={[["intellipay_config", "enable_cash_discount"]]}
|
||||||
name={["intellipay_config", "cash_discount_percentage"]}
|
name={["intellipay_config", "cash_discount_percentage"]}
|
||||||
rules={[
|
rules={[
|
||||||
({ getFieldsValue }) => ({ required: form.getFieldValue(["intellipay_config", "enable_cash_discount"]) })
|
({ getFieldsValue }) => ({ required: form.getFieldValue(["intellipay_config", "enable_cash_discount"]) })
|
||||||
]}
|
]}
|
||||||
>
|
>
|
||||||
<InputNumber min={0} max={100} precision={1} suffix='%'/>
|
<InputNumber min={0} max={100} precision={1} suffix="%" />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
</LayoutFormRow>
|
</LayoutFormRow>
|
||||||
</>
|
</>
|
||||||
|
|||||||
@@ -81,14 +81,14 @@ export const logImEXEvent = (eventName, additionalParams, stateProp = null) => {
|
|||||||
user: (state.user && state.user.currentUser && state.user.currentUser.email) || null,
|
user: (state.user && state.user.currentUser && state.user.currentUser.email) || null,
|
||||||
...additionalParams
|
...additionalParams
|
||||||
};
|
};
|
||||||
axios.post("/ioevent", {
|
// axios.post("/ioevent", {
|
||||||
useremail: (state.user && state.user.currentUser && state.user.currentUser.email) || null,
|
// useremail: (state.user && state.user.currentUser && state.user.currentUser.email) || null,
|
||||||
bodyshopid: (state.user && state.user.bodyshop && state.user.bodyshop.id) || null,
|
// bodyshopid: (state.user && state.user.bodyshop && state.user.bodyshop.id) || null,
|
||||||
operationName: eventName,
|
// operationName: eventName,
|
||||||
variables: additionalParams,
|
// variables: additionalParams,
|
||||||
dbevent: false,
|
// dbevent: false,
|
||||||
env: `master-AIO|${import.meta.env.VITE_APP_GIT_SHA_DATE}`
|
// env: `master-AIO|${import.meta.env.VITE_APP_GIT_SHA_DATE}`
|
||||||
});
|
// });
|
||||||
// console.log(
|
// console.log(
|
||||||
// "%c[Analytics]",
|
// "%c[Analytics]",
|
||||||
// "background-color: green ;font-weight:bold;",
|
// "background-color: green ;font-weight:bold;",
|
||||||
|
|||||||
@@ -692,6 +692,7 @@ export const GET_JOB_BY_PK = gql`
|
|||||||
tax_str_rt
|
tax_str_rt
|
||||||
tax_sub_rt
|
tax_sub_rt
|
||||||
tax_tow_rt
|
tax_tow_rt
|
||||||
|
tlos_ind
|
||||||
towin
|
towin
|
||||||
towing_payable
|
towing_payable
|
||||||
unit_number
|
unit_number
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
10
hasura/migrations/1731471670370_run_sql_migration/down.sql
Normal file
10
hasura/migrations/1731471670370_run_sql_migration/down.sql
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
-- Could not auto-generate a down migration.
|
||||||
|
-- Please write an appropriate down migration for the SQL below:
|
||||||
|
-- CREATE INDEX jobs_search_gin_ro_number ON jobs USING GIN ((ro_number) gin_trgm_ops);
|
||||||
|
-- CREATE INDEX jobs_search_gin_ownrfn ON jobs USING GIN ((ownr_fn) gin_trgm_ops);
|
||||||
|
-- CREATE INDEX jobs_search_gin_clm_no ON jobs USING GIN ((clm_no) gin_trgm_ops);
|
||||||
|
-- CREATE INDEX jobs_search_gin_plate_no ON jobs USING GIN ((plate_no) gin_trgm_ops);
|
||||||
|
-- CREATE INDEX jobs_search_gin_v_make_desc ON jobs USING GIN (( v_make_desc) gin_trgm_ops);
|
||||||
|
-- CREATE INDEX jobs_search_gin_v_model_desc ON jobs USING GIN (( v_model_desc) gin_trgm_ops);
|
||||||
|
-- CREATE INDEX jobs_search_gin_ownr_ln ON jobs USING GIN (( ownr_ln) gin_trgm_ops);
|
||||||
|
-- CREATE INDEX jobs_search_gin_ownr_co_nm ON jobs USING GIN (( ownr_co_nm) gin_trgm_ops);
|
||||||
8
hasura/migrations/1731471670370_run_sql_migration/up.sql
Normal file
8
hasura/migrations/1731471670370_run_sql_migration/up.sql
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
CREATE INDEX jobs_search_gin_ro_number ON jobs USING GIN ((ro_number) gin_trgm_ops);
|
||||||
|
CREATE INDEX jobs_search_gin_ownrfn ON jobs USING GIN ((ownr_fn) gin_trgm_ops);
|
||||||
|
CREATE INDEX jobs_search_gin_clm_no ON jobs USING GIN ((clm_no) gin_trgm_ops);
|
||||||
|
CREATE INDEX jobs_search_gin_plate_no ON jobs USING GIN ((plate_no) gin_trgm_ops);
|
||||||
|
CREATE INDEX jobs_search_gin_v_make_desc ON jobs USING GIN (( v_make_desc) gin_trgm_ops);
|
||||||
|
CREATE INDEX jobs_search_gin_v_model_desc ON jobs USING GIN (( v_model_desc) gin_trgm_ops);
|
||||||
|
CREATE INDEX jobs_search_gin_ownr_ln ON jobs USING GIN (( ownr_ln) gin_trgm_ops);
|
||||||
|
CREATE INDEX jobs_search_gin_ownr_co_nm ON jobs USING GIN (( ownr_co_nm) gin_trgm_ops);
|
||||||
@@ -1,14 +1,12 @@
|
|||||||
const path = require("path");
|
const path = require("path");
|
||||||
|
const fs = require("fs");
|
||||||
const Dinero = require("dinero.js");
|
const Dinero = require("dinero.js");
|
||||||
const { gql } = require("graphql-request");
|
const { gql } = require("graphql-request");
|
||||||
const queries = require("./server/graphql-client/queries");
|
|
||||||
const GraphQLClient = require("graphql-request").GraphQLClient;
|
|
||||||
const logger = require("./server/utils/logger");
|
|
||||||
const AxiosLib = require("axios").default;
|
const AxiosLib = require("axios").default;
|
||||||
const axios = AxiosLib.create();
|
const axios = AxiosLib.create();
|
||||||
|
const pLimit = require("p-limit");
|
||||||
|
const converter = require("json-2-csv");
|
||||||
|
|
||||||
// Dinero.defaultCurrency = "USD";
|
|
||||||
// Dinero.globalLocale = "en-CA";
|
|
||||||
Dinero.globalRoundingMode = "HALF_EVEN";
|
Dinero.globalRoundingMode = "HALF_EVEN";
|
||||||
const client = require("./server/graphql-client/graphql-client").client;
|
const client = require("./server/graphql-client/graphql-client").client;
|
||||||
require("dotenv").config({
|
require("dotenv").config({
|
||||||
@@ -16,8 +14,9 @@ require("dotenv").config({
|
|||||||
});
|
});
|
||||||
|
|
||||||
async function RunTheTest() {
|
async function RunTheTest() {
|
||||||
const bodyshopids = ["b501bb82-22b2-493a-8a0f-152938194869"];
|
const bodyshopids = ["71f8494c-89f0-43e0-8eb2-820b52d723bc"];
|
||||||
const bearerToken = `Bearer eyJhbGciOiJSUzI1NiIsImtpZCI6ImJhNjI1OTZmNTJmNTJlZDQ0MDQ5Mzk2YmU3ZGYzNGQyYzY0ZjQ1M2UiLCJ0eXAiOiJKV1QifQ.eyJuYW1lIjoiUm9tZSBEZXZlbG9wbWVudCIsImh0dHBzOi8vaGFzdXJhLmlvL2p3dC9jbGFpbXMiOnsieC1oYXN1cmEtZGVmYXVsdC1yb2xlIjoidXNlciIsIngtaGFzdXJhLWFsbG93ZWQtcm9sZXMiOlsidXNlciJdLCJ4LWhhc3VyYS11c2VyLWlkIjoidDZZbTFORGxDRE9QWnIzRjliZ3VXSDRMaFNYMiJ9LCJpb2FkbWluIjp0cnVlLCJpc3MiOiJodHRwczovL3NlY3VyZXRva2VuLmdvb2dsZS5jb20vcm9tZS1wcm9kLTEiLCJhdWQiOiJyb21lLXByb2QtMSIsImF1dGhfdGltZSI6MTcxMDk1MTg1MCwidXNlcl9pZCI6InQ2WW0xTkRsQ0RPUFpyM0Y5Ymd1V0g0TGhTWDIiLCJzdWIiOiJ0NlltMU5EbENET1BacjNGOWJndVdINExoU1gyIiwiaWF0IjoxNzExNTczODI1LCJleHAiOjE3MTE1Nzc0MjUsImVtYWlsIjoicGF0cmlja0Byb21lLmRldiIsImVtYWlsX3ZlcmlmaWVkIjpmYWxzZSwiZmlyZWJhc2UiOnsiaWRlbnRpdGllcyI6eyJlbWFpbCI6WyJwYXRyaWNrQHJvbWUuZGV2Il19LCJzaWduX2luX3Byb3ZpZGVyIjoicGFzc3dvcmQifX0.0kBySA9tJznLYj8TtncHGVWJO0IcmLKP2G1UyyXwaj45kTa25bjT9RWjM-NslX_zjOvrvmQZzisFAb6M1Jf6geNjOMLIqb8bhihhzEZK4CcRfvjT6cpZxnOO2Dp_1Y5OePbvOBS_GlfdsovVWa84OLuhYC5G_3QwHT8_2Cttz4CbrC6M_vd7QsGODJYBbVKMhOdZhzpNq7AbOUh3749WRjLMMobpnZDrmQlsyg3PAqtX1FHO25WQS2rma9QahGDSY736JfbkuZJ2XbNn0axEGpK7RQLUcuRkFUlfKqYplNbR_e1Q3kEfRAZpxBPXZysrDcbDNhbkWCoTmJ3fle55OA`;
|
const bearerToken = `Bearer eyJhbGciOiJSUzI1NiIsImtpZCI6ImI4Y2FjOTViNGE1YWNkZTBiOTY1NzJkZWU4YzhjOTVlZWU0OGNjY2QiLCJ0eXAiOiJKV1QifQ.eyJuYW1lIjoiUGF0cmljayBGaWMgKERFVikiLCJodHRwczovL2hhc3VyYS5pby9qd3QvY2xhaW1zIjp7IngtaGFzdXJhLWRlZmF1bHQtcm9sZSI6InVzZXIiLCJ4LWhhc3VyYS1hbGxvd2VkLXJvbGVzIjpbInVzZXIiXSwieC1oYXN1cmEtdXNlci1pZCI6ImhOSjhBRHB0REhRQkRFcXNCOFFNWVRqaURuZjEifSwiaXNzIjoiaHR0cHM6Ly9zZWN1cmV0b2tlbi5nb29nbGUuY29tL2ltZXgtZGV2IiwiYXVkIjoiaW1leC1kZXYiLCJhdXRoX3RpbWUiOjE3MzAxMzIwMjksInVzZXJfaWQiOiJoTko4QURwdERIUUJERXFzQjhRTVlUamlEbmYxIiwic3ViIjoiaE5KOEFEcHRESFFCREVxc0I4UU1ZVGppRG5mMSIsImlhdCI6MTczMDg0MTc2NSwiZXhwIjoxNzMwODQ1MzY1LCJlbWFpbCI6InBhdHJpY2tAaW1leC5kZXYiLCJlbWFpbF92ZXJpZmllZCI6ZmFsc2UsImZpcmViYXNlIjp7ImlkZW50aXRpZXMiOnsiZW1haWwiOlsicGF0cmlja0BpbWV4LmRldiJdfSwic2lnbl9pbl9wcm92aWRlciI6InBhc3N3b3JkIn19.npQWkyB5cB4wmkaBsQiY3JbvBM9vKPqf3e22nVHnSydGcQi0p9M2mca9FcDtdcWvQlShUM63FF-6KkzpovC92sHauNmzCSXRInaaCPEussUUNSJEe2gEV03tYX447LkkSmFQbJ5V6qLTIDelm25fF0MoEDVnLTgythK_9927f8cxKZH1kEow0ymDeMaWey1sRyu7n15OJMcu692mfuQnBAArGTHGJ4YmReI7tMmdrV438MLxuVpH5CLb6uzlUdZoJ__7yh0kz0lkZEeHQAL8yq-0fISbPeZ5uXuMzYGrHuuKsIPRoeShVSVnF7ov8yTT3_YrCkhYbxl0eSTfBB5OdQ`;
|
||||||
|
|
||||||
const { jobs } = await client.request(
|
const { jobs } = await client.request(
|
||||||
gql`
|
gql`
|
||||||
query GET_JOBS($bodyshopids: [uuid!]!) {
|
query GET_JOBS($bodyshopids: [uuid!]!) {
|
||||||
@@ -36,69 +35,98 @@ async function RunTheTest() {
|
|||||||
|
|
||||||
const results = [];
|
const results = [];
|
||||||
|
|
||||||
for (const [index, job] of jobs.entries()) {
|
const limit = pLimit(5); // Set concurrency limit to 3
|
||||||
process.stdout.cursorTo(0);
|
|
||||||
process.stdout.write(
|
|
||||||
`Processing job ${index + 1} of ${jobs.length}. Failed jobs: ${results.filter((r) => r.result !== "PASS").length}`
|
|
||||||
);
|
|
||||||
|
|
||||||
try {
|
const tasks = jobs.map((job, index) => {
|
||||||
await axios.post(
|
return limit(async () => {
|
||||||
`http://localhost:4000/job/totalsssu`,
|
process.stdout.cursorTo(0);
|
||||||
{ id: job.id },
|
process.stdout.write(
|
||||||
{ headers: { Authorization: bearerToken } }
|
`Processing job ${index + 1} of ${jobs.length}. Failed jobs: ${results.filter((r) => r.overallTotalCorrect !== "PASS").length}. Correct jobs because of adjustment: ${results.filter((r) => r.correctJobsBecauseOfAdjustment).length}`
|
||||||
);
|
);
|
||||||
const { jobs_by_pk: newjob } = await client.request(
|
|
||||||
gql`
|
try {
|
||||||
query GET_JOBS($id: uuid!) {
|
await axios.post(
|
||||||
jobs_by_pk(id: $id) {
|
`http://localhost:4000/job/totalsssu`,
|
||||||
id
|
{ id: job.id },
|
||||||
ro_number
|
{ headers: { Authorization: bearerToken } }
|
||||||
cieca_ttl
|
);
|
||||||
job_totals
|
const { jobs_by_pk: newjob } = await client.request(
|
||||||
ownr_fn
|
gql`
|
||||||
ownr_ln
|
query GET_JOBS($id: uuid!) {
|
||||||
ownr_co_nm
|
jobs_by_pk(id: $id) {
|
||||||
ins_co_nm
|
id
|
||||||
comment
|
ro_number
|
||||||
|
cieca_ttl
|
||||||
|
job_totals
|
||||||
|
ownr_fn
|
||||||
|
ownr_ln
|
||||||
|
ownr_co_nm
|
||||||
|
ins_co_nm
|
||||||
|
comment
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
`,
|
||||||
|
{
|
||||||
|
id: job.id
|
||||||
}
|
}
|
||||||
`,
|
);
|
||||||
{
|
|
||||||
id: job.id
|
const result = {
|
||||||
|
id: newjob.id,
|
||||||
|
owner: `${newjob.ownr_fn} ${newjob.ownr_ln} ${job.ownr_co_nm || ""}`,
|
||||||
|
ins_co: newjob.ins_co_nm,
|
||||||
|
comment: newjob.comment,
|
||||||
|
imexsubtotal: Dinero(newjob.job_totals.totals.subtotal).toFormat("0.00"),
|
||||||
|
imextotalrepair: Dinero(newjob.job_totals.totals.total_repairs).toFormat("0.00"),
|
||||||
|
g_tax: newjob.cieca_ttl.data.g_tax,
|
||||||
|
n_ttl_amt: newjob.cieca_ttl.data.n_ttl_amt,
|
||||||
|
g_ttl_amt: newjob.cieca_ttl.data.g_ttl_amt
|
||||||
|
};
|
||||||
|
|
||||||
|
const calcTotal = newjob.job_totals.totals.total_repairs.amount;
|
||||||
|
const ttlTotal = newjob.cieca_ttl.data.g_ttl_amt * 100;
|
||||||
|
result.difference = (calcTotal - ttlTotal) / 100;
|
||||||
|
|
||||||
|
if (Math.abs(calcTotal - ttlTotal) > 3) {
|
||||||
|
result.overallTotalCorrect = "***FAIL***";
|
||||||
|
} else {
|
||||||
|
result.overallTotalCorrect = "PASS";
|
||||||
}
|
}
|
||||||
);
|
result.ttl_adjustment = Dinero(newjob.job_totals.totals.ttl_adjustment).toFormat();
|
||||||
|
result.ttl_tax_adjustment = Dinero(newjob.job_totals.totals.ttl_tax_adjustment).toFormat();
|
||||||
|
|
||||||
const result = {
|
const calcTaxDinero = Dinero(newjob.job_totals.totals.us_sales_tax_breakdown.ty1Tax)
|
||||||
id: newjob.id,
|
.add(Dinero(newjob.job_totals.totals.us_sales_tax_breakdown.ty2Tax))
|
||||||
owner: `${newjob.ownr_fn} ${newjob.ownr_ln} ${job.ownr_co_nm || ""}`,
|
.add(Dinero(newjob.job_totals.totals.us_sales_tax_breakdown.ty3Tax))
|
||||||
ins_co: newjob.ins_co_nm,
|
.add(Dinero(newjob.job_totals.totals.us_sales_tax_breakdown.ty4Tax))
|
||||||
comment: newjob.comment
|
.add(Dinero(newjob.job_totals.totals.us_sales_tax_breakdown.ty5Tax))
|
||||||
};
|
.add(Dinero(newjob.job_totals.totals.ttl_tax_adjustment));
|
||||||
|
result.calcTax = calcTaxDinero.toFormat("0.00");
|
||||||
|
const calcTax = calcTaxDinero.getAmount() / 100;
|
||||||
|
const emsTax = newjob.cieca_ttl.data.g_tax;
|
||||||
|
result.taxDifference = calcTax - emsTax;
|
||||||
|
|
||||||
const calcTotal = newjob.job_totals.totals.total_repairs.amount;
|
if (Math.abs(calcTax - emsTax) > 3) {
|
||||||
const ttlTotal = newjob.cieca_ttl.data.g_ttl_amt * 100;
|
result.taxCorrect = "***FAIL***";
|
||||||
result.difference = (calcTotal - ttlTotal) / 100;
|
} else {
|
||||||
|
result.taxCorrect = "PASS";
|
||||||
|
}
|
||||||
|
|
||||||
if (Math.abs(calcTotal - ttlTotal) > 3) {
|
results.push(result);
|
||||||
//Diff is greater than 5 cents. Fail it.
|
} catch (error) {
|
||||||
result.result = "***FAIL***";
|
results.push({
|
||||||
} else {
|
ro_number: job.ro_number,
|
||||||
result.result = "PASS";
|
id: job.id,
|
||||||
|
result: error.message
|
||||||
|
});
|
||||||
}
|
}
|
||||||
// console.log(`${result.result} => RO ${job.ro_number} - ${job.id} `);
|
});
|
||||||
|
});
|
||||||
|
|
||||||
results.push(result);
|
await Promise.all(tasks);
|
||||||
} catch (error) {
|
|
||||||
results.push({
|
|
||||||
ro_number: job.ro_number,
|
|
||||||
id: job.id,
|
|
||||||
result: "**503 FAILURE**"
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
console.table(results.filter((r) => r.result !== "PASS"));
|
console.table(results.filter((r) => r.overallTotalCorrect !== "PASS"));
|
||||||
|
console.log("=======================================");
|
||||||
const summary = results.reduce(
|
const summary = results.reduce(
|
||||||
(acc, val) => {
|
(acc, val) => {
|
||||||
if (val.result === "PASS") {
|
if (val.result === "PASS") {
|
||||||
@@ -110,18 +138,12 @@ async function RunTheTest() {
|
|||||||
{ pass: 0, fail: 0 }
|
{ pass: 0, fail: 0 }
|
||||||
);
|
);
|
||||||
console.log("Pass Rate: ", ((summary.pass / (summary.fail + summary.pass)) * 100).toFixed(1));
|
console.log("Pass Rate: ", ((summary.pass / (summary.fail + summary.pass)) * 100).toFixed(1));
|
||||||
|
|
||||||
|
const ret = converter.json2csv(results, { emptyFieldValue: "" });
|
||||||
|
|
||||||
|
fs.writeFile(`./logs/totalstest-${Date.now()}.csv`, ret, (error) => console.log(error));
|
||||||
}
|
}
|
||||||
|
|
||||||
RunTheTest();
|
RunTheTest().catch((error) => {
|
||||||
|
console.log("Error in RunTheTest: ", error);
|
||||||
// mutation {
|
});
|
||||||
// delete_jobs(where: {shopid: {_eq: "a7ee1503-ee05-4a02-b80e-bdb11d1cc8ac"}}) {
|
|
||||||
// affected_rows
|
|
||||||
// }
|
|
||||||
// delete_owners(where: {shopid: {_eq: "a7ee1503-ee05-4a02-b80e-bdb11d1cc8ac"}}) {
|
|
||||||
// affected_rows
|
|
||||||
// }
|
|
||||||
// delete_vehicles(where: {shopid: {_eq: "a7ee1503-ee05-4a02-b80e-bdb11d1cc8ac"}}) {
|
|
||||||
// affected_rows
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|||||||
7
package-lock.json
generated
7
package-lock.json
generated
@@ -65,6 +65,7 @@
|
|||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@trivago/prettier-plugin-sort-imports": "^4.3.0",
|
"@trivago/prettier-plugin-sort-imports": "^4.3.0",
|
||||||
"concurrently": "^8.2.2",
|
"concurrently": "^8.2.2",
|
||||||
|
"p-limit": "^3.1.0",
|
||||||
"prettier": "^3.3.3",
|
"prettier": "^3.3.3",
|
||||||
"source-map-explorer": "^2.5.2"
|
"source-map-explorer": "^2.5.2"
|
||||||
},
|
},
|
||||||
@@ -7706,7 +7707,8 @@
|
|||||||
"version": "3.1.0",
|
"version": "3.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz",
|
||||||
"integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==",
|
"integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==",
|
||||||
"optional": true,
|
"devOptional": true,
|
||||||
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"yocto-queue": "^0.1.0"
|
"yocto-queue": "^0.1.0"
|
||||||
},
|
},
|
||||||
@@ -9700,7 +9702,8 @@
|
|||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz",
|
||||||
"integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==",
|
"integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==",
|
||||||
"optional": true,
|
"devOptional": true,
|
||||||
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=10"
|
"node": ">=10"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -75,6 +75,7 @@
|
|||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@trivago/prettier-plugin-sort-imports": "^4.3.0",
|
"@trivago/prettier-plugin-sort-imports": "^4.3.0",
|
||||||
"concurrently": "^8.2.2",
|
"concurrently": "^8.2.2",
|
||||||
|
"p-limit": "^3.1.0",
|
||||||
"prettier": "^3.3.3",
|
"prettier": "^3.3.3",
|
||||||
"source-map-explorer": "^2.5.2"
|
"source-map-explorer": "^2.5.2"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -548,6 +548,61 @@ exports.default = function ({ bodyshop, jobs_by_pk, qbo = false, items, taxCodes
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (jobs_by_pk.job_totals.totals?.ttl_adjustment) {
|
||||||
|
// Do not need to check for ImEX or Rome because ImEX uses a different totals calculation that will never set this field.
|
||||||
|
if (qbo) {
|
||||||
|
const taxAccountCode = findTaxCode(
|
||||||
|
{
|
||||||
|
local: false,
|
||||||
|
federal: InstanceManager({ imex: true, rome: false }),
|
||||||
|
state: jobs_by_pk.tax_lbr_rt === 0 ? false : true
|
||||||
|
},
|
||||||
|
bodyshop.md_responsibility_centers.sales_tax_codes
|
||||||
|
);
|
||||||
|
|
||||||
|
const QboTaxId = InstanceManager({
|
||||||
|
imex: taxCodes[taxAccountCode],
|
||||||
|
rome: CheckQBOUSATaxID({
|
||||||
|
// jobline: jobline,
|
||||||
|
type: "adjustment",
|
||||||
|
job: jobs_by_pk
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
InvoiceLineAdd.push({
|
||||||
|
DetailType: "SalesItemLineDetail",
|
||||||
|
Amount: Dinero(jobs_by_pk.job_totals.totals?.ttl_adjustment).toFormat(DineroQbFormat),
|
||||||
|
SalesItemLineDetail: {
|
||||||
|
...(jobs_by_pk.class ? { ClassRef: { value: classes[jobs_by_pk.class] } } : {}),
|
||||||
|
ItemRef: {
|
||||||
|
value: items[responsibilityCenters.ttl_adjustment?.accountitem]
|
||||||
|
},
|
||||||
|
TaxCodeRef: {
|
||||||
|
value: QboTaxId
|
||||||
|
},
|
||||||
|
Qty: 1
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
InvoiceLineAdd.push({
|
||||||
|
ItemRef: {
|
||||||
|
FullName: responsibilityCenters.ttl_adjustment?.accountitem
|
||||||
|
},
|
||||||
|
Desc: "Adjustment",
|
||||||
|
Quantity: 1,
|
||||||
|
Amount: Dinero(jobs_by_pk.job_totals.totals?.ttl_adjustment).toFormat(DineroQbFormat),
|
||||||
|
SalesTaxCodeRef: InstanceManager({
|
||||||
|
imex: {
|
||||||
|
FullName: "E"
|
||||||
|
},
|
||||||
|
rome: {
|
||||||
|
FullName: bodyshop.md_responsibility_centers.taxes.itemexemptcode || "NON"
|
||||||
|
}
|
||||||
|
})
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
//Add tax lines
|
//Add tax lines
|
||||||
const job_totals = jobs_by_pk.job_totals;
|
const job_totals = jobs_by_pk.job_totals;
|
||||||
const federal_tax = Dinero(job_totals.totals.federal_tax);
|
const federal_tax = Dinero(job_totals.totals.federal_tax);
|
||||||
@@ -824,7 +879,60 @@ exports.default = function ({ bodyshop, jobs_by_pk, qbo = false, items, taxCodes
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (jobs_by_pk.job_totals.totals.ttl_tax_adjustment) {
|
||||||
|
// Do not need to check for ImEX or Rome because ImEX uses a different totals calculation that will never set this field.
|
||||||
|
if (qbo) {
|
||||||
|
const taxAccountCode = findTaxCode(
|
||||||
|
{
|
||||||
|
local: false,
|
||||||
|
federal: InstanceManager({ imex: true, rome: false }),
|
||||||
|
state: jobs_by_pk.tax_lbr_rt === 0 ? false : true
|
||||||
|
},
|
||||||
|
bodyshop.md_responsibility_centers.sales_tax_codes
|
||||||
|
);
|
||||||
|
|
||||||
|
const QboTaxId = InstanceManager({
|
||||||
|
imex: taxCodes[taxAccountCode],
|
||||||
|
rome: CheckQBOUSATaxID({
|
||||||
|
// jobline: jobline,
|
||||||
|
type: "adjustment",
|
||||||
|
job: jobs_by_pk
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
InvoiceLineAdd.push({
|
||||||
|
DetailType: "SalesItemLineDetail",
|
||||||
|
Amount: Dinero(jobs_by_pk.job_totals.totals?.ttl_tax_adjustment).toFormat(DineroQbFormat),
|
||||||
|
SalesItemLineDetail: {
|
||||||
|
...(jobs_by_pk.class ? { ClassRef: { value: classes[jobs_by_pk.class] } } : {}),
|
||||||
|
ItemRef: {
|
||||||
|
value: items[responsibilityCenters.ttl_tax_adjustment?.accountitem]
|
||||||
|
},
|
||||||
|
TaxCodeRef: {
|
||||||
|
value: QboTaxId
|
||||||
|
},
|
||||||
|
Qty: 1
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
InvoiceLineAdd.push({
|
||||||
|
ItemRef: {
|
||||||
|
FullName: responsibilityCenters.ttl_tax_adjustment?.accountitem
|
||||||
|
},
|
||||||
|
Desc: "Tax Adjustment",
|
||||||
|
Quantity: 1,
|
||||||
|
Amount: Dinero(jobs_by_pk.job_totals.totals?.ttl_tax_adjustment).toFormat(DineroQbFormat),
|
||||||
|
SalesTaxCodeRef: InstanceManager({
|
||||||
|
imex: {
|
||||||
|
FullName: "E"
|
||||||
|
},
|
||||||
|
rome: {
|
||||||
|
FullName: bodyshop.md_responsibility_centers.taxes.itemexemptcode || "NON"
|
||||||
|
}
|
||||||
|
})
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
if (!qbo && InvoiceLineAdd.length === 0) {
|
if (!qbo && InvoiceLineAdd.length === 0) {
|
||||||
//Handle the scenario where there is a $0 sale invoice.
|
//Handle the scenario where there is a $0 sale invoice.
|
||||||
InvoiceLineAdd.push({
|
InvoiceLineAdd.push({
|
||||||
|
|||||||
@@ -219,6 +219,11 @@ async function InsertPayment(oauthClient, qbo_realmId, req, payment, parentRef,
|
|||||||
PaymentMethodRef: {
|
PaymentMethodRef: {
|
||||||
value: paymentMethods[payment.type]
|
value: paymentMethods[payment.type]
|
||||||
},
|
},
|
||||||
|
PrivateNote: payment.memo
|
||||||
|
? payment.memo.length > 4000
|
||||||
|
? payment.memo.substring(0, 4000).trim()
|
||||||
|
: payment.memo.trim()
|
||||||
|
: "",
|
||||||
PaymentRefNum: payment.transactionid,
|
PaymentRefNum: payment.transactionid,
|
||||||
...(invoices && invoices.length === 1 && invoices[0]
|
...(invoices && invoices.length === 1 && invoices[0]
|
||||||
? {
|
? {
|
||||||
|
|||||||
@@ -352,6 +352,7 @@ function calculateAllocations(connectionData, job) {
|
|||||||
// console.log("NO MASH ACCOUNT FOUND!!");
|
// console.log("NO MASH ACCOUNT FOUND!!");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (InstanceManager({ rome: true })) {
|
if (InstanceManager({ rome: true })) {
|
||||||
//profile level adjustments for parts
|
//profile level adjustments for parts
|
||||||
Object.keys(job.job_totals.parts.adjustments).forEach((key) => {
|
Object.keys(job.job_totals.parts.adjustments).forEach((key) => {
|
||||||
@@ -427,6 +428,41 @@ function calculateAllocations(connectionData, job) {
|
|||||||
} else {
|
} else {
|
||||||
return { ...taxAllocations[key], tax: key };
|
return { ...taxAllocations[key], tax: key };
|
||||||
}
|
}
|
||||||
})
|
}),
|
||||||
|
|
||||||
|
...(job.job_totals.totals.ttl_adjustment
|
||||||
|
? [
|
||||||
|
{
|
||||||
|
center: "SUB ADJ",
|
||||||
|
sale: Dinero(job.job_totals.totals.ttl_adjustment),
|
||||||
|
cost: Dinero(),
|
||||||
|
profitCenter: {
|
||||||
|
name: "SUB ADJ",
|
||||||
|
accountdesc: "SUB ADJ",
|
||||||
|
accountitem: "SUB ADJ",
|
||||||
|
accountname: "SUB ADJ",
|
||||||
|
dms_acctnumber: bodyshop.md_responsibility_centers.ttl_adjustment.dms_acctnumber
|
||||||
|
},
|
||||||
|
costCenter: {}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
: []),
|
||||||
|
...(job.job_totals.totals.ttl_tax_adjustment
|
||||||
|
? [
|
||||||
|
{
|
||||||
|
center: "TAX ADJ",
|
||||||
|
sale: Dinero(job.job_totals.totals.ttl_tax_adjustment),
|
||||||
|
cost: Dinero(),
|
||||||
|
profitCenter: {
|
||||||
|
name: "TAX ADJ",
|
||||||
|
accountdesc: "TAX ADJ",
|
||||||
|
accountitem: "TAX ADJ",
|
||||||
|
accountname: "TAX ADJ",
|
||||||
|
dms_acctnumber: bodyshop.md_responsibility_centers.ttl_tax_adjustment.dms_acctnumber
|
||||||
|
},
|
||||||
|
costCenter: {}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
: [])
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -47,7 +47,11 @@ exports.default = async (req, res) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Send immediate response and continue processing.
|
// Send immediate response and continue processing.
|
||||||
res.status(200).send();
|
res.status(202).json({
|
||||||
|
success: true,
|
||||||
|
message: "Processing request ...",
|
||||||
|
timestamp: new Date().toISOString()
|
||||||
|
});
|
||||||
|
|
||||||
try {
|
try {
|
||||||
logger.log("autohouse-start", "DEBUG", "api", null, null);
|
logger.log("autohouse-start", "DEBUG", "api", null, null);
|
||||||
@@ -146,7 +150,7 @@ async function processBatch(batch, start, end) {
|
|||||||
allErrors.push({
|
allErrors.push({
|
||||||
bodyshopid: bodyshop.id,
|
bodyshopid: bodyshop.id,
|
||||||
imexshopid: bodyshop.imexshopid,
|
imexshopid: bodyshop.imexshopid,
|
||||||
autuhouseid: bodyshop.autuhouseid,
|
autohouseid: bodyshop.autohouseid,
|
||||||
fatal: true,
|
fatal: true,
|
||||||
errors: [error.toString()]
|
errors: [error.toString()]
|
||||||
});
|
});
|
||||||
@@ -154,7 +158,7 @@ async function processBatch(batch, start, end) {
|
|||||||
allErrors.push({
|
allErrors.push({
|
||||||
bodyshopid: bodyshop.id,
|
bodyshopid: bodyshop.id,
|
||||||
imexshopid: bodyshop.imexshopid,
|
imexshopid: bodyshop.imexshopid,
|
||||||
autuhouseid: bodyshop.autuhouseid,
|
autohouseid: bodyshop.autohouseid,
|
||||||
errors: erroredJobs.map((ej) => ({
|
errors: erroredJobs.map((ej) => ({
|
||||||
ro_number: ej.job?.ro_number,
|
ro_number: ej.job?.ro_number,
|
||||||
jobid: ej.job?.id,
|
jobid: ej.job?.id,
|
||||||
@@ -176,9 +180,8 @@ async function uploadViaSFTP(allxmlsToUpload) {
|
|||||||
|
|
||||||
for (const xmlObj of allxmlsToUpload) {
|
for (const xmlObj of allxmlsToUpload) {
|
||||||
try {
|
try {
|
||||||
logger.log("autohouse-sftp-upload", "DEBUG", "api", null, { filename: xmlObj.filename });
|
|
||||||
xmlObj.result = await sftp.put(Buffer.from(xmlObj.xml), `${xmlObj.filename}`);
|
xmlObj.result = await sftp.put(Buffer.from(xmlObj.xml), `${xmlObj.filename}`);
|
||||||
logger.log("autohouse-sftp-upload-result", "DEBUG", "api", null, {
|
logger.log("autohouse-sftp-upload", "DEBUG", "api", null, {
|
||||||
filename: xmlObj.filename,
|
filename: xmlObj.filename,
|
||||||
result: xmlObj.result
|
result: xmlObj.result
|
||||||
});
|
});
|
||||||
@@ -609,10 +612,7 @@ const CreateRepairOrderTag = (job, errorCallback) => {
|
|||||||
};
|
};
|
||||||
return ret;
|
return ret;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.log("autohouse-job-calculate-error", "ERROR", "api", null, {
|
logger.log("autohouse-job-calculate-error", "ERROR", "api", null, { error: error.message, stack: error.stack });
|
||||||
error
|
|
||||||
});
|
|
||||||
|
|
||||||
errorCallback({ jobid: job.id, ro_number: job.ro_number, error });
|
errorCallback({ jobid: job.id, ro_number: job.ro_number, error });
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -39,7 +39,11 @@ exports.default = async (req, res) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Send immediate response and continue processing.
|
// Send immediate response and continue processing.
|
||||||
res.status(200).send();
|
res.status(202).json({
|
||||||
|
success: true,
|
||||||
|
message: "Processing request ...",
|
||||||
|
timestamp: new Date().toISOString()
|
||||||
|
});
|
||||||
|
|
||||||
try {
|
try {
|
||||||
logger.log("chatter-start", "DEBUG", "api", null, null);
|
logger.log("chatter-start", "DEBUG", "api", null, null);
|
||||||
@@ -176,9 +180,8 @@ async function uploadViaSFTP(allcsvsToUpload) {
|
|||||||
|
|
||||||
for (const csvObj of allcsvsToUpload) {
|
for (const csvObj of allcsvsToUpload) {
|
||||||
try {
|
try {
|
||||||
logger.log("chatter-sftp-upload", "DEBUG", "api", null, { filename: csvObj.filename });
|
|
||||||
csvObj.result = await sftp.put(Buffer.from(csvObj.csv), `${csvObj.filename}`);
|
csvObj.result = await sftp.put(Buffer.from(csvObj.csv), `${csvObj.filename}`);
|
||||||
logger.log("chatter-sftp-upload-result", "DEBUG", "api", null, {
|
logger.log("chatter-sftp-upload", "DEBUG", "api", null, {
|
||||||
filename: csvObj.filename,
|
filename: csvObj.filename,
|
||||||
result: csvObj.result
|
result: csvObj.result
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -26,174 +26,184 @@ const ftpSetup = {
|
|||||||
password: process.env.CLAIMSCORP_PASSWORD,
|
password: process.env.CLAIMSCORP_PASSWORD,
|
||||||
debug: (message, ...data) => logger.log(message, "DEBUG", "api", null, data),
|
debug: (message, ...data) => logger.log(message, "DEBUG", "api", null, data),
|
||||||
algorithms: {
|
algorithms: {
|
||||||
serverHostKey: ["ssh-rsa", "ssh-dss"]
|
serverHostKey: ["ssh-rsa", "ssh-dss", "rsa-sha2-256", "rsa-sha2-512", "ecdsa-sha2-nistp256", "ecdsa-sha2-nistp384"]
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const allxmlsToUpload = [];
|
||||||
|
const allErrors = [];
|
||||||
|
|
||||||
exports.default = async (req, res) => {
|
exports.default = async (req, res) => {
|
||||||
// Only process if in production environment.
|
// Only process if in production environment.
|
||||||
if (process.env.NODE_ENV !== "production") {
|
if (process.env.NODE_ENV !== "production") {
|
||||||
res.sendStatus(403);
|
res.sendStatus(403);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
// Only process if the appropriate token is provided.
|
||||||
//Query for the List of Bodyshop Clients.
|
|
||||||
logger.log("claimscorp-start", "DEBUG", "api", null, null);
|
|
||||||
const { bodyshops } = await client.request(queries.GET_CLAIMSCORP_SHOPS);
|
|
||||||
|
|
||||||
const specificShopIds = req.body.bodyshopIds; // ['uuid]
|
|
||||||
const { start, end, skipUpload } = req.body; //YYYY-MM-DD
|
|
||||||
if (req.headers["x-imex-auth"] !== process.env.AUTOHOUSE_AUTH_TOKEN) {
|
if (req.headers["x-imex-auth"] !== process.env.AUTOHOUSE_AUTH_TOKEN) {
|
||||||
res.sendStatus(401);
|
res.sendStatus(401);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const allxmlsToUpload = [];
|
|
||||||
const allErrors = [];
|
// Send immediate response and continue processing.
|
||||||
|
res.status(202).json({
|
||||||
|
success: true,
|
||||||
|
message: "Processing request ...",
|
||||||
|
timestamp: new Date().toISOString()
|
||||||
|
});
|
||||||
|
|
||||||
try {
|
try {
|
||||||
for (const bodyshop of specificShopIds ? bodyshops.filter((b) => specificShopIds.includes(b.id)) : bodyshops) {
|
logger.log("claimscorp-start", "DEBUG", "api", null, null);
|
||||||
|
const { bodyshops } = await client.request(queries.GET_CLAIMSCORP_SHOPS); //Query for the List of Bodyshop Clients.
|
||||||
|
const specificShopIds = req.body.bodyshopIds; // ['uuid];
|
||||||
|
const { start, end, skipUpload } = req.body; //YYYY-MM-DD
|
||||||
|
|
||||||
|
const batchSize = 10;
|
||||||
|
|
||||||
|
const shopsToProcess =
|
||||||
|
specificShopIds?.length > 0 ? bodyshops.filter((shop) => specificShopIds.includes(shop.id)) : bodyshops;
|
||||||
|
logger.log("claimscorp-shopsToProcess-generated", "DEBUG", "api", null, null);
|
||||||
|
|
||||||
|
if (shopsToProcess.length === 0) {
|
||||||
|
logger.log("claimscorp-shopsToProcess-empty", "DEBUG", "api", null, null);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const batchPromises = [];
|
||||||
|
for (let i = 0; i < shopsToProcess.length; i += batchSize) {
|
||||||
|
const batch = shopsToProcess.slice(i, i + batchSize);
|
||||||
|
const batchPromise = (async () => {
|
||||||
|
await processBatch(batch, start, end);
|
||||||
|
|
||||||
|
if (skipUpload) {
|
||||||
|
for (const xmlObj of allxmlsToUpload) {
|
||||||
|
fs.writeFileSync(`./logs/${xmlObj.filename}`, xmlObj.xml);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
await uploadViaSFTP(allxmlsToUpload);
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
batchPromises.push(batchPromise);
|
||||||
|
}
|
||||||
|
await Promise.all(batchPromises);
|
||||||
|
await sendServerEmail({
|
||||||
|
subject: `ClaimsCorp Report ${moment().format("MM-DD-YY")}`,
|
||||||
|
text: `Errors:\n${JSON.stringify(allErrors, null, 2)}\n\nUploaded:\n${JSON.stringify(
|
||||||
|
allxmlsToUpload.map((x) => ({ filename: x.filename, count: x.count, result: x.result })),
|
||||||
|
null,
|
||||||
|
2
|
||||||
|
)}`
|
||||||
|
});
|
||||||
|
|
||||||
|
logger.log("claimscorp-end", "DEBUG", "api", null, null);
|
||||||
|
} catch (error) {
|
||||||
|
logger.log("claimscorp-error", "ERROR", "api", null, { error: error.message, stack: error.stack });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
async function processBatch(batch, start, end) {
|
||||||
|
for (const bodyshop of batch) {
|
||||||
|
const erroredJobs = [];
|
||||||
|
try {
|
||||||
logger.log("claimscorp-start-shop-extract", "DEBUG", "api", bodyshop.id, {
|
logger.log("claimscorp-start-shop-extract", "DEBUG", "api", bodyshop.id, {
|
||||||
shopname: bodyshop.shopname
|
shopname: bodyshop.shopname
|
||||||
});
|
});
|
||||||
const erroredJobs = [];
|
|
||||||
try {
|
|
||||||
const { jobs, bodyshops_by_pk } = await client.request(queries.CLAIMSCORP_QUERY, {
|
|
||||||
bodyshopid: bodyshop.id,
|
|
||||||
start: start ? moment(start).startOf("day") : moment().subtract(5, "days").startOf("day"),
|
|
||||||
...(end && { end: moment(end).endOf("day") })
|
|
||||||
});
|
|
||||||
|
|
||||||
const claimsCorpObject = {
|
const { jobs, bodyshops_by_pk } = await client.request(queries.CLAIMSCORP_QUERY, {
|
||||||
DataFeed: {
|
bodyshopid: bodyshop.id,
|
||||||
ShopInfo: {
|
start: start ? moment(start).startOf("day") : moment().subtract(5, "days").startOf("day"),
|
||||||
ShopID: bodyshops_by_pk.claimscorpid,
|
...(end && { end: moment(end).endOf("day") })
|
||||||
ShopName: bodyshops_by_pk.shopname,
|
|
||||||
RO: jobs.map((j) =>
|
|
||||||
CreateRepairOrderTag({ ...j, bodyshop: bodyshops_by_pk }, function ({ job, error }) {
|
|
||||||
erroredJobs.push({ job: job, error: error.toString() });
|
|
||||||
})
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
if (erroredJobs.length > 0) {
|
|
||||||
logger.log("claimscorp-failed-jobs", "ERROR", "api", bodyshop.id, {
|
|
||||||
count: erroredJobs.length,
|
|
||||||
jobs: JSON.stringify(erroredJobs.map((j) => j.job.ro_number))
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
var ret = builder
|
|
||||||
.create(
|
|
||||||
{
|
|
||||||
// version: "1.0",
|
|
||||||
// encoding: "UTF-8",
|
|
||||||
//keepNullNodes: true,
|
|
||||||
},
|
|
||||||
claimsCorpObject
|
|
||||||
)
|
|
||||||
.end({ allowEmptyTags: true });
|
|
||||||
|
|
||||||
allxmlsToUpload.push({
|
|
||||||
count: claimsCorpObject.DataFeed.ShopInfo.RO.length,
|
|
||||||
xml: ret,
|
|
||||||
filename: `${bodyshop.claimscorpid}-${moment().format("YYYYMMDDTHHMMss")}.xml`
|
|
||||||
});
|
|
||||||
|
|
||||||
logger.log("claimscorp-end-shop-extract", "DEBUG", "api", bodyshop.id, {
|
|
||||||
shopname: bodyshop.shopname
|
|
||||||
});
|
|
||||||
} catch (error) {
|
|
||||||
//Error at the shop level.
|
|
||||||
logger.log("claimscorp-error-shop", "ERROR", "api", bodyshop.id, {
|
|
||||||
...error
|
|
||||||
});
|
|
||||||
|
|
||||||
allErrors.push({
|
|
||||||
bodyshopid: bodyshop.id,
|
|
||||||
imexshopid: bodyshop.imexshopid,
|
|
||||||
claimscorpid: bodyshop.claimscorpid,
|
|
||||||
fatal: true,
|
|
||||||
errors: [error.toString()]
|
|
||||||
});
|
|
||||||
} finally {
|
|
||||||
allErrors.push({
|
|
||||||
bodyshopid: bodyshop.id,
|
|
||||||
imexshopid: bodyshop.imexshopid,
|
|
||||||
claimscorpid: bodyshop.claimscorpid,
|
|
||||||
errors: erroredJobs.map((ej) => ({
|
|
||||||
ro_number: ej.job?.ro_number,
|
|
||||||
jobid: ej.job?.id,
|
|
||||||
error: ej.error
|
|
||||||
}))
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (skipUpload) {
|
|
||||||
for (const xmlObj of allxmlsToUpload) {
|
|
||||||
fs.writeFileSync(`./logs/${xmlObj.filename}`, xmlObj.xml);
|
|
||||||
}
|
|
||||||
|
|
||||||
res.json(allxmlsToUpload);
|
|
||||||
sendServerEmail({
|
|
||||||
subject: `ClaimsCorp Report ${moment().format("MM-DD-YY")}`,
|
|
||||||
text: `Errors: ${allErrors.map((e) => JSON.stringify(e, null, 2))}
|
|
||||||
Uploaded: ${JSON.stringify(
|
|
||||||
allxmlsToUpload.map((x) => ({ filename: x.filename, count: x.count })),
|
|
||||||
null,
|
|
||||||
2
|
|
||||||
)}
|
|
||||||
`
|
|
||||||
});
|
});
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
let sftp = new Client();
|
const claimsCorpObject = {
|
||||||
sftp.on("error", (errors) =>
|
DataFeed: {
|
||||||
logger.log("claimscorp-sftp-error", "ERROR", "api", null, {
|
ShopInfo: {
|
||||||
...errors
|
ShopID: bodyshops_by_pk.claimscorpid,
|
||||||
})
|
ShopName: bodyshops_by_pk.shopname,
|
||||||
);
|
RO: jobs.map((j) =>
|
||||||
try {
|
CreateRepairOrderTag({ ...j, bodyshop: bodyshops_by_pk }, function ({ job, error }) {
|
||||||
//Connect to the FTP and upload all.
|
erroredJobs.push({ job: job, error: error.toString() });
|
||||||
|
})
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
await sftp.connect(ftpSetup);
|
if (erroredJobs.length > 0) {
|
||||||
|
logger.log("claimscorp-failed-jobs", "ERROR", "api", bodyshop.id, {
|
||||||
for (const xmlObj of allxmlsToUpload) {
|
count: erroredJobs.length,
|
||||||
logger.log("claimscorp-sftp-upload", "DEBUG", "api", null, {
|
jobs: JSON.stringify(erroredJobs.map((j) => j.job.ro_number))
|
||||||
filename: xmlObj.filename
|
|
||||||
});
|
|
||||||
|
|
||||||
const uploadResult = await sftp.put(Buffer.from(xmlObj.xml), `/${xmlObj.filename}`);
|
|
||||||
logger.log("claimscorp-sftp-upload-result", "DEBUG", "api", null, {
|
|
||||||
uploadResult
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
//***TODO Change filing naming when creating the cron job. IM_ShopInternalName_DDMMYYYY_HHMMSS.xml
|
const ret = builder.create({}, claimsCorpObject).end({ allowEmptyTags: true });
|
||||||
|
|
||||||
|
allxmlsToUpload.push({
|
||||||
|
count: claimsCorpObject.DataFeed.ShopInfo.RO.length,
|
||||||
|
xml: ret,
|
||||||
|
filename: `${bodyshop.claimscorpid}-${moment().format("YYYYMMDDTHHMMss")}.xml`
|
||||||
|
});
|
||||||
|
|
||||||
|
logger.log("claimscorp-end-shop-extract", "DEBUG", "api", bodyshop.id, {
|
||||||
|
shopname: bodyshop.shopname
|
||||||
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.log("claimscorp-sftp-error", "ERROR", "api", null, {
|
//Error at the shop level.
|
||||||
...error
|
logger.log("claimscorp-error-shop", "ERROR", "api", bodyshop.id, { error: error.message, stack: error.stack });
|
||||||
|
|
||||||
|
allErrors.push({
|
||||||
|
bodyshopid: bodyshop.id,
|
||||||
|
imexshopid: bodyshop.imexshopid,
|
||||||
|
claimscorpid: bodyshop.claimscorpid,
|
||||||
|
fatal: true,
|
||||||
|
errors: [error.toString()]
|
||||||
});
|
});
|
||||||
} finally {
|
} finally {
|
||||||
sftp.end();
|
allErrors.push({
|
||||||
|
bodyshopid: bodyshop.id,
|
||||||
|
imexshopid: bodyshop.imexshopid,
|
||||||
|
claimscorpid: bodyshop.claimscorpid,
|
||||||
|
errors: erroredJobs.map((ej) => ({
|
||||||
|
ro_number: ej.job?.ro_number,
|
||||||
|
jobid: ej.job?.id,
|
||||||
|
error: ej.error
|
||||||
|
}))
|
||||||
|
});
|
||||||
}
|
}
|
||||||
sendServerEmail({
|
|
||||||
subject: `ClaimsCorp Report ${moment().format("MM-DD-YY")}`,
|
|
||||||
text: `Errors: ${allErrors.map((e) => JSON.stringify(e, null, 2))}
|
|
||||||
Uploaded: ${JSON.stringify(
|
|
||||||
allxmlsToUpload.map((x) => ({ filename: x.filename, count: x.count })),
|
|
||||||
null,
|
|
||||||
2
|
|
||||||
)}
|
|
||||||
`
|
|
||||||
});
|
|
||||||
res.sendStatus(200);
|
|
||||||
} catch (error) {
|
|
||||||
res.status(200).json(error);
|
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
|
async function uploadViaSFTP(allxmlsToUpload) {
|
||||||
|
const sftp = new Client();
|
||||||
|
sftp.on("error", (errors) =>
|
||||||
|
logger.log("claimscorp-sftp-connection-error", "ERROR", "api", null, { error: errors.message, stack: errors.stack })
|
||||||
|
);
|
||||||
|
try {
|
||||||
|
//Connect to the FTP and upload all.
|
||||||
|
await sftp.connect(ftpSetup);
|
||||||
|
|
||||||
|
for (const xmlObj of allxmlsToUpload) {
|
||||||
|
try {
|
||||||
|
xmlObj.result = await sftp.put(Buffer.from(xmlObj.xml), `${xmlObj.filename}`);
|
||||||
|
logger.log("claimscorp-sftp-upload", "DEBUG", "api", null, {
|
||||||
|
filename: xmlObj.filename,
|
||||||
|
result: xmlObj.result
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
logger.log("claimscorp-sftp-upload-error", "ERROR", "api", null, {
|
||||||
|
filename: xmlObj.filename,
|
||||||
|
error: error.message,
|
||||||
|
stack: error.stack
|
||||||
|
});
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
logger.log("claimscorp-sftp-error", "ERROR", "api", null, { error: error.message, stack: error.stack });
|
||||||
|
throw error;
|
||||||
|
} finally {
|
||||||
|
sftp.end();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const CreateRepairOrderTag = (job, errorCallback) => {
|
const CreateRepairOrderTag = (job, errorCallback) => {
|
||||||
//Level 2
|
//Level 2
|
||||||
@@ -445,10 +455,7 @@ const CreateRepairOrderTag = (job, errorCallback) => {
|
|||||||
};
|
};
|
||||||
return ret;
|
return ret;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.log("claimscorp-job-calculate-error", "ERROR", "api", null, {
|
logger.log("claimscorp-job-calculate-error", "ERROR", "api", null, { error: error.message, stack: error.stack });
|
||||||
error
|
|
||||||
});
|
|
||||||
|
|
||||||
errorCallback({ jobid: job.id, ro_number: job.ro_number, error });
|
errorCallback({ jobid: job.id, ro_number: job.ro_number, error });
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -16,8 +16,7 @@ const { sendServerEmail } = require("../email/sendemail");
|
|||||||
const DineroFormat = "0,0.00";
|
const DineroFormat = "0,0.00";
|
||||||
const DateFormat = "MM/DD/YYYY";
|
const DateFormat = "MM/DD/YYYY";
|
||||||
|
|
||||||
const repairOpCodes = ["OP4", "OP9", "OP10"];
|
const kaizenShopsIDs = ["SUMMIT", "STRATHMORE", "SUNRIDGE", "SHAW"];
|
||||||
const replaceOpCodes = ["OP2", "OP5", "OP11", "OP12"];
|
|
||||||
|
|
||||||
const ftpSetup = {
|
const ftpSetup = {
|
||||||
host: process.env.KAIZEN_HOST,
|
host: process.env.KAIZEN_HOST,
|
||||||
@@ -30,173 +29,179 @@ const ftpSetup = {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const allxmlsToUpload = [];
|
||||||
|
const allErrors = [];
|
||||||
|
|
||||||
exports.default = async (req, res) => {
|
exports.default = async (req, res) => {
|
||||||
// Only process if in production environment.
|
// Only process if in production environment.
|
||||||
if (process.env.NODE_ENV !== "production") {
|
if (process.env.NODE_ENV !== "production") {
|
||||||
res.sendStatus(403);
|
res.sendStatus(403);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
// Only process if the appropriate token is provided.
|
||||||
//Query for the List of Bodyshop Clients.
|
|
||||||
logger.log("kaizen-start", "DEBUG", "api", null, null);
|
|
||||||
const kaizenShopsIDs = ["SUMMIT", "STRATHMORE", "SUNRIDGE", "SHAW"];
|
|
||||||
|
|
||||||
const { bodyshops } = await client.request(queries.GET_KAIZEN_SHOPS, {
|
|
||||||
imexshopid: kaizenShopsIDs
|
|
||||||
});
|
|
||||||
|
|
||||||
const specificShopIds = req.body.bodyshopIds; // ['uuid]
|
|
||||||
const { start, end, skipUpload } = req.body; //YYYY-MM-DD
|
|
||||||
if (req.headers["x-imex-auth"] !== process.env.AUTOHOUSE_AUTH_TOKEN) {
|
if (req.headers["x-imex-auth"] !== process.env.AUTOHOUSE_AUTH_TOKEN) {
|
||||||
res.sendStatus(401);
|
res.sendStatus(401);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const allxmlsToUpload = [];
|
|
||||||
const allErrors = [];
|
// Send immediate response and continue processing.
|
||||||
|
res.status(202).json({
|
||||||
|
success: true,
|
||||||
|
message: "Processing request ...",
|
||||||
|
timestamp: new Date().toISOString()
|
||||||
|
});
|
||||||
|
|
||||||
try {
|
try {
|
||||||
for (const bodyshop of specificShopIds ? bodyshops.filter((b) => specificShopIds.includes(b.id)) : bodyshops) {
|
logger.log("kaizen-start", "DEBUG", "api", null, null);
|
||||||
|
const { bodyshops } = await client.request(queries.GET_KAIZEN_SHOPS, { imexshopid: kaizenShopsIDs }); //Query for the List of Bodyshop Clients.
|
||||||
|
const specificShopIds = req.body.bodyshopIds; // ['uuid];
|
||||||
|
const { start, end, skipUpload } = req.body; //YYYY-MM-DD
|
||||||
|
|
||||||
|
const batchSize = 10;
|
||||||
|
|
||||||
|
const shopsToProcess =
|
||||||
|
specificShopIds?.length > 0 ? bodyshops.filter((shop) => specificShopIds.includes(shop.id)) : bodyshops;
|
||||||
|
logger.log("kaizen-shopsToProcess-generated", "DEBUG", "api", null, null);
|
||||||
|
|
||||||
|
if (shopsToProcess.length === 0) {
|
||||||
|
logger.log("kaizen-shopsToProcess-empty", "DEBUG", "api", null, null);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const batchPromises = [];
|
||||||
|
for (let i = 0; i < shopsToProcess.length; i += batchSize) {
|
||||||
|
const batch = shopsToProcess.slice(i, i + batchSize);
|
||||||
|
const batchPromise = (async () => {
|
||||||
|
await processBatch(batch, start, end);
|
||||||
|
|
||||||
|
if (skipUpload) {
|
||||||
|
for (const xmlObj of allxmlsToUpload) {
|
||||||
|
fs.writeFileSync(`./logs/${xmlObj.filename}`, xmlObj.xml);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
await uploadViaSFTP(allxmlsToUpload);
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
batchPromises.push(batchPromise);
|
||||||
|
}
|
||||||
|
await Promise.all(batchPromises);
|
||||||
|
await sendServerEmail({
|
||||||
|
subject: `Kaizen Report ${moment().format("MM-DD-YY")}`,
|
||||||
|
text: `Errors:\n${JSON.stringify(allErrors, null, 2)}\n\nUploaded:\n${JSON.stringify(
|
||||||
|
allxmlsToUpload.map((x) => ({ filename: x.filename, count: x.count, result: x.result })),
|
||||||
|
null,
|
||||||
|
2
|
||||||
|
)}`
|
||||||
|
});
|
||||||
|
|
||||||
|
logger.log("kaizen-end", "DEBUG", "api", null, null);
|
||||||
|
} catch (error) {
|
||||||
|
logger.log("kaizen-error", "ERROR", "api", null, { error: error.message, stack: error.stack });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
async function processBatch(batch, start, end) {
|
||||||
|
for (const bodyshop of batch) {
|
||||||
|
const erroredJobs = [];
|
||||||
|
try {
|
||||||
logger.log("kaizen-start-shop-extract", "DEBUG", "api", bodyshop.id, {
|
logger.log("kaizen-start-shop-extract", "DEBUG", "api", bodyshop.id, {
|
||||||
shopname: bodyshop.shopname
|
shopname: bodyshop.shopname
|
||||||
});
|
});
|
||||||
const erroredJobs = [];
|
|
||||||
try {
|
|
||||||
const { jobs, bodyshops_by_pk } = await client.request(queries.KAIZEN_QUERY, {
|
|
||||||
bodyshopid: bodyshop.id,
|
|
||||||
start: start ? moment(start).startOf("day") : moment().subtract(5, "days").startOf("day"),
|
|
||||||
...(end && { end: moment(end).endOf("day") })
|
|
||||||
});
|
|
||||||
|
|
||||||
const kaizenObject = {
|
const { jobs, bodyshops_by_pk } = await client.request(queries.KAIZEN_QUERY, {
|
||||||
DataFeed: {
|
bodyshopid: bodyshop.id,
|
||||||
ShopInfo: {
|
start: start ? moment(start).startOf("day") : moment().subtract(5, "days").startOf("day"),
|
||||||
ShopName: bodyshops_by_pk.shopname,
|
...(end && { end: moment(end).endOf("day") })
|
||||||
Jobs: jobs.map((j) =>
|
|
||||||
CreateRepairOrderTag({ ...j, bodyshop: bodyshops_by_pk }, function ({ job, error }) {
|
|
||||||
erroredJobs.push({ job: job, error: error.toString() });
|
|
||||||
})
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
if (erroredJobs.length > 0) {
|
|
||||||
logger.log("kaizen-failed-jobs", "ERROR", "api", bodyshop.id, {
|
|
||||||
count: erroredJobs.length,
|
|
||||||
jobs: JSON.stringify(erroredJobs.map((j) => j.job.ro_number))
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
var ret = builder
|
|
||||||
.create(
|
|
||||||
{
|
|
||||||
// version: "1.0",
|
|
||||||
// encoding: "UTF-8",
|
|
||||||
//keepNullNodes: true,
|
|
||||||
},
|
|
||||||
kaizenObject
|
|
||||||
)
|
|
||||||
.end({ allowEmptyTags: true });
|
|
||||||
|
|
||||||
allxmlsToUpload.push({
|
|
||||||
count: kaizenObject.DataFeed.ShopInfo.Jobs.length,
|
|
||||||
xml: ret,
|
|
||||||
filename: `${bodyshop.shopname}-${moment().format("YYYYMMDDTHHMMss")}.xml`
|
|
||||||
});
|
|
||||||
|
|
||||||
logger.log("kaizen-end-shop-extract", "DEBUG", "api", bodyshop.id, {
|
|
||||||
shopname: bodyshop.shopname
|
|
||||||
});
|
|
||||||
} catch (error) {
|
|
||||||
//Error at the shop level.
|
|
||||||
logger.log("kaizen-error-shop", "ERROR", "api", bodyshop.id, {
|
|
||||||
...error
|
|
||||||
});
|
|
||||||
|
|
||||||
allErrors.push({
|
|
||||||
bodyshopid: bodyshop.id,
|
|
||||||
imexshopid: bodyshop.imexshopid,
|
|
||||||
shopname: bodyshop.shopname,
|
|
||||||
fatal: true,
|
|
||||||
errors: [error.toString()]
|
|
||||||
});
|
|
||||||
} finally {
|
|
||||||
allErrors.push({
|
|
||||||
bodyshopid: bodyshop.id,
|
|
||||||
imexshopid: bodyshop.imexshopid,
|
|
||||||
shopname: bodyshop.shopname,
|
|
||||||
errors: erroredJobs.map((ej) => ({
|
|
||||||
ro_number: ej.job?.ro_number,
|
|
||||||
jobid: ej.job?.id,
|
|
||||||
error: ej.error
|
|
||||||
}))
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (skipUpload) {
|
|
||||||
for (const xmlObj of allxmlsToUpload) {
|
|
||||||
fs.writeFileSync(`./logs/${xmlObj.filename}`, xmlObj.xml);
|
|
||||||
}
|
|
||||||
|
|
||||||
res.json(allxmlsToUpload);
|
|
||||||
sendServerEmail({
|
|
||||||
subject: `Kaizen Report ${moment().format("MM-DD-YY")}`,
|
|
||||||
text: `Errors: ${allErrors.map((e) => JSON.stringify(e, null, 2))}
|
|
||||||
Uploaded: ${JSON.stringify(
|
|
||||||
allxmlsToUpload.map((x) => ({ filename: x.filename, count: x.count })),
|
|
||||||
null,
|
|
||||||
2
|
|
||||||
)}
|
|
||||||
`
|
|
||||||
});
|
});
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
let sftp = new Client();
|
const kaizenObject = {
|
||||||
sftp.on("error", (errors) =>
|
DataFeed: {
|
||||||
logger.log("kaizen-sftp-error", "ERROR", "api", null, {
|
ShopInfo: {
|
||||||
...errors
|
ShopName: bodyshops_by_pk.shopname,
|
||||||
})
|
Jobs: jobs.map((j) =>
|
||||||
);
|
CreateRepairOrderTag({ ...j, bodyshop: bodyshops_by_pk }, function ({ job, error }) {
|
||||||
try {
|
erroredJobs.push({ job: job, error: error.toString() });
|
||||||
//Connect to the FTP and upload all.
|
})
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
await sftp.connect(ftpSetup);
|
if (erroredJobs.length > 0) {
|
||||||
|
logger.log("kaizen-failed-jobs", "ERROR", "api", bodyshop.id, {
|
||||||
for (const xmlObj of allxmlsToUpload) {
|
count: erroredJobs.length,
|
||||||
logger.log("kaizen-sftp-upload", "DEBUG", "api", null, {
|
jobs: JSON.stringify(erroredJobs.map((j) => j.job.ro_number))
|
||||||
filename: xmlObj.filename
|
|
||||||
});
|
|
||||||
|
|
||||||
const uploadResult = await sftp.put(Buffer.from(xmlObj.xml), `/${xmlObj.filename}`);
|
|
||||||
logger.log("kaizen-sftp-upload-result", "DEBUG", "api", null, {
|
|
||||||
uploadResult
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
//***TODO Change filing naming when creating the cron job. IM_ShopInternalName_DDMMYYYY_HHMMSS.xml
|
const ret = builder.create({}, kaizenObject).end({ allowEmptyTags: true });
|
||||||
|
|
||||||
|
allxmlsToUpload.push({
|
||||||
|
count: kaizenObject.DataFeed.ShopInfo.Jobs.length,
|
||||||
|
xml: ret,
|
||||||
|
filename: `${bodyshop.shopname}-${moment().format("YYYYMMDDTHHMMss")}.xml`
|
||||||
|
});
|
||||||
|
|
||||||
|
logger.log("kaizen-end-shop-extract", "DEBUG", "api", bodyshop.id, {
|
||||||
|
shopname: bodyshop.shopname
|
||||||
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.log("kaizen-sftp-error", "ERROR", "api", null, {
|
//Error at the shop level.
|
||||||
...error
|
logger.log("kaizen-error-shop", "ERROR", "api", bodyshop.id, { error: error.message, stack: error.stack });
|
||||||
|
|
||||||
|
allErrors.push({
|
||||||
|
bodyshopid: bodyshop.id,
|
||||||
|
imexshopid: bodyshop.imexshopid,
|
||||||
|
shopname: bodyshop.shopname,
|
||||||
|
fatal: true,
|
||||||
|
errors: [error.toString()]
|
||||||
});
|
});
|
||||||
} finally {
|
} finally {
|
||||||
sftp.end();
|
allErrors.push({
|
||||||
|
bodyshopid: bodyshop.id,
|
||||||
|
imexshopid: bodyshop.imexshopid,
|
||||||
|
shopname: bodyshop.shopname,
|
||||||
|
errors: erroredJobs.map((ej) => ({
|
||||||
|
ro_number: ej.job?.ro_number,
|
||||||
|
jobid: ej.job?.id,
|
||||||
|
error: ej.error
|
||||||
|
}))
|
||||||
|
});
|
||||||
}
|
}
|
||||||
sendServerEmail({
|
|
||||||
subject: `Kaizen Report ${moment().format("MM-DD-YY")}`,
|
|
||||||
text: `Errors: ${allErrors.map((e) => JSON.stringify(e, null, 2))}
|
|
||||||
Uploaded: ${JSON.stringify(
|
|
||||||
allxmlsToUpload.map((x) => ({ filename: x.filename, count: x.count })),
|
|
||||||
null,
|
|
||||||
2
|
|
||||||
)}
|
|
||||||
`
|
|
||||||
});
|
|
||||||
res.sendStatus(200);
|
|
||||||
} catch (error) {
|
|
||||||
res.status(200).json(error);
|
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
|
async function uploadViaSFTP(allxmlsToUpload) {
|
||||||
|
const sftp = new Client();
|
||||||
|
sftp.on("error", (errors) =>
|
||||||
|
logger.log("kaizen-sftp-connection-error", "ERROR", "api", null, { error: errors.message, stack: errors.stack })
|
||||||
|
);
|
||||||
|
try {
|
||||||
|
//Connect to the FTP and upload all.
|
||||||
|
await sftp.connect(ftpSetup);
|
||||||
|
|
||||||
|
for (const xmlObj of allxmlsToUpload) {
|
||||||
|
try {
|
||||||
|
xmlObj.result = await sftp.put(Buffer.from(xmlObj.xml), `${xmlObj.filename}`);
|
||||||
|
logger.log("kaizen-sftp-upload", "DEBUG", "api", null, {
|
||||||
|
filename: xmlObj.filename,
|
||||||
|
result: xmlObj.result
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
logger.log("kaizen-sftp-upload-error", "ERROR", "api", null, {
|
||||||
|
filename: xmlObj.filename,
|
||||||
|
error: error.message,
|
||||||
|
stack: error.stack
|
||||||
|
});
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
logger.log("kaizen-sftp-error", "ERROR", "api", null, { error: error.message, stack: error.stack });
|
||||||
|
throw error;
|
||||||
|
} finally {
|
||||||
|
sftp.end();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const CreateRepairOrderTag = (job, errorCallback) => {
|
const CreateRepairOrderTag = (job, errorCallback) => {
|
||||||
//Level 2
|
//Level 2
|
||||||
@@ -420,10 +425,7 @@ const CreateRepairOrderTag = (job, errorCallback) => {
|
|||||||
};
|
};
|
||||||
return ret;
|
return ret;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.log("kaizen-job-calculate-error", "ERROR", "api", null, {
|
logger.log("kaizen-job-calculate-error", "ERROR", "api", null, { error: error.message, stack: error.stack });
|
||||||
error
|
|
||||||
});
|
|
||||||
|
|
||||||
errorCallback({ jobid: job.id, ro_number: job.ro_number, error });
|
errorCallback({ jobid: job.id, ro_number: job.ro_number, error });
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1365,6 +1365,7 @@ exports.GET_JOB_BY_PK = `query GET_JOB_BY_PK($id: uuid!) {
|
|||||||
cieca_pfl
|
cieca_pfl
|
||||||
cieca_pft
|
cieca_pft
|
||||||
cieca_pfo
|
cieca_pfo
|
||||||
|
cieca_ttl
|
||||||
vehicle {
|
vehicle {
|
||||||
id
|
id
|
||||||
notes
|
notes
|
||||||
|
|||||||
@@ -849,6 +849,41 @@ function GenerateCostingData(job) {
|
|||||||
gppercent: formatGpPercent(0)
|
gppercent: formatGpPercent(0)
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
//Push adjustments to bottom line.
|
||||||
|
if (job.job_totals?.totals?.ttl_adjustment) {
|
||||||
|
//Add to totals.
|
||||||
|
const Adjustment = Dinero(job.job_totals.totals.ttl_adjustment); //Need to invert, since this is being assigned as a cost.
|
||||||
|
summaryData.totalAdditionalSales = summaryData.totalAdditionalSales.add(Adjustment);
|
||||||
|
summaryData.totalSales = summaryData.totalSales.add(Adjustment);
|
||||||
|
//Add to lines.
|
||||||
|
costCenterData.push({
|
||||||
|
id: "AdjEst",
|
||||||
|
cost_center: "Adjustment (Est. Match)",
|
||||||
|
sale_labor: Dinero().toFormat(),
|
||||||
|
sale_labor_dinero: Dinero(),
|
||||||
|
sale_parts: Dinero().toFormat(),
|
||||||
|
sale_parts_dinero: Dinero(),
|
||||||
|
sale_additional: Adjustment.toFormat(),
|
||||||
|
sale_additional_dinero: Adjustment,
|
||||||
|
sale_sublet: Dinero(),
|
||||||
|
sale_sublet_dinero: Dinero(),
|
||||||
|
sales: Adjustment.toFormat(),
|
||||||
|
sales_dinero: Adjustment,
|
||||||
|
cost_parts: Dinero().toFormat(),
|
||||||
|
cost_parts_dinero: Dinero(),
|
||||||
|
cost_labor: Dinero().toFormat(), //Adjustment.toFormat(),
|
||||||
|
cost_labor_dinero: Dinero(), // Adjustment,
|
||||||
|
cost_additional: Dinero(),
|
||||||
|
cost_additional_dinero: Dinero(),
|
||||||
|
cost_sublet: Dinero(),
|
||||||
|
cost_sublet_dinero: Dinero(),
|
||||||
|
costs: Dinero().toFormat(),
|
||||||
|
costs_dinero: Dinero(),
|
||||||
|
gpdollars_dinero: Dinero(),
|
||||||
|
gpdollars: Dinero().toFormat(),
|
||||||
|
gppercent: formatGpPercent(0)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
//Final summary data massaging.
|
//Final summary data massaging.
|
||||||
|
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ exports.totalsSsu = async function (req, res) {
|
|||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.log("job-totals-ssu-USA-error", "ERROR", req?.user?.email, id, {
|
logger.log("job-totals-ssu-USA-error", "ERROR", req?.user?.email, id, {
|
||||||
jobid: id,
|
jobid: id,
|
||||||
error
|
error: error.message
|
||||||
});
|
});
|
||||||
res.status(503).send();
|
res.status(503).send();
|
||||||
}
|
}
|
||||||
@@ -68,6 +68,54 @@ async function TotalsServerSide(req, res) {
|
|||||||
ret.additional = CalculateAdditional(job);
|
ret.additional = CalculateAdditional(job);
|
||||||
ret.totals = CalculateTaxesTotals(job, ret);
|
ret.totals = CalculateTaxesTotals(job, ret);
|
||||||
|
|
||||||
|
// Sub total scrubbbing.
|
||||||
|
const emsTotal =
|
||||||
|
job.cieca_ttl.data.n_ttl_amt === job.cieca_ttl.data.g_ttl_amt //It looks like sometimes, gross and net are the same, but they shouldn't be.
|
||||||
|
? job.cieca_ttl.data.n_ttl_amt - job.cieca_ttl.data.g_tax
|
||||||
|
: job.cieca_ttl.data.g_ttl_amt - job.cieca_ttl.data.g_tax; //If they are, adjust the gross total down by the tax amount.
|
||||||
|
const ttlDifference =
|
||||||
|
emsTotal -
|
||||||
|
ret.totals.subtotal
|
||||||
|
.add(
|
||||||
|
Dinero({
|
||||||
|
amount: Math.round((job.adjustment_bottom_line || 0) * 100)
|
||||||
|
}).multiply(-1) //Add back in the adjustment to the subtotal. We don't want to scrub it twice.
|
||||||
|
)
|
||||||
|
.getAmount() /
|
||||||
|
100;
|
||||||
|
|
||||||
|
if (Math.abs(ttlDifference) > 0.0) {
|
||||||
|
//If difference is greater than a pennny, we need to adjust it.
|
||||||
|
ret.totals.ttl_adjustment = Dinero({ amount: Math.round(ttlDifference * 100) });
|
||||||
|
ret.totals.subtotal = ret.totals.subtotal.add(ret.totals.ttl_adjustment);
|
||||||
|
ret.totals.total_repairs = ret.totals.total_repairs.add(ret.totals.ttl_adjustment);
|
||||||
|
ret.totals.net_repairs = ret.totals.net_repairs.add(ret.totals.ttl_adjustment);
|
||||||
|
logger.log("job-totals-USA-ttl-adj", "DEBUG", null, job.id, {
|
||||||
|
adjAmount: ttlDifference
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
//Taxes Scrubbing
|
||||||
|
const emsTaxTotal = job.cieca_ttl.data.g_tax;
|
||||||
|
const totalUsTaxes =
|
||||||
|
(ret.totals.us_sales_tax_breakdown.ty1Tax.getAmount() +
|
||||||
|
ret.totals.us_sales_tax_breakdown.ty2Tax.getAmount() +
|
||||||
|
ret.totals.us_sales_tax_breakdown.ty3Tax.getAmount() +
|
||||||
|
ret.totals.us_sales_tax_breakdown.ty4Tax.getAmount() +
|
||||||
|
ret.totals.us_sales_tax_breakdown.ty5Tax.getAmount()) /
|
||||||
|
100;
|
||||||
|
const ttlTaxDifference = emsTaxTotal - totalUsTaxes;
|
||||||
|
|
||||||
|
if (Math.abs(ttlTaxDifference) > 0.0) {
|
||||||
|
//If difference is greater than a pennny, we need to adjust it.
|
||||||
|
ret.totals.ttl_tax_adjustment = Dinero({ amount: Math.round(ttlTaxDifference * 100) });
|
||||||
|
ret.totals.total_repairs = ret.totals.total_repairs.add(ret.totals.ttl_tax_adjustment);
|
||||||
|
ret.totals.net_repairs = ret.totals.net_repairs.add(ret.totals.ttl_tax_adjustment);
|
||||||
|
logger.log("job-totals-USA-ttl-tax-adj", "DEBUG", null, job.id, {
|
||||||
|
adjAmount: ttlTaxDifference
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.log("job-totals-ssu-USA-error", "ERROR", req.user?.email, job.id, {
|
logger.log("job-totals-ssu-USA-error", "ERROR", req.user?.email, job.id, {
|
||||||
@@ -842,17 +890,21 @@ function CalculateTaxesTotals(job, otherTotals) {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
//Add towing and storage taxable amounts
|
//Add towing and storage taxable amounts
|
||||||
const stlTowing = job.cieca_stl?.data.find((c) => c.ttl_typecd === "OTTW");
|
const stlTowing = job.cieca_stl?.data.find((c) => c.ttl_typecd === "OTTW" || c.ttl_type === "OTTW");
|
||||||
const stlStorage = job.cieca_stl?.data.find((c) => c.ttl_typecd === "OTST");
|
const stlStorage = job.cieca_stl?.data.find((c) => c.ttl_typecd === "OTST" || c.ttl_type === "OTST");
|
||||||
|
|
||||||
if (stlTowing)
|
if (stlTowing)
|
||||||
taxableAmounts.TOW = Dinero({
|
taxableAmounts.TOW = taxableAmounts.TOW.add(
|
||||||
amount: Math.round(stlTowing.t_amt * 100)
|
Dinero({
|
||||||
});
|
amount: Math.round(stlTowing.t_amt * 100)
|
||||||
|
})
|
||||||
|
);
|
||||||
if (stlStorage)
|
if (stlStorage)
|
||||||
taxableAmounts.TOW = Dinero({
|
taxableAmounts.TOW = taxableAmounts.TOW.add(
|
||||||
amount: Math.round(stlStorage.t_amt * 100)
|
(taxableAmounts.TOW = Dinero({
|
||||||
});
|
amount: Math.round(stlStorage.t_amt * 100)
|
||||||
|
}))
|
||||||
|
);
|
||||||
|
|
||||||
const pfp = job.parts_tax_rates;
|
const pfp = job.parts_tax_rates;
|
||||||
|
|
||||||
@@ -959,7 +1011,7 @@ function CalculateTaxesTotals(job, otherTotals) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.log("job-totals-USA Key with issue", "error", null, null, {
|
logger.log("job-totals-USA Key with issue", "error", null, job.id, {
|
||||||
key
|
key
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -989,7 +1041,7 @@ function CalculateTaxesTotals(job, otherTotals) {
|
|||||||
for (let threshCounter = 1; threshCounter <= 5; threshCounter++) {
|
for (let threshCounter = 1; threshCounter <= 5; threshCounter++) {
|
||||||
const thresholdAmount = parseFloat(job.cieca_pft[`ty${tyCounter}_thres${threshCounter}`]) || 0;
|
const thresholdAmount = parseFloat(job.cieca_pft[`ty${tyCounter}_thres${threshCounter}`]) || 0;
|
||||||
const thresholdTaxRate = parseFloat(job.cieca_pft[`ty${tyCounter}_rate${threshCounter}`]) || 0;
|
const thresholdTaxRate = parseFloat(job.cieca_pft[`ty${tyCounter}_rate${threshCounter}`]) || 0;
|
||||||
|
// console.log(taxTierKey, tyCounter, threshCounter, thresholdAmount, thresholdTaxRate);
|
||||||
let taxableAmountInThisThreshold;
|
let taxableAmountInThisThreshold;
|
||||||
if (
|
if (
|
||||||
thresholdAmount === 9999.99 ||
|
thresholdAmount === 9999.99 ||
|
||||||
@@ -1013,11 +1065,8 @@ function CalculateTaxesTotals(job, otherTotals) {
|
|||||||
taxableAmountInThisThreshold = Dinero({
|
taxableAmountInThisThreshold = Dinero({
|
||||||
amount: Math.round(thresholdAmount * 100)
|
amount: Math.round(thresholdAmount * 100)
|
||||||
});
|
});
|
||||||
remainingTaxableAmounts[taxTierKey] = remainingTaxableAmounts[taxTierKey].subtract(
|
remainingTaxableAmounts[taxTierKey] =
|
||||||
Dinero({
|
remainingTaxableAmounts[taxTierKey].subtract(taxableAmountInThisThreshold);
|
||||||
amount: Math.round(taxableAmountInThisThreshold * 100)
|
|
||||||
})
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1026,8 +1075,8 @@ function CalculateTaxesTotals(job, otherTotals) {
|
|||||||
totalTaxByTier[taxTierKey] = totalTaxByTier[taxTierKey].add(taxAmountToAdd);
|
totalTaxByTier[taxTierKey] = totalTaxByTier[taxTierKey].add(taxAmountToAdd);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.log("job-totals-USA - PFP Calculation Error", "error", null, null, {
|
logger.log("job-totals-USA - PFP Calculation Error", "error", null, job.id, {
|
||||||
error
|
error: error.message
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -95,14 +95,14 @@ const createS3Client = () => {
|
|||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
return {
|
return {
|
||||||
uploadFileToS3,
|
uploadFileToS3,
|
||||||
downloadFileFromS3,
|
downloadFileFromS3,
|
||||||
listFilesInS3Bucket,
|
listFilesInS3Bucket,
|
||||||
deleteFileFromS3,
|
deleteFileFromS3,
|
||||||
copyFileInS3,
|
copyFileInS3,
|
||||||
fileExistsInS3
|
fileExistsInS3,
|
||||||
|
...s3Client
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user