Compare commits
1 Commits
feature/IO
...
feature/IO
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4e8ea736c5 |
@@ -145,124 +145,168 @@ export function ShopInfoGeneral({ form, bodyshop }) {
|
||||
</Form.Item>
|
||||
</LayoutFormRow>
|
||||
<LayoutFormRow header={t("bodyshop.labels.accountingsetup")} id="accountingsetup">
|
||||
{HasFeatureAccess({ featureName: "export", bodyshop }) && (
|
||||
<>
|
||||
<Form.Item label={t("bodyshop.labels.qbo")} valuePropName="checked" name={["accountingconfig", "qbo"]}>
|
||||
<Switch />
|
||||
</Form.Item>
|
||||
{InstanceRenderManager({
|
||||
imex: (
|
||||
<Form.Item shouldUpdate noStyle>
|
||||
{() => (
|
||||
<Form.Item
|
||||
label={t("bodyshop.labels.qbo_usa")}
|
||||
shouldUpdate
|
||||
valuePropName="checked"
|
||||
name={["accountingconfig", "qbo_usa"]}
|
||||
>
|
||||
<Switch disabled={!form.getFieldValue(["accountingconfig", "qbo"])} />
|
||||
</Form.Item>
|
||||
)}
|
||||
</Form.Item>
|
||||
)
|
||||
})}
|
||||
<Form.Item label={t("bodyshop.labels.qbo_departmentid")} name={["accountingconfig", "qbo_departmentid"]}>
|
||||
<Input />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("bodyshop.labels.accountingtiers")}
|
||||
rules={[
|
||||
{
|
||||
required: true
|
||||
//message: t("general.validation.required"),
|
||||
}
|
||||
]}
|
||||
name={["accountingconfig", "tiers"]}
|
||||
>
|
||||
<Radio.Group>
|
||||
<Radio value={2}>2</Radio>
|
||||
<Radio value={3}>3</Radio>
|
||||
</Radio.Group>
|
||||
</Form.Item>
|
||||
<Form.Item shouldUpdate>
|
||||
{() => {
|
||||
return (
|
||||
<Form.Item
|
||||
label={t("bodyshop.labels.2tiersetup")}
|
||||
shouldUpdate
|
||||
rules={[
|
||||
{
|
||||
required: form.getFieldValue(["accountingconfig", "tiers"]) === 2
|
||||
//message: t("general.validation.required"),
|
||||
}
|
||||
]}
|
||||
name={["accountingconfig", "twotierpref"]}
|
||||
>
|
||||
<Radio.Group disabled={form.getFieldValue(["accountingconfig", "tiers"]) === 3}>
|
||||
<Radio value="name">{t("bodyshop.labels.2tiername")}</Radio>
|
||||
<Radio value="source">{t("bodyshop.labels.2tiersource")}</Radio>
|
||||
</Radio.Group>
|
||||
</Form.Item>
|
||||
);
|
||||
}}
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("bodyshop.labels.printlater")}
|
||||
valuePropName="checked"
|
||||
name={["accountingconfig", "printlater"]}
|
||||
>
|
||||
<Switch />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("bodyshop.labels.emaillater")}
|
||||
valuePropName="checked"
|
||||
name={["accountingconfig", "emaillater"]}
|
||||
>
|
||||
<Switch />
|
||||
</Form.Item>
|
||||
</>
|
||||
)}
|
||||
<Form.Item
|
||||
label={t("bodyshop.fields.inhousevendorid")}
|
||||
name={"inhousevendorid"}
|
||||
rules={[
|
||||
{
|
||||
required: true
|
||||
//message: t("general.validation.required"),
|
||||
}
|
||||
]}
|
||||
>
|
||||
<Input />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("bodyshop.fields.default_adjustment_rate")}
|
||||
name={"default_adjustment_rate"}
|
||||
rules={[
|
||||
{
|
||||
required: true
|
||||
//message: t("general.validation.required"),
|
||||
}
|
||||
]}
|
||||
>
|
||||
<InputNumber min={0} precision={2} />
|
||||
</Form.Item>
|
||||
{InstanceRenderManager({
|
||||
imex: (
|
||||
<Form.Item label={t("bodyshop.fields.federal_tax_id")} name="federal_tax_id">
|
||||
<Input />
|
||||
</Form.Item>
|
||||
)
|
||||
})}
|
||||
<Form.Item label={t("bodyshop.fields.state_tax_id")} name="state_tax_id">
|
||||
<Input />
|
||||
</Form.Item>
|
||||
{HasFeatureAccess({ featureName: "bills", bodyshop }) && (
|
||||
<>
|
||||
{InstanceRenderManager({
|
||||
imex: (
|
||||
{[
|
||||
...(HasFeatureAccess({ featureName: "export", bodyshop })
|
||||
? [
|
||||
<Form.Item
|
||||
label={t("bodyshop.fields.invoice_federal_tax_rate")}
|
||||
name={["bill_tax_rates", "federal_tax_rate"]}
|
||||
key="qbo"
|
||||
label={t("bodyshop.labels.qbo")}
|
||||
valuePropName="checked"
|
||||
name={["accountingconfig", "qbo"]}
|
||||
>
|
||||
<Switch />
|
||||
</Form.Item>,
|
||||
InstanceRenderManager({
|
||||
imex: (
|
||||
<Form.Item key="qbo_usa_wrapper" shouldUpdate noStyle>
|
||||
{() => (
|
||||
<Form.Item
|
||||
label={t("bodyshop.labels.qbo_usa")}
|
||||
shouldUpdate
|
||||
valuePropName="checked"
|
||||
name={["accountingconfig", "qbo_usa"]}
|
||||
>
|
||||
<Switch disabled={!form.getFieldValue(["accountingconfig", "qbo"])} />
|
||||
</Form.Item>
|
||||
)}
|
||||
</Form.Item>
|
||||
)
|
||||
}),
|
||||
<Form.Item
|
||||
key="qbo_departmentid"
|
||||
label={t("bodyshop.labels.qbo_departmentid")}
|
||||
name={["accountingconfig", "qbo_departmentid"]}
|
||||
>
|
||||
<Input />
|
||||
</Form.Item>,
|
||||
<Form.Item
|
||||
key="accountingtiers"
|
||||
label={t("bodyshop.labels.accountingtiers")}
|
||||
rules={[
|
||||
{
|
||||
required: true
|
||||
//message: t("general.validation.required"),
|
||||
}
|
||||
]}
|
||||
name={["accountingconfig", "tiers"]}
|
||||
>
|
||||
<Radio.Group>
|
||||
<Radio value={2}>2</Radio>
|
||||
<Radio value={3}>3</Radio>
|
||||
</Radio.Group>
|
||||
</Form.Item>,
|
||||
<Form.Item key="twotierpref_wrapper" shouldUpdate>
|
||||
{() => {
|
||||
return (
|
||||
<Form.Item
|
||||
label={t("bodyshop.labels.2tiersetup")}
|
||||
shouldUpdate
|
||||
rules={[
|
||||
{
|
||||
required: form.getFieldValue(["accountingconfig", "tiers"]) === 2
|
||||
//message: t("general.validation.required"),
|
||||
}
|
||||
]}
|
||||
name={["accountingconfig", "twotierpref"]}
|
||||
>
|
||||
<Radio.Group disabled={form.getFieldValue(["accountingconfig", "tiers"]) === 3}>
|
||||
<Radio value="name">{t("bodyshop.labels.2tiername")}</Radio>
|
||||
<Radio value="source">{t("bodyshop.labels.2tiersource")}</Radio>
|
||||
</Radio.Group>
|
||||
</Form.Item>
|
||||
);
|
||||
}}
|
||||
</Form.Item>,
|
||||
<Form.Item
|
||||
key="printlater"
|
||||
label={t("bodyshop.labels.printlater")}
|
||||
valuePropName="checked"
|
||||
name={["accountingconfig", "printlater"]}
|
||||
>
|
||||
<Switch />
|
||||
</Form.Item>,
|
||||
<Form.Item
|
||||
key="emaillater"
|
||||
label={t("bodyshop.labels.emaillater")}
|
||||
valuePropName="checked"
|
||||
name={["accountingconfig", "emaillater"]}
|
||||
>
|
||||
<Switch />
|
||||
</Form.Item>
|
||||
]
|
||||
: []),
|
||||
<Form.Item
|
||||
key="inhousevendorid"
|
||||
label={t("bodyshop.fields.inhousevendorid")}
|
||||
name={"inhousevendorid"}
|
||||
rules={[
|
||||
{
|
||||
required: true
|
||||
//message: t("general.validation.required"),
|
||||
}
|
||||
]}
|
||||
>
|
||||
<Input />
|
||||
</Form.Item>,
|
||||
<Form.Item
|
||||
key="default_adjustment_rate"
|
||||
label={t("bodyshop.fields.default_adjustment_rate")}
|
||||
name={"default_adjustment_rate"}
|
||||
rules={[
|
||||
{
|
||||
required: true
|
||||
//message: t("general.validation.required"),
|
||||
}
|
||||
]}
|
||||
>
|
||||
<InputNumber min={0} precision={2} />
|
||||
</Form.Item>,
|
||||
InstanceRenderManager({
|
||||
imex: (
|
||||
<Form.Item key="federal_tax_id" label={t("bodyshop.fields.federal_tax_id")} name="federal_tax_id">
|
||||
<Input />
|
||||
</Form.Item>
|
||||
)
|
||||
}),
|
||||
<Form.Item key="state_tax_id" label={t("bodyshop.fields.state_tax_id")} name="state_tax_id">
|
||||
<Input />
|
||||
</Form.Item>,
|
||||
...(HasFeatureAccess({ featureName: "bills", bodyshop })
|
||||
? [
|
||||
InstanceRenderManager({
|
||||
imex: (
|
||||
<Form.Item
|
||||
key="invoice_federal_tax_rate"
|
||||
label={t("bodyshop.fields.invoice_federal_tax_rate")}
|
||||
name={["bill_tax_rates", "federal_tax_rate"]}
|
||||
rules={[
|
||||
{
|
||||
required: true
|
||||
//message: t("general.validation.required"),
|
||||
}
|
||||
]}
|
||||
>
|
||||
<InputNumber />
|
||||
</Form.Item>
|
||||
)
|
||||
}),
|
||||
<Form.Item
|
||||
key="invoice_state_tax_rate"
|
||||
label={t("bodyshop.fields.invoice_state_tax_rate")}
|
||||
name={["bill_tax_rates", "state_tax_rate"]}
|
||||
rules={[
|
||||
{
|
||||
required: true
|
||||
//message: t("general.validation.required"),
|
||||
}
|
||||
]}
|
||||
>
|
||||
<InputNumber />
|
||||
</Form.Item>,
|
||||
<Form.Item
|
||||
key="invoice_local_tax_rate"
|
||||
label={t("bodyshop.fields.invoice_local_tax_rate")}
|
||||
name={["bill_tax_rates", "local_tax_rate"]}
|
||||
rules={[
|
||||
{
|
||||
required: true
|
||||
@@ -272,117 +316,118 @@ export function ShopInfoGeneral({ form, bodyshop }) {
|
||||
>
|
||||
<InputNumber />
|
||||
</Form.Item>
|
||||
)
|
||||
})}
|
||||
<Form.Item
|
||||
label={t("bodyshop.fields.invoice_state_tax_rate")}
|
||||
name={["bill_tax_rates", "state_tax_rate"]}
|
||||
rules={[
|
||||
{
|
||||
required: true
|
||||
//message: t("general.validation.required"),
|
||||
}
|
||||
]}
|
||||
>
|
||||
<InputNumber />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("bodyshop.fields.invoice_local_tax_rate")}
|
||||
name={["bill_tax_rates", "local_tax_rate"]}
|
||||
rules={[
|
||||
{
|
||||
required: true
|
||||
//message: t("general.validation.required"),
|
||||
}
|
||||
]}
|
||||
>
|
||||
<InputNumber />
|
||||
</Form.Item>
|
||||
</>
|
||||
)}
|
||||
<Form.Item
|
||||
name={["md_payment_types"]}
|
||||
label={t("bodyshop.fields.md_payment_types")}
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
//message: t("general.validation.required"),
|
||||
type: "array"
|
||||
}
|
||||
]}
|
||||
>
|
||||
<Select mode="tags" />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
name={["md_categories"]}
|
||||
label={t("bodyshop.fields.md_categories")}
|
||||
rules={[
|
||||
{
|
||||
//message: t("general.validation.required"),
|
||||
type: "array"
|
||||
}
|
||||
]}
|
||||
>
|
||||
<Select mode="tags" />
|
||||
</Form.Item>
|
||||
{HasFeatureAccess({ featureName: "export", bodyshop }) && (
|
||||
<>
|
||||
<Form.Item
|
||||
name={["accountingconfig", "ReceivableCustomField1"]}
|
||||
label={t("bodyshop.fields.ReceivableCustomField", { number: 1 })}
|
||||
>
|
||||
{ReceivableCustomFieldSelect}
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
name={["accountingconfig", "ReceivableCustomField2"]}
|
||||
label={t("bodyshop.fields.ReceivableCustomField", { number: 2 })}
|
||||
>
|
||||
{ReceivableCustomFieldSelect}
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
name={["accountingconfig", "ReceivableCustomField3"]}
|
||||
label={t("bodyshop.fields.ReceivableCustomField", { number: 3 })}
|
||||
>
|
||||
{ReceivableCustomFieldSelect}
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
name={["md_classes"]}
|
||||
label={t("bodyshop.fields.md_classes")}
|
||||
rules={[
|
||||
({ getFieldValue }) => {
|
||||
return {
|
||||
required: getFieldValue("enforce_class"),
|
||||
//message: t("general.validation.required"),
|
||||
type: "array"
|
||||
};
|
||||
}
|
||||
]}
|
||||
>
|
||||
<Select mode="tags" />
|
||||
</Form.Item>
|
||||
<Form.Item name={["enforce_class"]} label={t("bodyshop.fields.enforce_class")} valuePropName="checked">
|
||||
<Switch />
|
||||
</Form.Item>
|
||||
{ClosingPeriod.treatment === "on" && (
|
||||
<Form.Item
|
||||
name={["accountingconfig", "ClosingPeriod"]}
|
||||
label={t("bodyshop.fields.closingperiod")} //{t("reportcenter.labels.dates")}
|
||||
>
|
||||
<DatePicker.RangePicker format="MM/DD/YYYY" presets={DatePickerRanges} />
|
||||
</Form.Item>
|
||||
)}
|
||||
{ADPPayroll.treatment === "on" && (
|
||||
<Form.Item name={["accountingconfig", "companyCode"]} label={t("bodyshop.fields.companycode")}>
|
||||
<Input />
|
||||
</Form.Item>
|
||||
)}
|
||||
{ADPPayroll.treatment === "on" && (
|
||||
<Form.Item name={["accountingconfig", "batchID"]} label={t("bodyshop.fields.batchid")}>
|
||||
<Input />
|
||||
</Form.Item>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
]
|
||||
: []),
|
||||
<Form.Item
|
||||
key="md_payment_types"
|
||||
name={["md_payment_types"]}
|
||||
label={t("bodyshop.fields.md_payment_types")}
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
//message: t("general.validation.required"),
|
||||
type: "array"
|
||||
}
|
||||
]}
|
||||
>
|
||||
<Select mode="tags" />
|
||||
</Form.Item>,
|
||||
<Form.Item
|
||||
key="md_categories"
|
||||
name={["md_categories"]}
|
||||
label={t("bodyshop.fields.md_categories")}
|
||||
rules={[
|
||||
{
|
||||
//message: t("general.validation.required"),
|
||||
type: "array"
|
||||
}
|
||||
]}
|
||||
>
|
||||
<Select mode="tags" />
|
||||
</Form.Item>,
|
||||
...(HasFeatureAccess({ featureName: "export", bodyshop })
|
||||
? [
|
||||
<Form.Item
|
||||
key="ReceivableCustomField1"
|
||||
name={["accountingconfig", "ReceivableCustomField1"]}
|
||||
label={t("bodyshop.fields.ReceivableCustomField", { number: 1 })}
|
||||
>
|
||||
{ReceivableCustomFieldSelect}
|
||||
</Form.Item>,
|
||||
<Form.Item
|
||||
key="ReceivableCustomField2"
|
||||
name={["accountingconfig", "ReceivableCustomField2"]}
|
||||
label={t("bodyshop.fields.ReceivableCustomField", { number: 2 })}
|
||||
>
|
||||
{ReceivableCustomFieldSelect}
|
||||
</Form.Item>,
|
||||
<Form.Item
|
||||
key="ReceivableCustomField3"
|
||||
name={["accountingconfig", "ReceivableCustomField3"]}
|
||||
label={t("bodyshop.fields.ReceivableCustomField", { number: 3 })}
|
||||
>
|
||||
{ReceivableCustomFieldSelect}
|
||||
</Form.Item>,
|
||||
<Form.Item
|
||||
key="md_classes"
|
||||
name={["md_classes"]}
|
||||
label={t("bodyshop.fields.md_classes")}
|
||||
rules={[
|
||||
({ getFieldValue }) => {
|
||||
return {
|
||||
required: getFieldValue("enforce_class"),
|
||||
//message: t("general.validation.required"),
|
||||
type: "array"
|
||||
};
|
||||
}
|
||||
]}
|
||||
>
|
||||
<Select mode="tags" />
|
||||
</Form.Item>,
|
||||
<Form.Item
|
||||
key="enforce_class"
|
||||
name={["enforce_class"]}
|
||||
label={t("bodyshop.fields.enforce_class")}
|
||||
valuePropName="checked"
|
||||
>
|
||||
<Switch />
|
||||
</Form.Item>,
|
||||
...(ClosingPeriod.treatment === "on"
|
||||
? [
|
||||
<Form.Item
|
||||
key="ClosingPeriod"
|
||||
name={["accountingconfig", "ClosingPeriod"]}
|
||||
label={t("bodyshop.fields.closingperiod")} //{t("reportcenter.labels.dates")}
|
||||
>
|
||||
<DatePicker.RangePicker format="MM/DD/YYYY" presets={DatePickerRanges} />
|
||||
</Form.Item>
|
||||
]
|
||||
: []),
|
||||
...(ADPPayroll.treatment === "on"
|
||||
? [
|
||||
<Form.Item
|
||||
key="companyCode"
|
||||
name={["accountingconfig", "companyCode"]}
|
||||
label={t("bodyshop.fields.companycode")}
|
||||
>
|
||||
<Input />
|
||||
</Form.Item>
|
||||
]
|
||||
: []),
|
||||
...(ADPPayroll.treatment === "on"
|
||||
? [
|
||||
<Form.Item
|
||||
key="batchID"
|
||||
name={["accountingconfig", "batchID"]}
|
||||
label={t("bodyshop.fields.batchid")}
|
||||
>
|
||||
<Input />
|
||||
</Form.Item>
|
||||
]
|
||||
: [])
|
||||
]
|
||||
: [])
|
||||
]}
|
||||
</LayoutFormRow>
|
||||
<FeatureWrapper featureName="scoreboard" noauth={() => null}>
|
||||
<LayoutFormRow header={t("bodyshop.labels.scoreboardsetup")} id="scoreboardsetup">
|
||||
@@ -446,211 +491,255 @@ export function ShopInfoGeneral({ form, bodyshop }) {
|
||||
</LayoutFormRow>
|
||||
</FeatureWrapper>
|
||||
<LayoutFormRow header={t("bodyshop.labels.systemsettings")} id="systemsettings">
|
||||
<Form.Item
|
||||
name={["md_referral_sources"]}
|
||||
label={t("bodyshop.fields.md_referral_sources")}
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
//message: t("general.validation.required"),
|
||||
type: "array"
|
||||
}
|
||||
]}
|
||||
>
|
||||
<Select mode="tags" />
|
||||
</Form.Item>
|
||||
<Form.Item name={["enforce_referral"]} label={t("bodyshop.fields.enforce_referral")} valuePropName="checked">
|
||||
<Switch />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
name={["enforce_conversion_csr"]}
|
||||
label={t("bodyshop.fields.enforce_conversion_csr")}
|
||||
valuePropName="checked"
|
||||
>
|
||||
<Switch />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
name={["enforce_conversion_category"]}
|
||||
label={t("bodyshop.fields.enforce_conversion_category")}
|
||||
valuePropName="checked"
|
||||
>
|
||||
<Switch />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
name={["target_touchtime"]}
|
||||
label={t("bodyshop.fields.target_touchtime")}
|
||||
rules={[
|
||||
{
|
||||
required: true
|
||||
//message: t("general.validation.required"),
|
||||
}
|
||||
]}
|
||||
>
|
||||
<InputNumber min={0.1} precision={1} />
|
||||
</Form.Item>
|
||||
<Form.Item label={t("bodyshop.fields.use_fippa")} name={["use_fippa"]} valuePropName="checked">
|
||||
<Switch />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("bodyshop.fields.md_hour_split.prep")}
|
||||
name={["md_hour_split", "prep"]}
|
||||
dependencies={[["md_hour_split", "paint"]]}
|
||||
rules={[
|
||||
({ getFieldValue }) => ({
|
||||
validator(rule, value) {
|
||||
if (!value && !getFieldValue(["md_hour_split", "paint"])) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
if (value + getFieldValue(["md_hour_split", "paint"]) === 1) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
return Promise.reject(t("bodyshop.validation.larsplit"));
|
||||
{[
|
||||
<Form.Item
|
||||
key="md_referral_sources"
|
||||
name={["md_referral_sources"]}
|
||||
label={t("bodyshop.fields.md_referral_sources")}
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
//message: t("general.validation.required"),
|
||||
type: "array"
|
||||
}
|
||||
})
|
||||
]}
|
||||
>
|
||||
<InputNumber min={0} max={1} precision={2} />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("bodyshop.fields.md_hour_split.paint")}
|
||||
name={["md_hour_split", "paint"]}
|
||||
dependencies={[["md_hour_split", "prep"]]}
|
||||
rules={[
|
||||
({ getFieldValue }) => ({
|
||||
validator(rule, value) {
|
||||
if (!value && !getFieldValue(["md_hour_split", "paint"])) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
if (value + getFieldValue(["md_hour_split", "prep"]) === 1) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
return Promise.reject(t("bodyshop.validation.larsplit"));
|
||||
]}
|
||||
>
|
||||
<Select mode="tags" />
|
||||
</Form.Item>,
|
||||
<Form.Item
|
||||
key="enforce_referral"
|
||||
name={["enforce_referral"]}
|
||||
label={t("bodyshop.fields.enforce_referral")}
|
||||
valuePropName="checked"
|
||||
>
|
||||
<Switch />
|
||||
</Form.Item>,
|
||||
<Form.Item
|
||||
key="enforce_conversion_csr"
|
||||
name={["enforce_conversion_csr"]}
|
||||
label={t("bodyshop.fields.enforce_conversion_csr")}
|
||||
valuePropName="checked"
|
||||
>
|
||||
<Switch />
|
||||
</Form.Item>,
|
||||
<Form.Item
|
||||
key="enforce_conversion_category"
|
||||
name={["enforce_conversion_category"]}
|
||||
label={t("bodyshop.fields.enforce_conversion_category")}
|
||||
valuePropName="checked"
|
||||
>
|
||||
<Switch />
|
||||
</Form.Item>,
|
||||
<Form.Item
|
||||
key="target_touchtime"
|
||||
name={["target_touchtime"]}
|
||||
label={t("bodyshop.fields.target_touchtime")}
|
||||
rules={[
|
||||
{
|
||||
required: true
|
||||
//message: t("general.validation.required"),
|
||||
}
|
||||
})
|
||||
]}
|
||||
>
|
||||
<InputNumber min={0} max={1} precision={2} />
|
||||
</Form.Item>
|
||||
<Form.Item label={t("bodyshop.fields.jc_hourly_rates.mapa")} name={["jc_hourly_rates", "mapa"]}>
|
||||
<CurrencyInput />
|
||||
</Form.Item>
|
||||
<Form.Item label={t("bodyshop.fields.jc_hourly_rates.mash")} name={["jc_hourly_rates", "mash"]}>
|
||||
<CurrencyInput />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
name={["use_paint_scale_data"]}
|
||||
label={t("bodyshop.fields.use_paint_scale_data")}
|
||||
valuePropName="checked"
|
||||
>
|
||||
<Switch />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
name={["attach_pdf_to_email"]}
|
||||
label={t("bodyshop.fields.attach_pdf_to_email")}
|
||||
valuePropName="checked"
|
||||
>
|
||||
<Switch />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
name={["md_from_emails"]}
|
||||
label={t("bodyshop.fields.md_from_emails")}
|
||||
// rules={[
|
||||
// {
|
||||
// //message: t("general.validation.required"),
|
||||
// type: "array",
|
||||
// },
|
||||
// ]}
|
||||
>
|
||||
<Select mode="tags" />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
name={["md_email_cc", "parts_order"]}
|
||||
label={t("bodyshop.fields.md_email_cc", { template: "parts_orders" })}
|
||||
rules={[
|
||||
{
|
||||
//message: t("general.validation.required"),
|
||||
type: "array"
|
||||
}
|
||||
]}
|
||||
>
|
||||
<Select mode="tags" />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
name={["md_email_cc", "parts_return_slip"]}
|
||||
label={t("bodyshop.fields.md_email_cc", { template: "parts_returns" })}
|
||||
rules={[
|
||||
{
|
||||
//message: t("general.validation.required"),
|
||||
type: "array"
|
||||
}
|
||||
]}
|
||||
>
|
||||
<Select mode="tags" />
|
||||
</Form.Item>
|
||||
|
||||
{HasFeatureAccess({ featureName: "timetickets", bodyshop }) && (
|
||||
<>
|
||||
<Form.Item
|
||||
name={["tt_allow_post_to_invoiced"]}
|
||||
label={t("bodyshop.fields.tt_allow_post_to_invoiced")}
|
||||
valuePropName="checked"
|
||||
>
|
||||
<Switch />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
name={["tt_enforce_hours_for_tech_console"]}
|
||||
label={t("bodyshop.fields.tt_enforce_hours_for_tech_console")}
|
||||
valuePropName="checked"
|
||||
>
|
||||
<Switch />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
name={["bill_allow_post_to_closed"]}
|
||||
label={t("bodyshop.fields.bill_allow_post_to_closed")}
|
||||
valuePropName="checked"
|
||||
>
|
||||
<Switch />
|
||||
</Form.Item>
|
||||
</>
|
||||
)}
|
||||
<Form.Item
|
||||
name={["md_ded_notes"]}
|
||||
label={t("bodyshop.fields.md_ded_notes")}
|
||||
rules={[
|
||||
{
|
||||
//message: t("general.validation.required"),
|
||||
type: "array"
|
||||
}
|
||||
]}
|
||||
>
|
||||
<Select mode="tags" />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("bodyshop.fields.md_functionality_toggles.parts_queue_toggle")}
|
||||
name={["md_functionality_toggles", "parts_queue_toggle"]}
|
||||
valuePropName="checked"
|
||||
>
|
||||
<Switch />
|
||||
</Form.Item>
|
||||
<Form.Item name={["last_name_first"]} label={t("bodyshop.fields.last_name_first")} valuePropName="checked">
|
||||
<Switch />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
name={["uselocalmediaserver"]}
|
||||
label={t("bodyshop.fields.uselocalmediaserver")}
|
||||
valuePropName="checked"
|
||||
>
|
||||
<Switch />
|
||||
</Form.Item>
|
||||
<Form.Item name={["localmediaserverhttp"]} label={t("bodyshop.fields.localmediaserverhttp")}>
|
||||
<Input />
|
||||
</Form.Item>
|
||||
<Form.Item name={["localmediaservernetwork"]} label={t("bodyshop.fields.localmediaservernetwork")}>
|
||||
<Input />
|
||||
</Form.Item>
|
||||
<Form.Item name={["localmediatoken"]} label={t("bodyshop.fields.localmediatoken")}>
|
||||
<Input />
|
||||
</Form.Item>
|
||||
]}
|
||||
>
|
||||
<InputNumber min={0.1} precision={1} />
|
||||
</Form.Item>,
|
||||
<Form.Item key="use_fippa" label={t("bodyshop.fields.use_fippa")} name={["use_fippa"]} valuePropName="checked">
|
||||
<Switch />
|
||||
</Form.Item>,
|
||||
<Form.Item
|
||||
key="md_hour_split_prep"
|
||||
label={t("bodyshop.fields.md_hour_split.prep")}
|
||||
name={["md_hour_split", "prep"]}
|
||||
dependencies={[["md_hour_split", "paint"]]}
|
||||
rules={[
|
||||
({ getFieldValue }) => ({
|
||||
validator(rule, value) {
|
||||
if (!value && !getFieldValue(["md_hour_split", "paint"])) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
if (value + getFieldValue(["md_hour_split", "paint"]) === 1) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
return Promise.reject(t("bodyshop.validation.larsplit"));
|
||||
}
|
||||
})
|
||||
]}
|
||||
>
|
||||
<InputNumber min={0} max={1} precision={2} />
|
||||
</Form.Item>,
|
||||
<Form.Item
|
||||
key="md_hour_split_paint"
|
||||
label={t("bodyshop.fields.md_hour_split.paint")}
|
||||
name={["md_hour_split", "paint"]}
|
||||
dependencies={[["md_hour_split", "prep"]]}
|
||||
rules={[
|
||||
({ getFieldValue }) => ({
|
||||
validator(rule, value) {
|
||||
if (!value && !getFieldValue(["md_hour_split", "paint"])) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
if (value + getFieldValue(["md_hour_split", "prep"]) === 1) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
return Promise.reject(t("bodyshop.validation.larsplit"));
|
||||
}
|
||||
})
|
||||
]}
|
||||
>
|
||||
<InputNumber min={0} max={1} precision={2} />
|
||||
</Form.Item>,
|
||||
<Form.Item
|
||||
key="jc_hourly_rates_mapa"
|
||||
label={t("bodyshop.fields.jc_hourly_rates.mapa")}
|
||||
name={["jc_hourly_rates", "mapa"]}
|
||||
>
|
||||
<CurrencyInput />
|
||||
</Form.Item>,
|
||||
<Form.Item
|
||||
key="jc_hourly_rates_mash"
|
||||
label={t("bodyshop.fields.jc_hourly_rates.mash")}
|
||||
name={["jc_hourly_rates", "mash"]}
|
||||
>
|
||||
<CurrencyInput />
|
||||
</Form.Item>,
|
||||
<Form.Item
|
||||
key="use_paint_scale_data"
|
||||
name={["use_paint_scale_data"]}
|
||||
label={t("bodyshop.fields.use_paint_scale_data")}
|
||||
valuePropName="checked"
|
||||
>
|
||||
<Switch />
|
||||
</Form.Item>,
|
||||
<Form.Item
|
||||
key="attach_pdf_to_email"
|
||||
name={["attach_pdf_to_email"]}
|
||||
label={t("bodyshop.fields.attach_pdf_to_email")}
|
||||
valuePropName="checked"
|
||||
>
|
||||
<Switch />
|
||||
</Form.Item>,
|
||||
<Form.Item
|
||||
key="md_from_emails"
|
||||
name={["md_from_emails"]}
|
||||
label={t("bodyshop.fields.md_from_emails")}
|
||||
// rules={[
|
||||
// {
|
||||
// //message: t("general.validation.required"),
|
||||
// type: "array",
|
||||
// },
|
||||
// ]}
|
||||
>
|
||||
<Select mode="tags" />
|
||||
</Form.Item>,
|
||||
<Form.Item
|
||||
key="md_email_cc_parts_order"
|
||||
name={["md_email_cc", "parts_order"]}
|
||||
label={t("bodyshop.fields.md_email_cc", { template: "parts_orders" })}
|
||||
rules={[
|
||||
{
|
||||
//message: t("general.validation.required"),
|
||||
type: "array"
|
||||
}
|
||||
]}
|
||||
>
|
||||
<Select mode="tags" />
|
||||
</Form.Item>,
|
||||
<Form.Item
|
||||
key="md_email_cc_parts_return_slip"
|
||||
name={["md_email_cc", "parts_return_slip"]}
|
||||
label={t("bodyshop.fields.md_email_cc", { template: "parts_returns" })}
|
||||
rules={[
|
||||
{
|
||||
//message: t("general.validation.required"),
|
||||
type: "array"
|
||||
}
|
||||
]}
|
||||
>
|
||||
<Select mode="tags" />
|
||||
</Form.Item>,
|
||||
...(HasFeatureAccess({ featureName: "timetickets", bodyshop })
|
||||
? [
|
||||
<Form.Item
|
||||
key="tt_allow_post_to_invoiced"
|
||||
name={["tt_allow_post_to_invoiced"]}
|
||||
label={t("bodyshop.fields.tt_allow_post_to_invoiced")}
|
||||
valuePropName="checked"
|
||||
>
|
||||
<Switch />
|
||||
</Form.Item>,
|
||||
<Form.Item
|
||||
key="tt_enforce_hours_for_tech_console"
|
||||
name={["tt_enforce_hours_for_tech_console"]}
|
||||
label={t("bodyshop.fields.tt_enforce_hours_for_tech_console")}
|
||||
valuePropName="checked"
|
||||
>
|
||||
<Switch />
|
||||
</Form.Item>,
|
||||
<Form.Item
|
||||
key="bill_allow_post_to_closed"
|
||||
name={["bill_allow_post_to_closed"]}
|
||||
label={t("bodyshop.fields.bill_allow_post_to_closed")}
|
||||
valuePropName="checked"
|
||||
>
|
||||
<Switch />
|
||||
</Form.Item>
|
||||
]
|
||||
: []),
|
||||
<Form.Item
|
||||
key="md_ded_notes"
|
||||
name={["md_ded_notes"]}
|
||||
label={t("bodyshop.fields.md_ded_notes")}
|
||||
rules={[
|
||||
{
|
||||
//message: t("general.validation.required"),
|
||||
type: "array"
|
||||
}
|
||||
]}
|
||||
>
|
||||
<Select mode="tags" />
|
||||
</Form.Item>,
|
||||
<Form.Item
|
||||
key="parts_queue_toggle"
|
||||
label={t("bodyshop.fields.md_functionality_toggles.parts_queue_toggle")}
|
||||
name={["md_functionality_toggles", "parts_queue_toggle"]}
|
||||
valuePropName="checked"
|
||||
>
|
||||
<Switch />
|
||||
</Form.Item>,
|
||||
<Form.Item
|
||||
key="last_name_first"
|
||||
name={["last_name_first"]}
|
||||
label={t("bodyshop.fields.last_name_first")}
|
||||
valuePropName="checked"
|
||||
>
|
||||
<Switch />
|
||||
</Form.Item>,
|
||||
<Form.Item
|
||||
key="uselocalmediaserver"
|
||||
name={["uselocalmediaserver"]}
|
||||
label={t("bodyshop.fields.uselocalmediaserver")}
|
||||
valuePropName="checked"
|
||||
>
|
||||
<Switch />
|
||||
</Form.Item>,
|
||||
<Form.Item
|
||||
key="localmediaserverhttp"
|
||||
name={["localmediaserverhttp"]}
|
||||
label={t("bodyshop.fields.localmediaserverhttp")}
|
||||
>
|
||||
<Input />
|
||||
</Form.Item>,
|
||||
<Form.Item
|
||||
key="localmediaservernetwork"
|
||||
name={["localmediaservernetwork"]}
|
||||
label={t("bodyshop.fields.localmediaservernetwork")}
|
||||
>
|
||||
<Input />
|
||||
</Form.Item>,
|
||||
<Form.Item key="localmediatoken" name={["localmediatoken"]} label={t("bodyshop.fields.localmediatoken")}>
|
||||
<Input />
|
||||
</Form.Item>
|
||||
]}
|
||||
</LayoutFormRow>
|
||||
<LayoutFormRow header={t("bodyshop.labels.shop_enabled_features")} id="sharing">
|
||||
<Form.Item
|
||||
|
||||
@@ -3,6 +3,7 @@ const queries = require("../graphql-client/queries");
|
||||
const Dinero = require("dinero.js");
|
||||
const moment = require("moment-timezone");
|
||||
var builder = require("xmlbuilder2");
|
||||
const _ = require("lodash");
|
||||
const logger = require("../utils/logger");
|
||||
const fs = require("fs");
|
||||
require("dotenv").config({
|
||||
@@ -15,7 +16,6 @@ const { sendServerEmail } = require("../email/sendemail");
|
||||
|
||||
const AHDineroFormat = "0.00";
|
||||
const AhDateFormat = "MMDDYYYY";
|
||||
const NON_ASCII_REGEX = /[^\x20-\x7E]/g;
|
||||
|
||||
const repairOpCodes = ["OP4", "OP9", "OP10"];
|
||||
const replaceOpCodes = ["OP2", "OP5", "OP11", "OP12"];
|
||||
@@ -37,11 +37,13 @@ const ftpSetup = {
|
||||
exports.default = async (req, res) => {
|
||||
// Only process if in production environment.
|
||||
if (process.env.NODE_ENV !== "production") {
|
||||
return res.sendStatus(403);
|
||||
res.sendStatus(403);
|
||||
return;
|
||||
}
|
||||
// Only process if the appropriate token is provided.
|
||||
if (req.headers["x-imex-auth"] !== process.env.AUTOHOUSE_AUTH_TOKEN) {
|
||||
return res.sendStatus(401);
|
||||
res.sendStatus(401);
|
||||
return;
|
||||
}
|
||||
|
||||
// Send immediate response and continue processing.
|
||||
@@ -820,7 +822,7 @@ const GenerateDetailLines = (job, line, statuses) => {
|
||||
BackOrdered: line.status === statuses.default_bo ? "1" : "0",
|
||||
Cost: (line.billlines[0] && (line.billlines[0].actual_cost * line.billlines[0].quantity).toFixed(2)) || 0,
|
||||
//Critical: null,
|
||||
Description: line.line_desc ? line.line_desc.replace(NON_ASCII_REGEX, "") : "",
|
||||
Description: line.line_desc ? line.line_desc.replace(/[^\x00-\x7F]/g, "") : "",
|
||||
DiscountMarkup: line.prt_dsmk_m || 0,
|
||||
InvoiceNumber: line.billlines[0] && line.billlines[0].bill.invoice_number,
|
||||
IOUPart: 0,
|
||||
@@ -832,7 +834,7 @@ const GenerateDetailLines = (job, line, statuses) => {
|
||||
OriginalCost: null,
|
||||
OriginalInvoiceNumber: null,
|
||||
PriceEach: line.act_price || 0,
|
||||
PartNumber: line.oem_partno ? line.oem_partno.replace(NON_ASCII_REGEX, "") : "",
|
||||
PartNumber: line.oem_partno ? line.oem_partno.replace(/[^\x00-\x7F]/g, "") : "",
|
||||
ProfitPercent: null,
|
||||
PurchaseOrderNumber: null,
|
||||
Qty: line.part_qty || 0,
|
||||
|
||||
@@ -1,408 +0,0 @@
|
||||
const path = require("path");
|
||||
const queries = require("../graphql-client/queries");
|
||||
const Dinero = require("dinero.js");
|
||||
const moment = require("moment-timezone");
|
||||
const logger = require("../utils/logger");
|
||||
const InstanceManager = require("../utils/instanceMgr").default;
|
||||
const { isString, isEmpty } = require("lodash");
|
||||
const fs = require("fs");
|
||||
const client = require("../graphql-client/graphql-client").client;
|
||||
const { sendServerEmail } = require("../email/sendemail");
|
||||
const { uploadFileToS3 } = require("../utils/s3");
|
||||
const crypto = require("crypto");
|
||||
|
||||
require("dotenv").config({
|
||||
path: path.resolve(process.cwd(), `.env.${process.env.NODE_ENV || "development"}`)
|
||||
});
|
||||
let Client = require("ssh2-sftp-client");
|
||||
|
||||
const AHDateFormat = "YYYY-MM-DD";
|
||||
|
||||
const NON_ASCII_REGEX = /[^\x20-\x7E]/g;
|
||||
|
||||
const ftpSetup = {
|
||||
host: process.env.CARFAX_HOST,
|
||||
port: process.env.CARFAX_PORT,
|
||||
username: process.env.CARFAX_USER,
|
||||
password: process.env.CARFAX_PASSWORD,
|
||||
debug:
|
||||
process.env.NODE_ENV !== "production"
|
||||
? (message, ...data) => logger.log(message, "DEBUG", "api", null, data)
|
||||
: () => {},
|
||||
algorithms: {
|
||||
serverHostKey: ["ssh-rsa", "ssh-dss", "rsa-sha2-256", "rsa-sha2-512", "ecdsa-sha2-nistp256", "ecdsa-sha2-nistp384"]
|
||||
}
|
||||
};
|
||||
|
||||
const S3_BUCKET_NAME = InstanceManager({
|
||||
imex: "imex-carfax-uploads",
|
||||
rome: "rome-carfax-uploads"
|
||||
});
|
||||
const region = InstanceManager.InstanceRegion;
|
||||
const isLocal = isString(process.env?.LOCALSTACK_HOSTNAME) && !isEmpty(process.env?.LOCALSTACK_HOSTNAME);
|
||||
|
||||
const uploadToS3 = (jsonObj) => {
|
||||
const webPath = isLocal
|
||||
? `https://${S3_BUCKET_NAME}.s3.localhost.localstack.cloud:4566/${jsonObj.filename}`
|
||||
: `https://${S3_BUCKET_NAME}.s3.${region}.amazonaws.com/${jsonObj.filename}`;
|
||||
|
||||
uploadFileToS3({ bucketName: S3_BUCKET_NAME, key: jsonObj.filename, content: jsonObj.json })
|
||||
.then(() => {
|
||||
logger.log("CARFAX-s3-upload", "DEBUG", "api", jsonObj.bodyshopid, {
|
||||
imexshopid: jsonObj.imexshopid,
|
||||
filename: jsonObj.filename,
|
||||
webPath
|
||||
});
|
||||
})
|
||||
.catch((error) => {
|
||||
logger.log("CARFAX-s3-upload-error", "ERROR", "api", jsonObj.bodyshopid, {
|
||||
imexshopid: jsonObj.imexshopid,
|
||||
filename: jsonObj.filename,
|
||||
webPath,
|
||||
error: error.message,
|
||||
stack: error.stack
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
exports.default = async (req, res) => {
|
||||
// Only process if in production environment.
|
||||
if (process.env.NODE_ENV !== "production") {
|
||||
return res.sendStatus(403);
|
||||
}
|
||||
// Only process if the appropriate token is provided.
|
||||
if (req.headers["x-imex-auth"] !== process.env.AUTOHOUSE_AUTH_TOKEN) {
|
||||
return res.sendStatus(401);
|
||||
}
|
||||
|
||||
// Send immediate response and continue processing.
|
||||
res.status(202).json({
|
||||
success: true,
|
||||
message: "Processing request ...",
|
||||
timestamp: new Date().toISOString()
|
||||
});
|
||||
|
||||
try {
|
||||
logger.log("CARFAX-start", "DEBUG", "api", null, null);
|
||||
const allXMLResults = [];
|
||||
const allErrors = [];
|
||||
|
||||
const { bodyshops } = await client.request(queries.GET_CARFAX_SHOPS); //Query for the List of Bodyshop Clients.
|
||||
const specificShopIds = req.body.bodyshopIds; // ['uuid];
|
||||
const { start, end, skipUpload, ignoreDateFilter } = req.body; //YYYY-MM-DD
|
||||
|
||||
const shopsToProcess =
|
||||
specificShopIds?.length > 0 ? bodyshops.filter((shop) => specificShopIds.includes(shop.id)) : bodyshops;
|
||||
logger.log("CARFAX-shopsToProcess-generated", "DEBUG", "api", null, null);
|
||||
|
||||
if (shopsToProcess.length === 0) {
|
||||
logger.log("CARFAX-shopsToProcess-empty", "DEBUG", "api", null, null);
|
||||
return;
|
||||
}
|
||||
|
||||
await processShopData(shopsToProcess, start, end, skipUpload, ignoreDateFilter, allXMLResults, allErrors);
|
||||
|
||||
await sendServerEmail({
|
||||
subject: `CARFAX Report ${moment().format("MM-DD-YY")}`,
|
||||
text: `Errors:\n${JSON.stringify(allErrors, null, 2)}\n\nUploaded:\n${JSON.stringify(
|
||||
allXMLResults.map((x) => ({
|
||||
imexshopid: x.imexshopid,
|
||||
filename: x.filename,
|
||||
count: x.count,
|
||||
result: x.result
|
||||
})),
|
||||
null,
|
||||
2
|
||||
)}`
|
||||
});
|
||||
|
||||
logger.log("CARFAX-end", "DEBUG", "api", null, null);
|
||||
} catch (error) {
|
||||
logger.log("CARFAX-error", "ERROR", "api", null, { error: error.message, stack: error.stack });
|
||||
}
|
||||
};
|
||||
|
||||
async function processShopData(shopsToProcess, start, end, skipUpload, ignoreDateFilter, allXMLResults, allErrors) {
|
||||
for (const bodyshop of shopsToProcess) {
|
||||
const shopid = bodyshop.imexshopid?.toLowerCase() || bodyshop.shopname.replace(/[^a-zA-Z0-9]/g, "").toLowerCase()
|
||||
const erroredJobs = [];
|
||||
try {
|
||||
logger.log("CARFAX-start-shop-extract", "DEBUG", "api", bodyshop.id, {
|
||||
shopname: bodyshop.shopname
|
||||
});
|
||||
|
||||
const { jobs, bodyshops_by_pk } = await client.request(queries.CARFAX_QUERY, {
|
||||
bodyshopid: bodyshop.id,
|
||||
...(ignoreDateFilter
|
||||
? {}
|
||||
: {
|
||||
start: start ? moment(start).startOf("day") : moment().subtract(7, "days").startOf("day"),
|
||||
...(end && { end: moment(end).endOf("day") })
|
||||
})
|
||||
});
|
||||
|
||||
const carfaxObject = {
|
||||
shopid: shopid,
|
||||
shop_name: bodyshop.shopname,
|
||||
job: 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("CARFAX-failed-jobs", "ERROR", "api", bodyshop.id, {
|
||||
count: erroredJobs.length,
|
||||
jobs: JSON.stringify(erroredJobs.map((j) => j.job.ro_number))
|
||||
});
|
||||
}
|
||||
|
||||
const jsonObj = {
|
||||
bodyshopid: bodyshop.id,
|
||||
imexshopid: shopid,
|
||||
json: JSON.stringify(carfaxObject, null, 2),
|
||||
filename: `${shopid}_${moment().format("DDMMYYYY_HHMMss")}.json`,
|
||||
count: carfaxObject.job.length
|
||||
};
|
||||
|
||||
if (skipUpload) {
|
||||
fs.writeFileSync(`./logs/${jsonObj.filename}`, jsonObj.json);
|
||||
} else {
|
||||
await uploadViaSFTP(jsonObj);
|
||||
}
|
||||
|
||||
allXMLResults.push({
|
||||
bodyshopid: bodyshop.id,
|
||||
imexshopid: shopid,
|
||||
count: jsonObj.count,
|
||||
filename: jsonObj.filename,
|
||||
result: jsonObj.result
|
||||
});
|
||||
|
||||
logger.log("CARFAX-end-shop-extract", "DEBUG", "api", bodyshop.id, {
|
||||
shopname: bodyshop.shopname
|
||||
});
|
||||
} catch (error) {
|
||||
//Error at the shop level.
|
||||
logger.log("CARFAX-error-shop", "ERROR", "api", bodyshop.id, { error: error.message, stack: error.stack });
|
||||
|
||||
allErrors.push({
|
||||
bodyshopid: bodyshop.id,
|
||||
imexshopid: shopid,
|
||||
CARFAXid: bodyshop.CARFAXid,
|
||||
fatal: true,
|
||||
errors: [error.toString()]
|
||||
});
|
||||
} finally {
|
||||
allErrors.push({
|
||||
bodyshopid: bodyshop.id,
|
||||
imexshopid: shopid,
|
||||
CARFAXid: bodyshop.CARFAXid,
|
||||
errors: erroredJobs.map((ej) => ({
|
||||
ro_number: ej.job?.ro_number,
|
||||
jobid: ej.job?.id,
|
||||
error: ej.error
|
||||
}))
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function uploadViaSFTP(jsonObj) {
|
||||
const sftp = new Client();
|
||||
sftp.on("error", (errors) =>
|
||||
logger.log("CARFAX-sftp-connection-error", "ERROR", "api", jsonObj.bodyshopid, {
|
||||
error: errors.message,
|
||||
stack: errors.stack
|
||||
})
|
||||
);
|
||||
try {
|
||||
// Upload to S3 first.
|
||||
uploadToS3(jsonObj);
|
||||
|
||||
//Connect to the FTP and upload all.
|
||||
await sftp.connect(ftpSetup);
|
||||
|
||||
try {
|
||||
jsonObj.result = await sftp.put(Buffer.from(jsonObj.json), `${jsonObj.filename}`);
|
||||
logger.log("CARFAX-sftp-upload", "DEBUG", "api", jsonObj.bodyshopid, {
|
||||
imexshopid: jsonObj.imexshopid,
|
||||
filename: jsonObj.filename,
|
||||
result: jsonObj.result
|
||||
});
|
||||
} catch (error) {
|
||||
logger.log("CARFAX-sftp-upload-error", "ERROR", "api", jsonObj.bodyshopid, {
|
||||
filename: jsonObj.filename,
|
||||
error: error.message,
|
||||
stack: error.stack
|
||||
});
|
||||
throw error;
|
||||
}
|
||||
} catch (error) {
|
||||
logger.log("CARFAX-sftp-error", "ERROR", "api", jsonObj.bodyshopid, { error: error.message, stack: error.stack });
|
||||
throw error;
|
||||
} finally {
|
||||
sftp.end();
|
||||
}
|
||||
}
|
||||
|
||||
const CreateRepairOrderTag = (job, errorCallback) => {
|
||||
if (!job.job_totals) {
|
||||
errorCallback({
|
||||
jobid: job.id,
|
||||
job: job,
|
||||
ro_number: job.ro_number,
|
||||
error: { toString: () => "No job totals for RO." }
|
||||
});
|
||||
return {};
|
||||
}
|
||||
|
||||
try {
|
||||
const ret = {
|
||||
ro_number: crypto.createHash("md5").update(job.ro_number, "utf8").digest("hex"),
|
||||
v_vin: job.v_vin || "",
|
||||
v_year: job.v_model_yr
|
||||
? parseInt(job.v_model_yr.match(/\d/g))
|
||||
? parseInt(job.v_model_yr.match(/\d/g).join(""), 10)
|
||||
: ""
|
||||
: "",
|
||||
v_make: job.v_make_desc || "",
|
||||
v_model: job.v_model_desc || "",
|
||||
|
||||
date_estimated:
|
||||
(job.date_estimated && moment(job.date_estimated).tz(job.bodyshop.timezone).format(AHDateFormat)) ||
|
||||
(job.created_at && moment(job.created_at).tz(job.bodyshop.timezone).format(AHDateFormat)) ||
|
||||
"",
|
||||
data_opened:
|
||||
(job.date_open && moment(job.date_open).tz(job.bodyshop.timezone).format(AHDateFormat)) ||
|
||||
(job.created_at && moment(job.created_at).tz(job.bodyshop.timezone).format(AHDateFormat)) ||
|
||||
"",
|
||||
date_invoiced:
|
||||
(job.date_invoiced && moment(job.date_invoiced).tz(job.bodyshop.timezone).format(AHDateFormat)) || "",
|
||||
loss_date: (job.loss_date && moment(job.loss_date).format(AHDateFormat)) || "",
|
||||
|
||||
ins_co_nm: job.ins_co_nm || "",
|
||||
loss_desc: job.loss_desc || "",
|
||||
theft_ind: job.theft_ind,
|
||||
tloss_ind: job.tlos_ind,
|
||||
subtotal: Dinero(job.job_totals.totals.subtotal).toUnit(),
|
||||
|
||||
areaofdamage: {
|
||||
impact1: generateAreaOfDamage(job.area_of_damage?.impact1 || ""),
|
||||
impact2: generateAreaOfDamage(job.area_of_damage?.impact2 || "")
|
||||
},
|
||||
|
||||
jobLines: job.joblines.length > 0 ? job.joblines.map((jl) => GenerateDetailLines(jl)) : [generateNullDetailLine()]
|
||||
};
|
||||
return ret;
|
||||
} catch (error) {
|
||||
logger.log("CARFAX-job-data-error", "ERROR", "api", null, { error: error.message, stack: error.stack });
|
||||
errorCallback({ jobid: job.id, ro_number: job.ro_number, error });
|
||||
}
|
||||
};
|
||||
|
||||
const GenerateDetailLines = (line) => {
|
||||
const ret = {
|
||||
line_desc: line.line_desc ? line.line_desc.replace(NON_ASCII_REGEX, "") : null,
|
||||
oem_partno: line.oem_partno ? line.oem_partno.replace(NON_ASCII_REGEX, "") : null,
|
||||
alt_partno: line.alt_partno ? line.alt_partno.replace(NON_ASCII_REGEX, "") : null,
|
||||
lbr_ty: generateLaborType(line.mod_lbr_ty),
|
||||
part_qty: line.part_qty || 0,
|
||||
part_type: generatePartType(line.part_type),
|
||||
act_price: line.act_price || 0
|
||||
};
|
||||
return ret;
|
||||
};
|
||||
|
||||
const generateNullDetailLine = () => {
|
||||
return {
|
||||
line_desc: null,
|
||||
oem_partno: null,
|
||||
alt_partno: null,
|
||||
lbr_ty: null,
|
||||
part_qty: 0,
|
||||
part_type: null,
|
||||
act_price: 0
|
||||
};
|
||||
};
|
||||
|
||||
const generateAreaOfDamage = (loc) => {
|
||||
const areaMap = {
|
||||
"01": "Right Front Corner",
|
||||
"02": "Right Front Side",
|
||||
"03": "Right Side",
|
||||
"04": "Right Rear Side",
|
||||
"05": "Right Rear Corner",
|
||||
"06": "Rear",
|
||||
"07": "Left Rear Corner",
|
||||
"08": "Left Rear Side",
|
||||
"09": "Left Side",
|
||||
10: "Left Front Side",
|
||||
11: "Left Front Corner",
|
||||
12: "Front",
|
||||
13: "Rollover",
|
||||
14: "Uknown",
|
||||
15: "Total Loss",
|
||||
16: "Non-Collision",
|
||||
19: "All Over",
|
||||
25: "Hood",
|
||||
26: "Deck Lid",
|
||||
27: "Roof",
|
||||
28: "Undercarriage",
|
||||
34: "All Over"
|
||||
};
|
||||
return areaMap[loc] || null;
|
||||
};
|
||||
|
||||
const generateLaborType = (type) => {
|
||||
const laborTypeMap = {
|
||||
laa: "Aluminum",
|
||||
lab: "Body",
|
||||
lad: "Diagnostic",
|
||||
lae: "Electrical",
|
||||
laf: "Frame",
|
||||
lag: "Glass",
|
||||
lam: "Mechanical",
|
||||
lar: "Refinish",
|
||||
las: "Structural",
|
||||
lau: "Other - LAU",
|
||||
la1: "Other - LA1",
|
||||
la2: "Other - LA2",
|
||||
la3: "Other - LA3",
|
||||
la4: "Other - LA4",
|
||||
null: "Other",
|
||||
mapa: "Paint Materials",
|
||||
mash: "Shop Materials",
|
||||
rates_subtotal: "Labor Total",
|
||||
"timetickets.labels.shift": "Shift",
|
||||
"timetickets.labels.amshift": "Morning Shift",
|
||||
"timetickets.labels.ambreak": "Morning Break",
|
||||
"timetickets.labels.pmshift": "Afternoon Shift",
|
||||
"timetickets.labels.pmbreak": "Afternoon Break",
|
||||
"timetickets.labels.lunch": "Lunch"
|
||||
};
|
||||
|
||||
return laborTypeMap[type?.toLowerCase()] || null;
|
||||
};
|
||||
|
||||
const generatePartType = (type) => {
|
||||
const partTypeMap = {
|
||||
paa: "Aftermarket",
|
||||
pae: "Existing",
|
||||
pag: "Glass",
|
||||
pal: "LKQ",
|
||||
pan: "OEM",
|
||||
pao: "Other",
|
||||
pas: "Sublet",
|
||||
pasl: "Sublet",
|
||||
ccc: "CC Cleaning",
|
||||
ccd: "CC Damage Waiver",
|
||||
ccdr: "CC Daily Rate",
|
||||
ccf: "CC Refuel",
|
||||
ccm: "CC Mileage",
|
||||
prt_dsmk_total: "Line Item Adjustment"
|
||||
};
|
||||
|
||||
return partTypeMap[type?.toLowerCase()] || null;
|
||||
};
|
||||
@@ -28,11 +28,13 @@ const ftpSetup = {
|
||||
exports.default = async (req, res) => {
|
||||
// Only process if in production environment.
|
||||
if (process.env.NODE_ENV !== "production") {
|
||||
return res.sendStatus(403);
|
||||
res.sendStatus(403);
|
||||
return;
|
||||
}
|
||||
// Only process if the appropriate token is provided.
|
||||
if (req.headers["x-imex-auth"] !== process.env.AUTOHOUSE_AUTH_TOKEN) {
|
||||
return res.sendStatus(401);
|
||||
res.sendStatus(401);
|
||||
return;
|
||||
}
|
||||
|
||||
// Send immediate response and continue processing.
|
||||
|
||||
@@ -3,6 +3,7 @@ const queries = require("../graphql-client/queries");
|
||||
const Dinero = require("dinero.js");
|
||||
const moment = require("moment-timezone");
|
||||
var builder = require("xmlbuilder2");
|
||||
const _ = require("lodash");
|
||||
const logger = require("../utils/logger");
|
||||
const fs = require("fs");
|
||||
require("dotenv").config({
|
||||
@@ -35,11 +36,13 @@ const ftpSetup = {
|
||||
exports.default = async (req, res) => {
|
||||
// Only process if in production environment.
|
||||
if (process.env.NODE_ENV !== "production") {
|
||||
return res.sendStatus(403);
|
||||
res.sendStatus(403);
|
||||
return;
|
||||
}
|
||||
// Only process if the appropriate token is provided.
|
||||
if (req.headers["x-imex-auth"] !== process.env.AUTOHOUSE_AUTH_TOKEN) {
|
||||
return res.sendStatus(401);
|
||||
res.sendStatus(401);
|
||||
return;
|
||||
}
|
||||
|
||||
// Send immediate response and continue processing.
|
||||
|
||||
@@ -5,5 +5,4 @@ exports.claimscorp = require("./claimscorp").default;
|
||||
exports.kaizen = require("./kaizen").default;
|
||||
exports.usageReport = require("./usageReport").default;
|
||||
exports.podium = require("./podium").default;
|
||||
exports.emsUpload = require("./emsUpload").default;
|
||||
exports.carfax = require("./carfax").default;
|
||||
exports.emsUpload = require("./emsUpload").default;
|
||||
@@ -3,6 +3,7 @@ const queries = require("../graphql-client/queries");
|
||||
const Dinero = require("dinero.js");
|
||||
const moment = require("moment-timezone");
|
||||
var builder = require("xmlbuilder2");
|
||||
const _ = require("lodash");
|
||||
const logger = require("../utils/logger");
|
||||
const fs = require("fs");
|
||||
require("dotenv").config({
|
||||
@@ -34,11 +35,13 @@ const ftpSetup = {
|
||||
exports.default = async (req, res) => {
|
||||
// Only process if in production environment.
|
||||
if (process.env.NODE_ENV !== "production") {
|
||||
return res.sendStatus(403);
|
||||
res.sendStatus(403);
|
||||
return;
|
||||
}
|
||||
// Only process if the appropriate token is provided.
|
||||
if (req.headers["x-imex-auth"] !== process.env.AUTOHOUSE_AUTH_TOKEN) {
|
||||
return res.sendStatus(401);
|
||||
res.sendStatus(401);
|
||||
return;
|
||||
}
|
||||
|
||||
// Send immediate response and continue processing.
|
||||
|
||||
@@ -29,11 +29,13 @@ const ftpSetup = {
|
||||
exports.default = async (req, res) => {
|
||||
// Only process if in production environment.
|
||||
if (process.env.NODE_ENV !== "production") {
|
||||
return res.sendStatus(403);
|
||||
res.sendStatus(403);
|
||||
return;
|
||||
}
|
||||
// Only process if the appropriate token is provided.
|
||||
if (req.headers["x-imex-auth"] !== process.env.AUTOHOUSE_AUTH_TOKEN) {
|
||||
return res.sendStatus(401);
|
||||
res.sendStatus(401);
|
||||
return;
|
||||
}
|
||||
|
||||
// Send immediate response and continue processing.
|
||||
|
||||
@@ -878,43 +878,6 @@ exports.CHATTER_QUERY = `query CHATTER_EXPORT($start: timestamptz, $bodyshopid:
|
||||
}
|
||||
}`;
|
||||
|
||||
exports.CARFAX_QUERY = `query CARFAX_EXPORT($start: timestamptz, $bodyshopid: uuid!, $end: timestamptz) {
|
||||
bodyshops_by_pk(id: $bodyshopid){
|
||||
id
|
||||
shopname
|
||||
imexshopid
|
||||
timezone
|
||||
}
|
||||
jobs(where: {_and: [{converted: {_eq: true}}, {v_vin: {_is_null: false}}, {date_invoiced: {_gt: $start}}, {date_invoiced: {_lte: $end}}, {shopid: {_eq: $bodyshopid}}]}) {
|
||||
id
|
||||
created_at
|
||||
ro_number
|
||||
v_model_yr
|
||||
v_model_desc
|
||||
v_make_desc
|
||||
v_vin
|
||||
date_estimated
|
||||
date_open
|
||||
date_invoiced
|
||||
loss_date
|
||||
ins_co_nm
|
||||
loss_desc
|
||||
theft_ind
|
||||
tlos_ind
|
||||
job_totals
|
||||
area_of_damage
|
||||
joblines(where: {removed: {_eq: false}}) {
|
||||
line_desc
|
||||
oem_partno
|
||||
alt_partno
|
||||
mod_lbr_ty
|
||||
part_qty
|
||||
part_type
|
||||
act_price
|
||||
}
|
||||
}
|
||||
}`;
|
||||
|
||||
exports.CLAIMSCORP_QUERY = `query CLAIMSCORP_EXPORT($start: timestamptz, $bodyshopid: uuid!, $end: timestamptz) {
|
||||
bodyshops_by_pk(id: $bodyshopid){
|
||||
id
|
||||
@@ -1853,14 +1816,6 @@ exports.GET_CHATTER_SHOPS = `query GET_CHATTER_SHOPS {
|
||||
}
|
||||
}`;
|
||||
|
||||
exports.GET_CARFAX_SHOPS = `query GET_CARFAX_SHOPS {
|
||||
bodyshops{
|
||||
id
|
||||
shopname
|
||||
imexshopid
|
||||
}
|
||||
}`;
|
||||
|
||||
exports.GET_CLAIMSCORP_SHOPS = `query GET_CLAIMSCORP_SHOPS {
|
||||
bodyshops(where: {claimscorpid: {_is_null: false}, _or: {claimscorpid: {_neq: ""}}}){
|
||||
id
|
||||
|
||||
@@ -144,7 +144,6 @@ const paymentRefund = async (req, res) => {
|
||||
|
||||
logger.log("intellipay-refund-success", "DEBUG", req.user?.email, null, {
|
||||
requestOptions: options,
|
||||
response: response?.data,
|
||||
...logResponseMeta
|
||||
});
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
const express = require("express");
|
||||
const router = express.Router();
|
||||
const { autohouse, claimscorp, chatter, kaizen, usageReport, podium, carfax } = require("../data/data");
|
||||
const { autohouse, claimscorp, chatter, kaizen, usageReport, podium } = require("../data/data");
|
||||
|
||||
router.post("/ah", autohouse);
|
||||
router.post("/cc", claimscorp);
|
||||
@@ -8,6 +8,5 @@ router.post("/chatter", chatter);
|
||||
router.post("/kaizen", kaizen);
|
||||
router.post("/usagereport", usageReport);
|
||||
router.post("/podium", podium);
|
||||
router.post("/carfax", carfax);
|
||||
|
||||
module.exports = router;
|
||||
|
||||
Reference in New Issue
Block a user