IO-3178 Flat Rate ATS

Signed-off-by: Allan Carr <allan.carr@thinkimex.com>
This commit is contained in:
Allan Carr
2025-03-24 09:59:41 -07:00
parent 4c6d28f612
commit 8bb86b9caa
14 changed files with 593 additions and 447 deletions

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 InstanceRenderManager from "../../utils/instanceRenderMgr";
import CABCpvrtCalculator from "../ca-bc-pvrt-calculator/ca-bc-pvrt-calculator.component"; import CABCpvrtCalculator from "../ca-bc-pvrt-calculator/ca-bc-pvrt-calculator.component";
import CurrencyInput from "../form-items-formatted/currency-form-item.component"; import CurrencyInput from "../form-items-formatted/currency-form-item.component";
import JobsDetailRatesChangeButton from "../jobs-detail-rates-change-button/jobs-detail-rates-change-button.component"; import JobsDetailRatesChangeButton from "../jobs-detail-rates-change-button/jobs-detail-rates-change-button.component";
@@ -14,9 +15,8 @@ import JobsDetailRatesLabor from "./jobs-detail-rates.labor.component";
import JobsDetailRatesMaterials from "./jobs-detail-rates.materials.component"; import JobsDetailRatesMaterials from "./jobs-detail-rates.materials.component";
import JobsDetailRatesOther from "./jobs-detail-rates.other.component"; import JobsDetailRatesOther from "./jobs-detail-rates.other.component";
import JobsDetailRatesParts from "./jobs-detail-rates.parts.component"; import JobsDetailRatesParts from "./jobs-detail-rates.parts.component";
import JobsDetailRatesTaxes from "./jobs-detail-rates.taxes.component";
import JobsDetailRatesProfileOVerride from "./jobs-detail-rates.profile-override.component"; import JobsDetailRatesProfileOVerride from "./jobs-detail-rates.profile-override.component";
import InstanceRenderManager from "../../utils/instanceRenderMgr"; import JobsDetailRatesTaxes from "./jobs-detail-rates.taxes.component";
const mapStateToProps = createStructuredSelector({ const mapStateToProps = createStructuredSelector({
jobRO: selectJobReadOnly, jobRO: selectJobReadOnly,
@@ -66,14 +66,48 @@ export function JobsDetailRates({ jobRO, form, job, bodyshop }) {
</Space> </Space>
)} )}
<Form.Item label={t("jobs.fields.auto_add_ats")} name="auto_add_ats" valuePropName="checked"> <Form.Item label={t("jobs.fields.auto_add_ats")} name="auto_add_ats" valuePropName="checked">
<Switch disabled={jobRO} /> <Switch
disabled={jobRO}
onChange={(checked) => {
if (checked) {
form.setFieldsValue({ flat_rate_ats: false });
form.setFieldsValue({ rate_ats: form.getFieldValue('rate_ats') || bodyshop.shoprates.rate_ats });
}
}}
/>
</Form.Item> </Form.Item>
<Form.Item noStyle shouldUpdate={(prev, cur) => prev.auto_add_ats !== cur.auto_add_ats}> <Form.Item noStyle shouldUpdate={(prev, cur) => prev.auto_add_ats !== cur.auto_add_ats}>
{() => { {() => {
if (form.getFieldValue("auto_add_ats")) if (form.getFieldValue("auto_add_ats"))
return ( return (
<Form.Item label={t("jobs.fields.rate_ats")} name="rate_ats" initialValue={bodyshop.shoprates.rate_atp}> <Form.Item label={t("jobs.fields.rate_ats")} name="rate_ats">
<CurrencyInput disabled={jobRO} />
</Form.Item>
);
return null;
}}
</Form.Item>
<Form.Item label={t("jobs.fields.flat_rate_ats")} name="flat_rate_ats" valuePropName="checked">
<Switch
disabled={jobRO}
onChange={(checked) => {
if (checked) {
form.setFieldsValue({ auto_add_ats: false });
form.setFieldsValue({ rate_ats_flat: form.getFieldValue('rate_ats_flat') || bodyshop.shoprates.rate_ats_flat });
}
}}
/>
</Form.Item>
<Form.Item noStyle shouldUpdate={(prev, cur) => prev.flat_rate_ats !== cur.flat_rate_ats}>
{() => {
if (form.getFieldValue("flat_rate_ats"))
return (
<Form.Item
label={t("jobs.fields.rate_ats_flat")}
name="rate_ats_flat"
>
<CurrencyInput disabled={jobRO} /> <CurrencyInput disabled={jobRO} />
</Form.Item> </Form.Item>
); );

View File

@@ -1,6 +1,5 @@
import { DeleteFilled } from "@ant-design/icons"; import { DeleteFilled } from "@ant-design/icons";
import { Button, Form, Input } from "antd"; import { Button, Form, Input, Space } from "antd";
import React from "react";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import CurrencyInput from "../form-items-formatted/currency-form-item.component"; import CurrencyInput from "../form-items-formatted/currency-form-item.component";
import FormListMoveArrows from "../form-list-move-arrows/form-list-move-arrows.component"; import FormListMoveArrows from "../form-list-move-arrows/form-list-move-arrows.component";
@@ -10,326 +9,338 @@ export default function ShopInfoLaborRates({ form }) {
const { t } = useTranslation(); const { t } = useTranslation();
return ( return (
<div> <>
<Form.List name={["md_labor_rates"]}> <LayoutFormRow header={t("bodyshop.labels.shoprates")}>
{(fields, { add, remove, move }) => { <Form.Item label={t("jobs.fields.rate_ats")} name={["shoprates", "rate_ats"]}>
return ( <CurrencyInput min={0} />
<div> </Form.Item>
{fields.map((field, index) => ( <Form.Item label={t("jobs.fields.rate_ats_flat")} name={["shoprates", "rate_ats_flat"]}>
<Form.Item key={field.key}> <CurrencyInput min={0} />
<LayoutFormRow> </Form.Item>
<Form.Item </LayoutFormRow>
label={t("jobs.fields.labor_rate_desc")} <LayoutFormRow header={t("bodyshop.labels.laborrates")}>
key={`${index}rate_label`} <Form.List name={["md_labor_rates"]}>
name={[field.name, "rate_label"]} {(fields, { add, remove, move }) => {
rules={[ return (
{ <div>
required: true {fields.map((field, index) => (
//message: t("general.validation.required"), <Form.Item key={field.key}>
} <LayoutFormRow noDivider={index === 0}>
]} <Form.Item
> label={t("jobs.fields.labor_rate_desc")}
<Input /> key={`${index}rate_label`}
</Form.Item> name={[field.name, "rate_label"]}
<Form.Item rules={[
label={t("jobs.fields.rate_laa")} {
key={`${index}rate_laa`} required: true
name={[field.name, "rate_laa"]} //message: t("general.validation.required"),
rules={[ }
{ ]}
required: true >
//message: t("general.validation.required"), <Input />
} </Form.Item>
]} <Form.Item
> label={t("jobs.fields.rate_laa")}
<CurrencyInput min={0} /> key={`${index}rate_laa`}
</Form.Item> name={[field.name, "rate_laa"]}
<Form.Item rules={[
label={t("jobs.fields.rate_lab")} {
key={`${index}rate_lab`} required: true
name={[field.name, "rate_lab"]} //message: t("general.validation.required"),
rules={[ }
{ ]}
required: true >
//message: t("general.validation.required"), <CurrencyInput min={0} />
} </Form.Item>
]} <Form.Item
> label={t("jobs.fields.rate_lab")}
<CurrencyInput min={0} /> key={`${index}rate_lab`}
</Form.Item> name={[field.name, "rate_lab"]}
<Form.Item rules={[
label={t("jobs.fields.rate_lad")} {
key={`${index}rate_lad`} required: true
name={[field.name, "rate_lad"]} //message: t("general.validation.required"),
rules={[ }
{ ]}
required: true >
//message: t("general.validation.required"), <CurrencyInput min={0} />
} </Form.Item>
]} <Form.Item
> label={t("jobs.fields.rate_lad")}
<CurrencyInput min={0} /> key={`${index}rate_lad`}
</Form.Item> name={[field.name, "rate_lad"]}
<Form.Item rules={[
label={t("jobs.fields.rate_lae")} {
key={`${index}rate_lae`} required: true
name={[field.name, "rate_lae"]} //message: t("general.validation.required"),
rules={[ }
{ ]}
required: true >
//message: t("general.validation.required"), <CurrencyInput min={0} />
} </Form.Item>
]} <Form.Item
> label={t("jobs.fields.rate_lae")}
<CurrencyInput min={0} /> key={`${index}rate_lae`}
</Form.Item> name={[field.name, "rate_lae"]}
<Form.Item rules={[
label={t("jobs.fields.rate_laf")} {
key={`${index}rate_laf`} required: true
name={[field.name, "rate_laf"]} //message: t("general.validation.required"),
rules={[ }
{ ]}
required: true >
//message: t("general.validation.required"), <CurrencyInput min={0} />
} </Form.Item>
]} <Form.Item
> label={t("jobs.fields.rate_laf")}
<CurrencyInput min={0} /> key={`${index}rate_laf`}
</Form.Item> name={[field.name, "rate_laf"]}
<Form.Item rules={[
label={t("jobs.fields.rate_lag")} {
key={`${index}rate_lag`} required: true
name={[field.name, "rate_lag"]} //message: t("general.validation.required"),
rules={[ }
{ ]}
required: true >
//message: t("general.validation.required"), <CurrencyInput min={0} />
} </Form.Item>
]} <Form.Item
> label={t("jobs.fields.rate_lag")}
<CurrencyInput min={0} /> key={`${index}rate_lag`}
</Form.Item> name={[field.name, "rate_lag"]}
<Form.Item rules={[
label={t("jobs.fields.rate_lam")} {
key={`${index}rate_lam`} required: true
name={[field.name, "rate_lam"]} //message: t("general.validation.required"),
rules={[ }
{ ]}
required: true >
//message: t("general.validation.required"), <CurrencyInput min={0} />
} </Form.Item>
]} <Form.Item
> label={t("jobs.fields.rate_lam")}
<CurrencyInput min={0} /> key={`${index}rate_lam`}
</Form.Item> name={[field.name, "rate_lam"]}
<Form.Item rules={[
label={t("jobs.fields.rate_lar")} {
key={`${index}rate_lar`} required: true
name={[field.name, "rate_lar"]} //message: t("general.validation.required"),
rules={[ }
{ ]}
required: true >
//message: t("general.validation.required"), <CurrencyInput min={0} />
} </Form.Item>
]} <Form.Item
> label={t("jobs.fields.rate_lar")}
<CurrencyInput min={0} /> key={`${index}rate_lar`}
</Form.Item> name={[field.name, "rate_lar"]}
<Form.Item rules={[
label={t("jobs.fields.rate_las")} {
key={`${index}rate_las`} required: true
name={[field.name, "rate_las"]} //message: t("general.validation.required"),
rules={[ }
{ ]}
required: true >
//message: t("general.validation.required"), <CurrencyInput min={0} />
} </Form.Item>
]} <Form.Item
> label={t("jobs.fields.rate_las")}
<CurrencyInput min={0} /> key={`${index}rate_las`}
</Form.Item> name={[field.name, "rate_las"]}
<Form.Item rules={[
label={t("jobs.fields.rate_la1")} {
key={`${index}rate_la1`} required: true
name={[field.name, "rate_la1"]} //message: t("general.validation.required"),
rules={[ }
{ ]}
required: true >
//message: t("general.validation.required"), <CurrencyInput min={0} />
} </Form.Item>
]} <Form.Item
> label={t("jobs.fields.rate_la1")}
<CurrencyInput min={0} /> key={`${index}rate_la1`}
</Form.Item> name={[field.name, "rate_la1"]}
<Form.Item rules={[
label={t("jobs.fields.rate_la2")} {
key={`${index}rate_la2`} required: true
name={[field.name, "rate_la2"]} //message: t("general.validation.required"),
rules={[ }
{ ]}
required: true >
//message: t("general.validation.required"), <CurrencyInput min={0} />
} </Form.Item>
]} <Form.Item
> label={t("jobs.fields.rate_la2")}
<CurrencyInput min={0} /> key={`${index}rate_la2`}
</Form.Item> name={[field.name, "rate_la2"]}
<Form.Item rules={[
label={t("jobs.fields.rate_la3")} {
key={`${index}rate_la3`} required: true
name={[field.name, "rate_la3"]} //message: t("general.validation.required"),
rules={[ }
{ ]}
required: true >
//message: t("general.validation.required"), <CurrencyInput min={0} />
} </Form.Item>
]} <Form.Item
> label={t("jobs.fields.rate_la3")}
<CurrencyInput min={0} /> key={`${index}rate_la3`}
</Form.Item> name={[field.name, "rate_la3"]}
<Form.Item rules={[
label={t("jobs.fields.rate_la4")} {
key={`${index}rate_la4`} required: true
name={[field.name, "rate_la4"]} //message: t("general.validation.required"),
rules={[ }
{ ]}
required: true >
//message: t("general.validation.required"), <CurrencyInput min={0} />
} </Form.Item>
]} <Form.Item
> label={t("jobs.fields.rate_la4")}
<CurrencyInput min={0} /> key={`${index}rate_la4`}
</Form.Item> name={[field.name, "rate_la4"]}
<Form.Item rules={[
label={t("jobs.fields.rate_mash")} {
key={`${index}rate_mash`} required: true
name={[field.name, "rate_mash"]} //message: t("general.validation.required"),
rules={[ }
{ ]}
required: true >
//message: t("general.validation.required"), <CurrencyInput min={0} />
} </Form.Item>
]} <Form.Item
> label={t("jobs.fields.rate_mash")}
<CurrencyInput min={0} /> key={`${index}rate_mash`}
</Form.Item> name={[field.name, "rate_mash"]}
<Form.Item rules={[
label={t("jobs.fields.rate_mapa")} {
key={`${index}rate_mapa`} required: true
name={[field.name, "rate_mapa"]} //message: t("general.validation.required"),
rules={[ }
{ ]}
required: true >
//message: t("general.validation.required"), <CurrencyInput min={0} />
} </Form.Item>
]} <Form.Item
> label={t("jobs.fields.rate_mapa")}
<CurrencyInput min={0} /> key={`${index}rate_mapa`}
</Form.Item> name={[field.name, "rate_mapa"]}
<Form.Item rules={[
label={t("jobs.fields.rate_ma2s")} {
key={`${index}rate_ma2s`} required: true
name={[field.name, "rate_ma2s"]} //message: t("general.validation.required"),
rules={[ }
{ ]}
required: true >
//message: t("general.validation.required"), <CurrencyInput min={0} />
} </Form.Item>
]} <Form.Item
> label={t("jobs.fields.rate_ma2s")}
<CurrencyInput min={0} /> key={`${index}rate_ma2s`}
</Form.Item> name={[field.name, "rate_ma2s"]}
<Form.Item rules={[
label={t("jobs.fields.rate_ma3s")} {
key={`${index}rate_ma3s`} required: true
name={[field.name, "rate_ma3s"]} //message: t("general.validation.required"),
rules={[ }
{ ]}
required: true >
//message: t("general.validation.required"), <CurrencyInput min={0} />
} </Form.Item>
]} <Form.Item
> label={t("jobs.fields.rate_ma3s")}
<CurrencyInput min={0} /> key={`${index}rate_ma3s`}
</Form.Item> name={[field.name, "rate_ma3s"]}
{ rules={[
// <Form.Item {
// label={t("jobs.fields.rate_mabl")} required: true
// key={`${index}rate_mabl`} //message: t("general.validation.required"),
// name={[field.name, "rate_mabl"]} }
// rules={[ ]}
// { >
// required: true, <CurrencyInput min={0} />
// //message: t("general.validation.required"), </Form.Item>
// }, {
// ]} // <Form.Item
// > // label={t("jobs.fields.rate_mabl")}
// <CurrencyInput min={0} /> // key={`${index}rate_mabl`}
// </Form.Item> // name={[field.name, "rate_mabl"]}
// <Form.Item // rules={[
// label={t("jobs.fields.rate_macs")} // {
// key={`${index}rate_macs`} // required: true,
// name={[field.name, "rate_macs"]} // //message: t("general.validation.required"),
// rules={[ // },
// { // ]}
// required: true, // >
// //message: t("general.validation.required"), // <CurrencyInput min={0} />
// }, // </Form.Item>
// ]} // <Form.Item
// > // label={t("jobs.fields.rate_macs")}
// <CurrencyInput min={0} /> // key={`${index}rate_macs`}
// </Form.Item> // name={[field.name, "rate_macs"]}
} // rules={[
<Form.Item // {
label={t("jobs.fields.rate_matd")} // required: true,
key={`${index}rate_matd`} // //message: t("general.validation.required"),
name={[field.name, "rate_matd"]} // },
rules={[ // ]}
{ // >
required: true // <CurrencyInput min={0} />
//message: t("general.validation.required"), // </Form.Item>
} }
]} <Form.Item
> label={t("jobs.fields.rate_matd")}
<CurrencyInput min={0} /> key={`${index}rate_matd`}
</Form.Item> name={[field.name, "rate_matd"]}
<Form.Item rules={[
label={t("jobs.fields.rate_mahw")} {
key={`${index}rate_mahw`} required: true
name={[field.name, "rate_mahw"]} //message: t("general.validation.required"),
rules={[ }
{ ]}
required: true >
//message: t("general.validation.required"), <CurrencyInput min={0} />
} </Form.Item>
]} <Form.Item
> label={t("jobs.fields.rate_mahw")}
<CurrencyInput min={0} /> key={`${index}rate_mahw`}
</Form.Item> name={[field.name, "rate_mahw"]}
<DeleteFilled rules={[
onClick={() => { {
remove(field.name); required: true
}} //message: t("general.validation.required"),
/> }
<FormListMoveArrows move={move} index={index} total={fields.rate_length} /> ]}
</LayoutFormRow> >
<CurrencyInput min={0} />
</Form.Item>
<Space>
<DeleteFilled
onClick={() => {
remove(field.name);
}}
/>
<FormListMoveArrows move={move} index={index} total={fields.rate_length} />
</Space>
</LayoutFormRow>
</Form.Item>
))}
<Form.Item>
<Button
type="dashed"
onClick={() => {
add();
}}
style={{ width: "100%" }}
>
{t("bodyshop.actions.newlaborrate")}
</Button>
</Form.Item> </Form.Item>
))} </div>
<Form.Item> );
<Button }}
type="dashed" </Form.List>
onClick={() => { </LayoutFormRow>
add(); </>
}}
style={{ width: "100%" }}
>
{t("bodyshop.actions.newlaborrate")}
</Button>
</Form.Item>
</div>
);
}}
</Form.List>
</div>
); );
} }

View File

@@ -509,6 +509,7 @@ export const GET_JOB_BY_PK = gql`
est_ct_ln est_ct_ln
est_ea est_ea
est_ph1 est_ph1
flat_rate_ats
federal_tax_rate federal_tax_rate
id id
inproduction inproduction
@@ -649,6 +650,7 @@ export const GET_JOB_BY_PK = gql`
policy_no policy_no
production_vars production_vars
rate_ats rate_ats
rate_ats_flat
rate_la1 rate_la1
rate_la2 rate_la2
rate_la3 rate_la3

View File

@@ -722,6 +722,7 @@
"scoreboardsetup": "Scoreboard Setup", "scoreboardsetup": "Scoreboard Setup",
"shop_enabled_features": "Shop Enabled Features", "shop_enabled_features": "Shop Enabled Features",
"shopinfo": "Shop Information", "shopinfo": "Shop Information",
"shoprates": "Shop Rates",
"speedprint": "Speed Print Configuration", "speedprint": "Speed Print Configuration",
"ssbuckets": "Job Size Definitions", "ssbuckets": "Job Size Definitions",
"systemsettings": "System Settings", "systemsettings": "System Settings",
@@ -1658,7 +1659,7 @@
"08": "Left Rear Side", "08": "Left Rear Side",
"09": "Left Side" "09": "Left Side"
}, },
"auto_add_ats": "Automatically Add/Update ATS", "auto_add_ats": "Automatically Add/Update ATS?",
"ca_bc_pvrt": "PVRT", "ca_bc_pvrt": "PVRT",
"ca_customer_gst": "Customer Portion of GST", "ca_customer_gst": "Customer Portion of GST",
"ca_gst_registrant": "GST Registrant", "ca_gst_registrant": "GST Registrant",
@@ -1757,6 +1758,7 @@
"est_ct_ln": "Estimator Last Name", "est_ct_ln": "Estimator Last Name",
"est_ea": "Estimator Email", "est_ea": "Estimator Email",
"est_ph1": "Estimator Phone #", "est_ph1": "Estimator Phone #",
"flat_rate_ats": "Flat Rate ATS?",
"federal_tax_payable": "Federal Tax Payable", "federal_tax_payable": "Federal Tax Payable",
"federal_tax_rate": "Federal Tax Rate", "federal_tax_rate": "Federal Tax Rate",
"ins_addr1": "Insurance Co. Address", "ins_addr1": "Insurance Co. Address",
@@ -1868,6 +1870,7 @@
}, },
"queued_for_parts": "Queued for Parts", "queued_for_parts": "Queued for Parts",
"rate_ats": "ATS Rate", "rate_ats": "ATS Rate",
"rate_ats_flat": "ATS Flat Rate",
"rate_la1": "LA1", "rate_la1": "LA1",
"rate_la2": "LA2", "rate_la2": "LA2",
"rate_la3": "LA3", "rate_la3": "LA3",

View File

@@ -722,6 +722,7 @@
"scoreboardsetup": "", "scoreboardsetup": "",
"shop_enabled_features": "", "shop_enabled_features": "",
"shopinfo": "", "shopinfo": "",
"shoprates": "",
"speedprint": "", "speedprint": "",
"ssbuckets": "", "ssbuckets": "",
"systemsettings": "", "systemsettings": "",
@@ -1757,6 +1758,7 @@
"est_ct_ln": "Apellido del tasador", "est_ct_ln": "Apellido del tasador",
"est_ea": "Correo electrónico del tasador", "est_ea": "Correo electrónico del tasador",
"est_ph1": "Número de teléfono del tasador", "est_ph1": "Número de teléfono del tasador",
"flat_rate_ats": "",
"federal_tax_payable": "Impuesto federal por pagar", "federal_tax_payable": "Impuesto federal por pagar",
"federal_tax_rate": "", "federal_tax_rate": "",
"ins_addr1": "Dirección de Insurance Co.", "ins_addr1": "Dirección de Insurance Co.",
@@ -1868,6 +1870,7 @@
}, },
"queued_for_parts": "", "queued_for_parts": "",
"rate_ats": "", "rate_ats": "",
"rate_ats_flat": "",
"rate_la1": "Tarifa LA1", "rate_la1": "Tarifa LA1",
"rate_la2": "Tarifa LA2", "rate_la2": "Tarifa LA2",
"rate_la3": "Tarifa LA3", "rate_la3": "Tarifa LA3",

View File

@@ -722,6 +722,7 @@
"scoreboardsetup": "", "scoreboardsetup": "",
"shop_enabled_features": "", "shop_enabled_features": "",
"shopinfo": "", "shopinfo": "",
"shoprates": "",
"speedprint": "", "speedprint": "",
"ssbuckets": "", "ssbuckets": "",
"systemsettings": "", "systemsettings": "",
@@ -1757,6 +1758,7 @@
"est_ct_ln": "Nom de l'évaluateur", "est_ct_ln": "Nom de l'évaluateur",
"est_ea": "Courriel de l'évaluateur", "est_ea": "Courriel de l'évaluateur",
"est_ph1": "Numéro de téléphone de l'évaluateur", "est_ph1": "Numéro de téléphone de l'évaluateur",
"flat_rate_ats": "",
"federal_tax_payable": "Impôt fédéral à payer", "federal_tax_payable": "Impôt fédéral à payer",
"federal_tax_rate": "", "federal_tax_rate": "",
"ins_addr1": "Adresse Insurance Co.", "ins_addr1": "Adresse Insurance Co.",
@@ -1868,6 +1870,7 @@
}, },
"queued_for_parts": "", "queued_for_parts": "",
"rate_ats": "", "rate_ats": "",
"rate_ats_flat": "",
"rate_la1": "Taux LA1", "rate_la1": "Taux LA1",
"rate_la2": "Taux LA2", "rate_la2": "Taux LA2",
"rate_la3": "Taux LA3", "rate_la3": "Taux LA3",

View File

@@ -3695,6 +3695,7 @@
- est_st - est_st
- est_zip - est_zip
- federal_tax_rate - federal_tax_rate
- flat_rate_ats
- g_bett_amt - g_bett_amt
- id - id
- inproduction - inproduction
@@ -3789,6 +3790,7 @@
- qb_multiple_payers - qb_multiple_payers
- queued_for_parts - queued_for_parts
- rate_ats - rate_ats
- rate_ats_flat
- rate_la1 - rate_la1
- rate_la2 - rate_la2
- rate_la3 - rate_la3
@@ -3965,6 +3967,7 @@
- est_st - est_st
- est_zip - est_zip
- federal_tax_rate - federal_tax_rate
- flat_rate_ats
- g_bett_amt - g_bett_amt
- id - id
- inproduction - inproduction
@@ -4060,6 +4063,7 @@
- qb_multiple_payers - qb_multiple_payers
- queued_for_parts - queued_for_parts
- rate_ats - rate_ats
- rate_ats_flat
- rate_la1 - rate_la1
- rate_la2 - rate_la2
- rate_la3 - rate_la3
@@ -4247,6 +4251,7 @@
- est_st - est_st
- est_zip - est_zip
- federal_tax_rate - federal_tax_rate
- flat_rate_ats
- g_bett_amt - g_bett_amt
- id - id
- inproduction - inproduction
@@ -4342,6 +4347,7 @@
- qb_multiple_payers - qb_multiple_payers
- queued_for_parts - queued_for_parts
- rate_ats - rate_ats
- rate_ats_flat
- rate_la1 - rate_la1
- rate_la2 - rate_la2
- rate_la3 - rate_la3

View File

@@ -0,0 +1,4 @@
-- Could not auto-generate a down migration.
-- Please write an appropriate down migration for the SQL below:
-- alter table "public"."jobs" add column "flat_rate_ats" boolean
-- null default 'false';

View File

@@ -0,0 +1,2 @@
alter table "public"."jobs" add column "flat_rate_ats" boolean
null default 'false';

View File

@@ -0,0 +1,4 @@
-- Could not auto-generate a down migration.
-- Please write an appropriate down migration for the SQL below:
-- alter table "public"."jobs" add column "rate_ats_flat" numeric
-- null;

View File

@@ -0,0 +1,2 @@
alter table "public"."jobs" add column "rate_ats_flat" numeric
null;

View File

@@ -1485,6 +1485,8 @@ exports.GET_JOB_BY_PK = `query GET_JOB_BY_PK($id: uuid!) {
materials materials
auto_add_ats auto_add_ats
rate_ats rate_ats
flat_rate_ats
rate_ats_flat
joblines(where: { removed: { _eq: false } }){ joblines(where: { removed: { _eq: false } }){
id id
line_no line_no

View File

@@ -1,7 +1,7 @@
const Dinero = require("dinero.js"); const Dinero = require("dinero.js");
const queries = require("../graphql-client/queries"); const queries = require("../graphql-client/queries");
const adminClient = require("../graphql-client/graphql-client").client; // const adminClient = require("../graphql-client/graphql-client").client;
const _ = require("lodash"); // const _ = require("lodash");
const logger = require("../utils/logger"); const logger = require("../utils/logger");
const InstanceMgr = require("../utils/instanceMgr").default; const InstanceMgr = require("../utils/instanceMgr").default;
@@ -45,7 +45,9 @@ exports.totalsSsu = async function (req, res) {
} }
}); });
res.status(200).send(); if (result) {
res.status(200).send();
}
} catch (error) { } catch (error) {
logger.log("job-totals-ssu-USA-error", "ERROR", req?.user?.email, id, { logger.log("job-totals-ssu-USA-error", "ERROR", req?.user?.email, id, {
jobid: id, jobid: id,
@@ -59,7 +61,7 @@ exports.totalsSsu = async function (req, res) {
//IMPORTANT*** These two functions MUST be mirrrored. //IMPORTANT*** These two functions MUST be mirrrored.
async function TotalsServerSide(req, res) { async function TotalsServerSide(req, res) {
const { job, client } = req.body; const { job, client } = req.body;
await AutoAddAtsIfRequired({ job: job, client: client }); await AtsAdjustmentsIfRequired({ job: job, client: client, user: req?.user });
try { try {
let ret = { let ret = {
@@ -137,11 +139,12 @@ async function Totals(req, res) {
const logger = req.logger; const logger = req.logger;
const client = req.userGraphQLClient; const client = req.userGraphQLClient;
logger.log("job-totals-ssu-USA", "DEBUG", req.user.email, job.id, { logger.log("job-totals-USA", "DEBUG", req.user.email, job.id, {
jobid: job.id jobid: job.id,
id: id
}); });
await AutoAddAtsIfRequired({ job, client }); await AtsAdjustmentsIfRequired({ job, client, user: req.user });
try { try {
let ret = { let ret = {
@@ -162,40 +165,45 @@ async function Totals(req, res) {
} }
} }
async function AutoAddAtsIfRequired({ job, client }) { async function AtsAdjustmentsIfRequired({ job, client, user }) {
//Check if ATS should be automatically added. if (job.auto_add_ats || job.flat_rate_ats) {
if (job.auto_add_ats) { let atsAmount = 0;
//Get the total sum of hours that should be the ATS amount.
//Check to see if an ATS line exists.
let atsLineIndex = null; let atsLineIndex = null;
const atsHours = job.joblines.reduce((acc, val, index) => {
if (val.line_desc && val.line_desc.toLowerCase() === "ats amount") {
atsLineIndex = index;
}
if ( //Check if ATS should be automatically added.
val.mod_lbr_ty !== "LA1" && if (job.auto_add_ats) {
val.mod_lbr_ty !== "LA2" && const excludedLaborTypes = new Set(["LAA", "LAG", "LAS", "LAU", "LA1", "LA2", "LA3", "LA4"]);
val.mod_lbr_ty !== "LA3" &&
val.mod_lbr_ty !== "LA4" &&
val.mod_lbr_ty !== "LAU" &&
val.mod_lbr_ty !== "LAG" &&
val.mod_lbr_ty !== "LAS" &&
val.mod_lbr_ty !== "LAA"
) {
acc = acc + val.mod_lb_hrs;
}
return acc; //Get the total sum of hours that should be the ATS amount.
}, 0); //Check to see if an ATS line exists.
const atsHours = job.joblines.reduce((acc, val, index) => {
if (val.line_desc?.toLowerCase() === "ats amount") {
atsLineIndex = index;
}
const atsAmount = atsHours * (job.rate_ats || 0); if (!excludedLaborTypes.has(val.mod_lbr_ty)) {
//If it does, update it in place, and make sure it is updated for local calculations. acc = acc + val.mod_lb_hrs;
}
return acc;
}, 0);
atsAmount = atsHours * (job.rate_ats || 0);
}
//Check if a Flat Rate ATS should be added.
if (job.flat_rate_ats) {
atsLineIndex = ((i) => (i === -1 ? null : i))(
job.joblines.findIndex((line) => line.line_desc?.toLowerCase() === "ats amount")
);
atsAmount = job.rate_ats_flat || 0;
}
//If it does not, create one for local calculations and insert it.
if (atsLineIndex === null) { if (atsLineIndex === null) {
const newAtsLine = { const newAtsLine = {
jobid: job.id, jobid: job.id,
alt_partm: null, alt_partm: null,
line_no: 35,
unq_seq: 0, unq_seq: 0,
line_ind: "E", line_ind: "E",
line_desc: "ATS Amount", line_desc: "ATS Amount",
@@ -220,19 +228,40 @@ async function AutoAddAtsIfRequired({ job, client }) {
prt_dsmk_m: 0.0 prt_dsmk_m: 0.0
}; };
const result = await client.request(queries.INSERT_NEW_JOB_LINE, { try {
lineInput: [newAtsLine] const result = await client.request(queries.INSERT_NEW_JOB_LINE, {
}); lineInput: [newAtsLine]
});
job.joblines.push(newAtsLine); if (result) {
job.joblines.push(newAtsLine);
}
} catch (error) {
logger.log("job-totals-ssu-ats-error", "ERROR", user?.email, job.id, {
jobid: job.id,
error: error.message,
stack: error.stack
});
}
} }
//If it does not, create one for local calculations and insert it. //If it does, update it in place, and make sure it is updated for local calculations.
else { else {
const result = await client.request(queries.UPDATE_JOB_LINE, { try {
line: { act_price: atsAmount }, const result = await client.request(queries.UPDATE_JOB_LINE, {
lineId: job.joblines[atsLineIndex].id line: { act_price: atsAmount },
}); lineId: job.joblines[atsLineIndex].id
job.joblines[atsLineIndex].act_price = atsAmount; });
if (result) {
job.joblines[atsLineIndex].act_price = atsAmount;
}
} catch (error) {
logger.log("job-totals-ssu-ats-error", "ERROR", user?.email, job.id, {
jobid: job.id,
atsLineIndex: atsLineIndex,
error: error.message,
stack: error.stack
});
}
} }
} }
} }
@@ -314,7 +343,7 @@ async function CalculateRatesTotals({ job, client }) {
let hasMashLine = false; let hasMashLine = false;
let hasMahwLine = false; let hasMahwLine = false;
let hasCustomMahwLine; let hasCustomMahwLine;
let mapaOpCodes = ParseCalopCode(job.materials["MAPA"]?.cal_opcode); // let mapaOpCodes = ParseCalopCode(job.materials["MAPA"]?.cal_opcode);
let mashOpCodes = ParseCalopCode(job.materials["MASH"]?.cal_opcode); let mashOpCodes = ParseCalopCode(job.materials["MASH"]?.cal_opcode);
jobLines.forEach((item) => { jobLines.forEach((item) => {
@@ -564,7 +593,7 @@ function CalculatePartsTotals(jobLines, parts_tax_rates, job) {
} }
}; };
default: default: {
if (!value.part_type && value.db_ref !== "900510" && value.db_ref !== "900511") return acc; if (!value.part_type && value.db_ref !== "900510" && value.db_ref !== "900511") return acc;
const discountAmount = const discountAmount =
@@ -631,6 +660,7 @@ function CalculatePartsTotals(jobLines, parts_tax_rates, job) {
) )
} }
}; };
}
} }
}, },
{ {
@@ -652,7 +682,7 @@ function CalculatePartsTotals(jobLines, parts_tax_rates, job) {
let adjustments = {}; let adjustments = {};
//Track all adjustments that need to be made. //Track all adjustments that need to be made.
const linesToAdjustForDiscount = []; //const linesToAdjustForDiscount = [];
Object.keys(parts_tax_rates).forEach((key) => { Object.keys(parts_tax_rates).forEach((key) => {
//Check if there's a discount or a mark up. //Check if there's a discount or a mark up.
let disc = Dinero(), let disc = Dinero(),
@@ -1019,7 +1049,9 @@ function CalculateTaxesTotals(job, otherTotals) {
} }
} catch (error) { } catch (error) {
logger.log("job-totals-USA Key with issue", "error", null, job.id, { logger.log("job-totals-USA Key with issue", "error", null, job.id, {
key key: key,
error: error.message,
stack: error.stack
}); });
} }
}); });
@@ -1158,6 +1190,8 @@ function CalculateTaxesTotals(job, otherTotals) {
exports.default = Totals; exports.default = Totals;
function DiscountNotAlreadyCounted(jobline, joblines) { function DiscountNotAlreadyCounted(jobline, joblines) {
void jobline;
void joblines;
return false; return false;
} }
@@ -1172,27 +1206,35 @@ function IsTrueOrYes(value) {
return value === true || value === "Y" || value === "y"; return value === true || value === "Y" || value === "y";
} }
async function UpdateJobLines(joblinesToUpdate) { // Function not in use from RO to IO Merger 02/05/2024
if (joblinesToUpdate.length === 0) return; // async function UpdateJobLines(joblinesToUpdate) {
const updateQueries = joblinesToUpdate.map((line, index) => // if (joblinesToUpdate.length === 0) return;
generateUpdateQuery(_.pick(line, ["id", "prt_dsmk_m", "prt_dsmk_p"]), index) // const updateQueries = joblinesToUpdate.map((line, index) =>
); // generateUpdateQuery(_.pick(line, ["id", "prt_dsmk_m", "prt_dsmk_p"]), index)
const query = ` // );
mutation UPDATE_EST_LINES{ // const query = `
${updateQueries} // mutation UPDATE_EST_LINES{
} // ${updateQueries}
`; // }
// `;
// try {
// const result = await adminClient.request(query);
// void result;
// } catch (error) {
// logger.log("update-job-lines", "error", null, null, {
// error: error.message,
// stack: error.stack
// });
// }
// }
const result = await adminClient.request(query); // const generateUpdateQuery = (lineToUpdate, index) => {
} // return `
// update_joblines${index}: update_joblines(where: { id: { _eq: "${
const generateUpdateQuery = (lineToUpdate, index) => { // lineToUpdate.id
return ` // }" } }, _set: ${JSON.stringify(lineToUpdate).replace(/"(\w+)"\s*:/g, "$1:")}) {
update_joblines${index}: update_joblines(where: { id: { _eq: "${ // returning {
lineToUpdate.id // id
}" } }, _set: ${JSON.stringify(lineToUpdate).replace(/"(\w+)"\s*:/g, "$1:")}) { // }
returning { // }`;
id // };
}
}`;
};

View File

@@ -1,7 +1,5 @@
const Dinero = require("dinero.js"); const Dinero = require("dinero.js");
const queries = require("../graphql-client/queries"); const queries = require("../graphql-client/queries");
const adminClient = require("../graphql-client/graphql-client").client;
const _ = require("lodash");
const logger = require("../utils/logger"); const logger = require("../utils/logger");
//****************************************************** */ //****************************************************** */
@@ -44,11 +42,16 @@ exports.totalsSsu = async function (req, res) {
} }
}); });
if (!result) {
throw new Error("Failed to update job totals");
}
res.status(200).send(); res.status(200).send();
} catch (error) { } catch (error) {
logger.log("job-totals-ssu-error", "ERROR", req.user.email, id, { logger.log("job-totals-ssu-error", "ERROR", req.user.email, id, {
jobid: id, jobid: id,
error error: error.message,
stack: error.stack
}); });
res.status(503).send(); res.status(503).send();
} }
@@ -57,7 +60,7 @@ exports.totalsSsu = async function (req, res) {
//IMPORTANT*** These two functions MUST be mirrrored. //IMPORTANT*** These two functions MUST be mirrrored.
async function TotalsServerSide(req, res) { async function TotalsServerSide(req, res) {
const { job, client } = req.body; const { job, client } = req.body;
await AutoAddAtsIfRequired({ job: job, client: client }); await AtsAdjustmentsIfRequired({ job: job, client: client, user: req?.user });
try { try {
let ret = { let ret = {
@@ -71,7 +74,8 @@ async function TotalsServerSide(req, res) {
} catch (error) { } catch (error) {
logger.log("job-totals-ssu-error", "ERROR", req?.user?.email, job.id, { logger.log("job-totals-ssu-error", "ERROR", req?.user?.email, job.id, {
jobid: job.id, jobid: job.id,
error error: error.message,
stack: error.stack
}); });
res.status(400).send(JSON.stringify(error)); res.status(400).send(JSON.stringify(error));
} }
@@ -83,13 +87,12 @@ async function Totals(req, res) {
const logger = req.logger; const logger = req.logger;
const client = req.userGraphQLClient; const client = req.userGraphQLClient;
logger.log("job-totals", "DEBUG", req.user.email, job.id, { logger.log("job-totals-", "DEBUG", req.user.email, job.id, {
jobid: job.id jobid: job.id,
id: id
}); });
logger.log("job-totals-ssu", "DEBUG", req.user.email, id, null); await AtsAdjustmentsIfRequired({ job, client, user: req.user });
await AutoAddAtsIfRequired({ job, client });
try { try {
let ret = { let ret = {
@@ -103,46 +106,52 @@ async function Totals(req, res) {
} catch (error) { } catch (error) {
logger.log("job-totals-error", "ERROR", req.user.email, job.id, { logger.log("job-totals-error", "ERROR", req.user.email, job.id, {
jobid: job.id, jobid: job.id,
error error: error.message,
stack: error.stack
}); });
res.status(400).send(JSON.stringify(error)); res.status(400).send(JSON.stringify(error));
} }
} }
async function AutoAddAtsIfRequired({ job, client }) { async function AtsAdjustmentsIfRequired({ job, client, user }) {
//Check if ATS should be automatically added. if (job.auto_add_ats || job.flat_rate_ats) {
if (job.auto_add_ats) { let atsAmount = 0;
//Get the total sum of hours that should be the ATS amount.
//Check to see if an ATS line exists.
let atsLineIndex = null; let atsLineIndex = null;
const atsHours = job.joblines.reduce((acc, val, index) => {
if (val.line_desc && val.line_desc.toLowerCase() === "ats amount") {
atsLineIndex = index;
}
if ( //Check if ATS should be automatically added.
val.mod_lbr_ty !== "LA1" && if (job.auto_add_ats) {
val.mod_lbr_ty !== "LA2" && const excludedLaborTypes = new Set(["LAA", "LAG", "LAS", "LAU", "LA1", "LA2", "LA3", "LA4"]);
val.mod_lbr_ty !== "LA3" &&
val.mod_lbr_ty !== "LA4" &&
val.mod_lbr_ty !== "LAU" &&
val.mod_lbr_ty !== "LAG" &&
val.mod_lbr_ty !== "LAS" &&
val.mod_lbr_ty !== "LAA"
) {
acc = acc + val.mod_lb_hrs;
}
return acc; //Get the total sum of hours that should be the ATS amount.
}, 0); //Check to see if an ATS line exists.
const atsHours = job.joblines.reduce((acc, val, index) => {
if (val.line_desc?.toLowerCase() === "ats amount") {
atsLineIndex = index;
}
const atsAmount = atsHours * (job.rate_ats || 0); if (!excludedLaborTypes.has(val.mod_lbr_ty)) {
//If it does, update it in place, and make sure it is updated for local calculations. acc = acc + val.mod_lb_hrs;
}
return acc;
}, 0);
atsAmount = atsHours * (job.rate_ats || 0);
}
//Check if a Flat Rate ATS should be added.
if (job.flat_rate_ats) {
atsLineIndex = ((i) => (i === -1 ? null : i))(
job.joblines.findIndex((line) => line.line_desc?.toLowerCase() === "ats amount")
);
atsAmount = job.rate_ats_flat || 0;
}
//If it does not, create one for local calculations and insert it.
if (atsLineIndex === null) { if (atsLineIndex === null) {
const newAtsLine = { const newAtsLine = {
jobid: job.id, jobid: job.id,
alt_partm: null, alt_partm: null,
line_no: 35,
unq_seq: 0, unq_seq: 0,
line_ind: "E", line_ind: "E",
line_desc: "ATS Amount", line_desc: "ATS Amount",
@@ -167,22 +176,41 @@ async function AutoAddAtsIfRequired({ job, client }) {
prt_dsmk_m: 0.0 prt_dsmk_m: 0.0
}; };
const result = await client.request(queries.INSERT_NEW_JOB_LINE, { try {
lineInput: [newAtsLine] const result = await client.request(queries.INSERT_NEW_JOB_LINE, {
}); lineInput: [newAtsLine]
});
job.joblines.push(newAtsLine); if (result) {
job.joblines.push(newAtsLine);
}
} catch (error) {
logger.log("job-totals-ssu-ats-error", "ERROR", user?.email, job.id, {
jobid: job.id,
error: error.message,
stack: error.stack
});
}
} }
//If it does not, create one for local calculations and insert it. //If it does, update it in place, and make sure it is updated for local calculations.
else { else {
const result = await client.request(queries.UPDATE_JOB_LINE, { try {
line: { act_price: atsAmount }, const result = await client.request(queries.UPDATE_JOB_LINE, {
lineId: job.joblines[atsLineIndex].id line: { act_price: atsAmount },
}); lineId: job.joblines[atsLineIndex].id
job.joblines[atsLineIndex].act_price = atsAmount; });
if (result) {
job.joblines[atsLineIndex].act_price = atsAmount;
}
} catch (error) {
logger.log("job-totals-ssu-ats-error", "ERROR", user?.email, job.id, {
jobid: job.id,
atsLineIndex: atsLineIndex,
error: error.message,
stack: error.stack
});
}
} }
//console.log(job.jobLines);
} }
} }