Merge remote-tracking branch 'origin/master-AIO' into feature/IO-3000-Migrate-MSG-to-Sockets
This commit is contained in:
17
.vscode/settings.json
vendored
17
.vscode/settings.json
vendored
@@ -13,13 +13,30 @@
|
||||
"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"
|
||||
]
|
||||
}
|
||||
|
||||
@@ -11156,6 +11156,27 @@
|
||||
</translation>
|
||||
</translations>
|
||||
</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>
|
||||
<name>insurancecos</name>
|
||||
<definition_loaded>false</definition_loaded>
|
||||
@@ -11198,27 +11219,6 @@
|
||||
</translation>
|
||||
</translations>
|
||||
</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>
|
||||
<name>intellipay_cash_discount</name>
|
||||
<definition_loaded>false</definition_loaded>
|
||||
@@ -11747,6 +11747,48 @@
|
||||
</translation>
|
||||
</translations>
|
||||
</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>
|
||||
</folder_node>
|
||||
<folder_node>
|
||||
@@ -11775,6 +11817,27 @@
|
||||
</concept_node>
|
||||
</children>
|
||||
</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>
|
||||
<name>scheduling</name>
|
||||
<definition_loaded>false</definition_loaded>
|
||||
@@ -36253,6 +36316,27 @@
|
||||
</translation>
|
||||
</translations>
|
||||
</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>
|
||||
<name>total_repairs</name>
|
||||
<definition_loaded>false</definition_loaded>
|
||||
@@ -48360,6 +48444,27 @@
|
||||
</translation>
|
||||
</translations>
|
||||
</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>
|
||||
<name>tasks_on_board</name>
|
||||
<definition_loaded>false</definition_loaded>
|
||||
@@ -48402,6 +48507,27 @@
|
||||
</translation>
|
||||
</translations>
|
||||
</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>
|
||||
<name>total_amount_on_board</name>
|
||||
<definition_loaded>false</definition_loaded>
|
||||
@@ -48444,6 +48570,27 @@
|
||||
</translation>
|
||||
</translations>
|
||||
</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>
|
||||
<name>total_hours_on_board</name>
|
||||
<definition_loaded>false</definition_loaded>
|
||||
@@ -48465,6 +48612,27 @@
|
||||
</translation>
|
||||
</translations>
|
||||
</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>
|
||||
<name>total_jobs_on_board</name>
|
||||
<definition_loaded>false</definition_loaded>
|
||||
@@ -48507,6 +48675,27 @@
|
||||
</translation>
|
||||
</translations>
|
||||
</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>
|
||||
<name>total_lab_on_board</name>
|
||||
<definition_loaded>false</definition_loaded>
|
||||
@@ -48549,6 +48738,27 @@
|
||||
</translation>
|
||||
</translations>
|
||||
</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>
|
||||
<name>total_lar_on_board</name>
|
||||
<definition_loaded>false</definition_loaded>
|
||||
@@ -48724,6 +48934,27 @@
|
||||
</translation>
|
||||
</translations>
|
||||
</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>
|
||||
<name>tasks_on_board</name>
|
||||
<definition_loaded>false</definition_loaded>
|
||||
@@ -48766,6 +48997,27 @@
|
||||
</translation>
|
||||
</translations>
|
||||
</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>
|
||||
<name>total_amount_on_board</name>
|
||||
<definition_loaded>false</definition_loaded>
|
||||
@@ -48808,6 +49060,27 @@
|
||||
</translation>
|
||||
</translations>
|
||||
</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>
|
||||
<name>total_hours_on_board</name>
|
||||
<definition_loaded>false</definition_loaded>
|
||||
@@ -48829,6 +49102,27 @@
|
||||
</translation>
|
||||
</translations>
|
||||
</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>
|
||||
<name>total_jobs_on_board</name>
|
||||
<definition_loaded>false</definition_loaded>
|
||||
@@ -48871,6 +49165,27 @@
|
||||
</translation>
|
||||
</translations>
|
||||
</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>
|
||||
<name>total_lab_on_board</name>
|
||||
<definition_loaded>false</definition_loaded>
|
||||
@@ -48913,6 +49228,27 @@
|
||||
</translation>
|
||||
</translations>
|
||||
</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>
|
||||
<name>total_lar_on_board</name>
|
||||
<definition_loaded>false</definition_loaded>
|
||||
@@ -51761,6 +52097,27 @@
|
||||
</translation>
|
||||
</translations>
|
||||
</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>
|
||||
<name>production_over_time</name>
|
||||
<definition_loaded>false</definition_loaded>
|
||||
@@ -54225,6 +54582,27 @@
|
||||
</translation>
|
||||
</translations>
|
||||
</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>
|
||||
<name>description</name>
|
||||
<definition_loaded>false</definition_loaded>
|
||||
@@ -54487,6 +54865,27 @@
|
||||
</translation>
|
||||
</translations>
|
||||
</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>
|
||||
<name>remind_at</name>
|
||||
<definition_loaded>false</definition_loaded>
|
||||
|
||||
@@ -105,7 +105,8 @@ const DateTimePicker = ({
|
||||
<TimePicker
|
||||
format="hh:mm a"
|
||||
minuteStep={15}
|
||||
defaultOpenValue={dayjs()
|
||||
defaultOpenValue={dayjs(value)
|
||||
.hour(dayjs().hour())
|
||||
.minute(Math.floor(dayjs().minute() / 15) * 15)
|
||||
.second(0)}
|
||||
onChange={(value) => {
|
||||
|
||||
@@ -141,14 +141,16 @@ export function JobTotalsTableTotals({ bodyshop, job }) {
|
||||
key: t("jobs.fields.ded_amt"),
|
||||
total: job.job_totals.totals.custPayable.deductible
|
||||
},
|
||||
...(InstanceRenderManager({
|
||||
imex: [{
|
||||
key: t("jobs.fields.federal_tax_payable"),
|
||||
total: job.job_totals.totals.custPayable.federal_tax
|
||||
}],
|
||||
...InstanceRenderManager({
|
||||
imex: [
|
||||
{
|
||||
key: t("jobs.fields.federal_tax_payable"),
|
||||
total: job.job_totals.totals.custPayable.federal_tax
|
||||
}
|
||||
],
|
||||
rome: [],
|
||||
promanager: "USE_ROME"
|
||||
})),
|
||||
}),
|
||||
{
|
||||
key: t("jobs.fields.other_amount_payable"),
|
||||
total: job.job_totals.totals.custPayable.other_customer_amount
|
||||
@@ -158,11 +160,32 @@ export function JobTotalsTableTotals({ bodyshop, job }) {
|
||||
total: job.job_totals.totals.custPayable.dep_taxes
|
||||
},
|
||||
|
||||
{
|
||||
key: t("jobs.labels.total_cust_payable"),
|
||||
total: job.job_totals.totals.custPayable.total,
|
||||
bold: true
|
||||
},
|
||||
...(bodyshop.intellipay_config?.enable_cash_discount
|
||||
? [
|
||||
{
|
||||
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"),
|
||||
total: job.job_totals.totals.net_repairs,
|
||||
|
||||
@@ -5,6 +5,7 @@ import { connect } from "react-redux";
|
||||
import { createStructuredSelector } from "reselect";
|
||||
import { selectJobReadOnly } from "../../redux/application/application.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 FormItemEmail from "../form-items-formatted/email-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 JobsDetailChangeFileHandler from "../jobs-detail-change-filehandler/jobs-detail-change-filehandler.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({
|
||||
jobRO: selectJobReadOnly,
|
||||
@@ -185,6 +185,9 @@ export function JobsDetailGeneral({ bodyshop, jobRO, job, form }) {
|
||||
<Form.Item label={t("jobs.fields.towin")} name="towin" valuePropName="checked">
|
||||
<Switch disabled={jobRO} />
|
||||
</Form.Item>
|
||||
<Form.Item label={t("jobs.fields.tlos_ind")} name="tlos_ind" valuePropName="checked">
|
||||
<Switch disabled={jobRO} />
|
||||
</Form.Item>
|
||||
</FormRow>
|
||||
</Col>
|
||||
<Col {...lossColDamage}>
|
||||
|
||||
@@ -142,7 +142,7 @@ export function ShopInfoComponent({ bodyshop, form, saveLoading }) {
|
||||
rome: [
|
||||
{
|
||||
key: "intellipay",
|
||||
label: t("bodyshop.labels.intellipay"),
|
||||
label: InstanceRenderManager({ rome: t("bodyshop.labels.romepay"), imex: t("bodyshop.labels.imexpay") }),
|
||||
children: <ShopInfoIntellipay form={form} />
|
||||
}
|
||||
],
|
||||
|
||||
@@ -676,7 +676,7 @@ export function ShopInfoGeneral({ form, bodyshop }) {
|
||||
}
|
||||
]}
|
||||
>
|
||||
<Input.TextArea rows={3} />
|
||||
<Input.TextArea autoSize />
|
||||
</Form.Item>
|
||||
<Space wrap>
|
||||
<DeleteFilled
|
||||
@@ -737,7 +737,7 @@ export function ShopInfoGeneral({ form, bodyshop }) {
|
||||
}
|
||||
]}
|
||||
>
|
||||
<Input.TextArea rows={3} />
|
||||
<Input.TextArea autoSize />
|
||||
</Form.Item>
|
||||
<Space wrap>
|
||||
<DeleteFilled
|
||||
@@ -1187,7 +1187,7 @@ export function ShopInfoGeneral({ form, bodyshop }) {
|
||||
key={`${index}line_desc`}
|
||||
name={[field.name, "line_desc"]}
|
||||
>
|
||||
<Input />
|
||||
<Input.TextArea autoSize />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("joblines.fields.mod_lbr_ty")}
|
||||
@@ -1330,7 +1330,7 @@ export function ShopInfoGeneral({ form, bodyshop }) {
|
||||
}
|
||||
]}
|
||||
>
|
||||
<Input />
|
||||
<Input.TextArea autoSize />
|
||||
</Form.Item>
|
||||
|
||||
<Space wrap>
|
||||
|
||||
@@ -39,14 +39,13 @@ export function ShopInfoIntellipay({ bodyshop, form }) {
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("bodyshop.fields.intellipay_config.cash_discount_percentage")}
|
||||
valuePropName="checked"
|
||||
dependencies={[["intellipay_config", "enable_cash_discount"]]}
|
||||
name={["intellipay_config", "cash_discount_percentage"]}
|
||||
rules={[
|
||||
({ 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>
|
||||
</LayoutFormRow>
|
||||
</>
|
||||
|
||||
@@ -81,14 +81,14 @@ export const logImEXEvent = (eventName, additionalParams, stateProp = null) => {
|
||||
user: (state.user && state.user.currentUser && state.user.currentUser.email) || null,
|
||||
...additionalParams
|
||||
};
|
||||
axios.post("/ioevent", {
|
||||
useremail: (state.user && state.user.currentUser && state.user.currentUser.email) || null,
|
||||
bodyshopid: (state.user && state.user.bodyshop && state.user.bodyshop.id) || null,
|
||||
operationName: eventName,
|
||||
variables: additionalParams,
|
||||
dbevent: false,
|
||||
env: `master-AIO|${import.meta.env.VITE_APP_GIT_SHA_DATE}`
|
||||
});
|
||||
// axios.post("/ioevent", {
|
||||
// useremail: (state.user && state.user.currentUser && state.user.currentUser.email) || null,
|
||||
// bodyshopid: (state.user && state.user.bodyshop && state.user.bodyshop.id) || null,
|
||||
// operationName: eventName,
|
||||
// variables: additionalParams,
|
||||
// dbevent: false,
|
||||
// env: `master-AIO|${import.meta.env.VITE_APP_GIT_SHA_DATE}`
|
||||
// });
|
||||
// console.log(
|
||||
// "%c[Analytics]",
|
||||
// "background-color: green ;font-weight:bold;",
|
||||
|
||||
@@ -692,6 +692,7 @@ export const GET_JOB_BY_PK = gql`
|
||||
tax_str_rt
|
||||
tax_sub_rt
|
||||
tax_tow_rt
|
||||
tlos_ind
|
||||
towin
|
||||
towing_payable
|
||||
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
@@ -219,7 +219,11 @@ async function InsertPayment(oauthClient, qbo_realmId, req, payment, parentRef,
|
||||
PaymentMethodRef: {
|
||||
value: paymentMethods[payment.type]
|
||||
},
|
||||
PrivateNote: payment.memo.length > 4000 ? payment.memo.substring(0, 4000).trim() : payment.memo.trim(),
|
||||
PrivateNote: payment.memo
|
||||
? payment.memo.length > 4000
|
||||
? payment.memo.substring(0, 4000).trim()
|
||||
: payment.memo.trim()
|
||||
: "",
|
||||
PaymentRefNum: payment.transactionid,
|
||||
...(invoices && invoices.length === 1 && invoices[0]
|
||||
? {
|
||||
|
||||
@@ -47,7 +47,11 @@ exports.default = async (req, res) => {
|
||||
}
|
||||
|
||||
// Send immediate response and continue processing.
|
||||
res.status(200).send();
|
||||
res.status(202).json({
|
||||
success: true,
|
||||
message: "Processing request ...",
|
||||
timestamp: new Date().toISOString()
|
||||
});
|
||||
|
||||
try {
|
||||
logger.log("autohouse-start", "DEBUG", "api", null, null);
|
||||
@@ -146,7 +150,7 @@ async function processBatch(batch, start, end) {
|
||||
allErrors.push({
|
||||
bodyshopid: bodyshop.id,
|
||||
imexshopid: bodyshop.imexshopid,
|
||||
autuhouseid: bodyshop.autuhouseid,
|
||||
autohouseid: bodyshop.autohouseid,
|
||||
fatal: true,
|
||||
errors: [error.toString()]
|
||||
});
|
||||
@@ -154,7 +158,7 @@ async function processBatch(batch, start, end) {
|
||||
allErrors.push({
|
||||
bodyshopid: bodyshop.id,
|
||||
imexshopid: bodyshop.imexshopid,
|
||||
autuhouseid: bodyshop.autuhouseid,
|
||||
autohouseid: bodyshop.autohouseid,
|
||||
errors: erroredJobs.map((ej) => ({
|
||||
ro_number: ej.job?.ro_number,
|
||||
jobid: ej.job?.id,
|
||||
@@ -176,9 +180,8 @@ async function uploadViaSFTP(allxmlsToUpload) {
|
||||
|
||||
for (const xmlObj of allxmlsToUpload) {
|
||||
try {
|
||||
logger.log("autohouse-sftp-upload", "DEBUG", "api", null, { filename: 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,
|
||||
result: xmlObj.result
|
||||
});
|
||||
@@ -609,10 +612,7 @@ const CreateRepairOrderTag = (job, errorCallback) => {
|
||||
};
|
||||
return ret;
|
||||
} catch (error) {
|
||||
logger.log("autohouse-job-calculate-error", "ERROR", "api", null, {
|
||||
error
|
||||
});
|
||||
|
||||
logger.log("autohouse-job-calculate-error", "ERROR", "api", null, { error: error.message, stack: error.stack });
|
||||
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.
|
||||
res.status(200).send();
|
||||
res.status(202).json({
|
||||
success: true,
|
||||
message: "Processing request ...",
|
||||
timestamp: new Date().toISOString()
|
||||
});
|
||||
|
||||
try {
|
||||
logger.log("chatter-start", "DEBUG", "api", null, null);
|
||||
@@ -176,9 +180,8 @@ async function uploadViaSFTP(allcsvsToUpload) {
|
||||
|
||||
for (const csvObj of allcsvsToUpload) {
|
||||
try {
|
||||
logger.log("chatter-sftp-upload", "DEBUG", "api", null, { filename: 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,
|
||||
result: csvObj.result
|
||||
});
|
||||
|
||||
@@ -26,174 +26,184 @@ const ftpSetup = {
|
||||
password: process.env.CLAIMSCORP_PASSWORD,
|
||||
debug: (message, ...data) => logger.log(message, "DEBUG", "api", null, data),
|
||||
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) => {
|
||||
// Only process if in production environment.
|
||||
if (process.env.NODE_ENV !== "production") {
|
||||
res.sendStatus(403);
|
||||
return;
|
||||
}
|
||||
|
||||
//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
|
||||
// Only process if the appropriate token is provided.
|
||||
if (req.headers["x-imex-auth"] !== process.env.AUTOHOUSE_AUTH_TOKEN) {
|
||||
res.sendStatus(401);
|
||||
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 {
|
||||
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, {
|
||||
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 = {
|
||||
DataFeed: {
|
||||
ShopInfo: {
|
||||
ShopID: bodyshops_by_pk.claimscorpid,
|
||||
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
|
||||
)}
|
||||
`
|
||||
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") })
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
let sftp = new Client();
|
||||
sftp.on("error", (errors) =>
|
||||
logger.log("claimscorp-sftp-error", "ERROR", "api", null, {
|
||||
...errors
|
||||
})
|
||||
);
|
||||
try {
|
||||
//Connect to the FTP and upload all.
|
||||
const claimsCorpObject = {
|
||||
DataFeed: {
|
||||
ShopInfo: {
|
||||
ShopID: bodyshops_by_pk.claimscorpid,
|
||||
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() });
|
||||
})
|
||||
)
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
await sftp.connect(ftpSetup);
|
||||
|
||||
for (const xmlObj of allxmlsToUpload) {
|
||||
logger.log("claimscorp-sftp-upload", "DEBUG", "api", null, {
|
||||
filename: xmlObj.filename
|
||||
});
|
||||
|
||||
const uploadResult = await sftp.put(Buffer.from(xmlObj.xml), `/${xmlObj.filename}`);
|
||||
logger.log("claimscorp-sftp-upload-result", "DEBUG", "api", null, {
|
||||
uploadResult
|
||||
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))
|
||||
});
|
||||
}
|
||||
|
||||
//***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) {
|
||||
logger.log("claimscorp-sftp-error", "ERROR", "api", null, {
|
||||
...error
|
||||
//Error at the shop level.
|
||||
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 {
|
||||
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) => {
|
||||
//Level 2
|
||||
@@ -445,10 +455,7 @@ const CreateRepairOrderTag = (job, errorCallback) => {
|
||||
};
|
||||
return ret;
|
||||
} catch (error) {
|
||||
logger.log("claimscorp-job-calculate-error", "ERROR", "api", null, {
|
||||
error
|
||||
});
|
||||
|
||||
logger.log("claimscorp-job-calculate-error", "ERROR", "api", null, { error: error.message, stack: error.stack });
|
||||
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 DateFormat = "MM/DD/YYYY";
|
||||
|
||||
const repairOpCodes = ["OP4", "OP9", "OP10"];
|
||||
const replaceOpCodes = ["OP2", "OP5", "OP11", "OP12"];
|
||||
const kaizenShopsIDs = ["SUMMIT", "STRATHMORE", "SUNRIDGE", "SHAW"];
|
||||
|
||||
const ftpSetup = {
|
||||
host: process.env.KAIZEN_HOST,
|
||||
@@ -30,173 +29,179 @@ const ftpSetup = {
|
||||
}
|
||||
};
|
||||
|
||||
const allxmlsToUpload = [];
|
||||
const allErrors = [];
|
||||
|
||||
exports.default = async (req, res) => {
|
||||
// Only process if in production environment.
|
||||
if (process.env.NODE_ENV !== "production") {
|
||||
res.sendStatus(403);
|
||||
return;
|
||||
}
|
||||
|
||||
//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
|
||||
// Only process if the appropriate token is provided.
|
||||
if (req.headers["x-imex-auth"] !== process.env.AUTOHOUSE_AUTH_TOKEN) {
|
||||
res.sendStatus(401);
|
||||
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 {
|
||||
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, {
|
||||
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 = {
|
||||
DataFeed: {
|
||||
ShopInfo: {
|
||||
ShopName: bodyshops_by_pk.shopname,
|
||||
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
|
||||
)}
|
||||
`
|
||||
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") })
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
let sftp = new Client();
|
||||
sftp.on("error", (errors) =>
|
||||
logger.log("kaizen-sftp-error", "ERROR", "api", null, {
|
||||
...errors
|
||||
})
|
||||
);
|
||||
try {
|
||||
//Connect to the FTP and upload all.
|
||||
const kaizenObject = {
|
||||
DataFeed: {
|
||||
ShopInfo: {
|
||||
ShopName: bodyshops_by_pk.shopname,
|
||||
Jobs: jobs.map((j) =>
|
||||
CreateRepairOrderTag({ ...j, bodyshop: bodyshops_by_pk }, function ({ job, error }) {
|
||||
erroredJobs.push({ job: job, error: error.toString() });
|
||||
})
|
||||
)
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
await sftp.connect(ftpSetup);
|
||||
|
||||
for (const xmlObj of allxmlsToUpload) {
|
||||
logger.log("kaizen-sftp-upload", "DEBUG", "api", null, {
|
||||
filename: xmlObj.filename
|
||||
});
|
||||
|
||||
const uploadResult = await sftp.put(Buffer.from(xmlObj.xml), `/${xmlObj.filename}`);
|
||||
logger.log("kaizen-sftp-upload-result", "DEBUG", "api", null, {
|
||||
uploadResult
|
||||
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))
|
||||
});
|
||||
}
|
||||
|
||||
//***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) {
|
||||
logger.log("kaizen-sftp-error", "ERROR", "api", null, {
|
||||
...error
|
||||
//Error at the shop level.
|
||||
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 {
|
||||
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) => {
|
||||
//Level 2
|
||||
@@ -420,10 +425,7 @@ const CreateRepairOrderTag = (job, errorCallback) => {
|
||||
};
|
||||
return ret;
|
||||
} catch (error) {
|
||||
logger.log("kaizen-job-calculate-error", "ERROR", "api", null, {
|
||||
error
|
||||
});
|
||||
|
||||
logger.log("kaizen-job-calculate-error", "ERROR", "api", null, { error: error.message, stack: error.stack });
|
||||
errorCallback({ jobid: job.id, ro_number: job.ro_number, error });
|
||||
}
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user