Merge remote-tracking branch 'origin/master-AIO' into feature/IO-3000-Migrate-MSG-to-Sockets

This commit is contained in:
Dave Richer
2024-11-18 10:10:40 -08:00
18 changed files with 11623 additions and 11149 deletions

17
.vscode/settings.json vendored
View File

@@ -13,13 +13,30 @@
"antd", "antd",
"appointmentconfirmation", "appointmentconfirmation",
"appt", "appt",
"autohouse",
"autohouseid",
"billlines",
"bodyshop", "bodyshop",
"bodyshopid",
"bodyshops",
"CIECA",
"claimscorp",
"claimscorpid",
"Dinero",
"driveable",
"IMEX", "IMEX",
"imexshopid",
"jobid",
"joblines",
"Kaizen",
"labhrs", "labhrs",
"larhrs", "larhrs",
"mixdata",
"ownr", "ownr",
"promanager", "promanager",
"shopname",
"smartscheduling", "smartscheduling",
"timetickets",
"touchtime" "touchtime"
] ]
} }

View 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>
@@ -36253,6 +36316,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 +48444,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 +48507,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 +48570,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 +48612,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 +48675,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 +48738,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 +48934,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 +48997,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 +49060,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 +49102,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 +49165,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 +49228,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 +52097,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 +54582,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 +54865,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>

View File

@@ -105,7 +105,8 @@ const DateTimePicker = ({
<TimePicker <TimePicker
format="hh:mm a" format="hh:mm a"
minuteStep={15} minuteStep={15}
defaultOpenValue={dayjs() defaultOpenValue={dayjs(value)
.hour(dayjs().hour())
.minute(Math.floor(dayjs().minute() / 15) * 15) .minute(Math.floor(dayjs().minute() / 15) * 15)
.second(0)} .second(0)}
onChange={(value) => { onChange={(value) => {

View File

@@ -141,14 +141,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 +160,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,

View File

@@ -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}>

View File

@@ -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} />
} }
], ],

View File

@@ -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>

View File

@@ -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>
</> </>

View File

@@ -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;",

View File

@@ -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

View File

@@ -219,7 +219,11 @@ async function InsertPayment(oauthClient, qbo_realmId, req, payment, parentRef,
PaymentMethodRef: { PaymentMethodRef: {
value: paymentMethods[payment.type] 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, PaymentRefNum: payment.transactionid,
...(invoices && invoices.length === 1 && invoices[0] ...(invoices && invoices.length === 1 && invoices[0]
? { ? {

View File

@@ -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 });
} }
}; };

View File

@@ -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
}); });

View File

@@ -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 });
} }
}; };

View File

@@ -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 });
} }
}; };