Merged release/2025-03-28 into feature/IO-3183-Dependency-Updates-and-maintenance

This commit is contained in:
Dave Richer
2025-03-25 13:42:43 +00:00
19 changed files with 12031 additions and 11766 deletions

View File

@@ -9,6 +9,6 @@ VITE_APP_CLOUDINARY_THUMB_TRANSFORMATIONS=c_fill,h_250,w_250
VITE_APP_FIREBASE_PUBLIC_VAPID_KEY='BG3tzU7L2BXlGZ_3VLK4PNaRceoEXEnmHfxcVbRMF5o5g05ejslhVPki9kBM9cBBT-08Ad9kN3HSpS6JmrWD6h4' VITE_APP_FIREBASE_PUBLIC_VAPID_KEY='BG3tzU7L2BXlGZ_3VLK4PNaRceoEXEnmHfxcVbRMF5o5g05ejslhVPki9kBM9cBBT-08Ad9kN3HSpS6JmrWD6h4'
VITE_APP_STRIPE_PUBLIC_KEY=pk_test_51GqB4TJl3nQjrZ0wCQWAxAhlNF8jKe0tipIa6ExBaxwJGitwvFsIZUEua4dUzaMIAuXp4qwYHXx7lgjyQSwP0Pe900vzm38C7g VITE_APP_STRIPE_PUBLIC_KEY=pk_test_51GqB4TJl3nQjrZ0wCQWAxAhlNF8jKe0tipIa6ExBaxwJGitwvFsIZUEua4dUzaMIAuXp4qwYHXx7lgjyQSwP0Pe900vzm38C7g
VITE_APP_AXIOS_BASE_API_URL=/api/ VITE_APP_AXIOS_BASE_API_URL=/api/
VITE_APP_REPORTS_SERVER_URL=https://reports3.test.imex.online VITE_APP_REPORTS_SERVER_URL=https://reports.test.imex.online
VITE_APP_SPLIT_API=ts615lqgnmk84thn72uk18uu5pgce6e0l4rc VITE_APP_SPLIT_API=ts615lqgnmk84thn72uk18uu5pgce6e0l4rc
VITE_APP_INSTANCE=IMEX VITE_APP_INSTANCE=IMEX

View File

@@ -10,7 +10,7 @@ VITE_APP_CLOUDINARY_THUMB_TRANSFORMATIONS=c_fill,h_250,w_250
VITE_APP_FIREBASE_PUBLIC_VAPID_KEY='BP1B7ZTYpn-KMt6nOxlld6aS8Skt3Q7ZLEqP0hAvGHxG4UojPYiXZ6kPlzZkUC5jH-EcWXomTLtmadAIxurfcHo' VITE_APP_FIREBASE_PUBLIC_VAPID_KEY='BP1B7ZTYpn-KMt6nOxlld6aS8Skt3Q7ZLEqP0hAvGHxG4UojPYiXZ6kPlzZkUC5jH-EcWXomTLtmadAIxurfcHo'
VITE_APP_STRIPE_PUBLIC_KEY=pk_test_51GqB4TJl3nQjrZ0wCQWAxAhlNF8jKe0tipIa6ExBaxwJGitwvFsIZUEua4dUzaMIAuXp4qwYHXx7lgjyQSwP0Pe900vzm38C7g VITE_APP_STRIPE_PUBLIC_KEY=pk_test_51GqB4TJl3nQjrZ0wCQWAxAhlNF8jKe0tipIa6ExBaxwJGitwvFsIZUEua4dUzaMIAuXp4qwYHXx7lgjyQSwP0Pe900vzm38C7g
VITE_APP_AXIOS_BASE_API_URL=/api/ VITE_APP_AXIOS_BASE_API_URL=/api/
VITE_APP_REPORTS_SERVER_URL=https://reports3.test.imex.online VITE_APP_REPORTS_SERVER_URL=https://reports.test.romeonline.io
VITE_APP_SPLIT_API=ts615lqgnmk84thn72uk18uu5pgce6e0l4rc VITE_APP_SPLIT_API=ts615lqgnmk84thn72uk18uu5pgce6e0l4rc
VITE_APP_COUNTRY=USA VITE_APP_COUNTRY=USA
VITE_APP_INSTANCE=ROME VITE_APP_INSTANCE=ROME

View File

@@ -9,7 +9,7 @@ VITE_APP_CLOUDINARY_THUMB_TRANSFORMATIONS=c_fill,h_250,w_250
VITE_APP_FIREBASE_PUBLIC_VAPID_KEY='BN2GcDPjipR5MTEosO5dT4CfQ3cmrdBIsI4juoOQrRijn_5aRiHlwj1mlq0W145mOusx6xynEKl_tvYJhpCc9lo' VITE_APP_FIREBASE_PUBLIC_VAPID_KEY='BN2GcDPjipR5MTEosO5dT4CfQ3cmrdBIsI4juoOQrRijn_5aRiHlwj1mlq0W145mOusx6xynEKl_tvYJhpCc9lo'
VITE_APP_STRIPE_PUBLIC_KEY=pk_test_51GqB4TJl3nQjrZ0wCQWAxAhlNF8jKe0tipIa6ExBaxwJGitwvFsIZUEua4dUzaMIAuXp4qwYHXx7lgjyQSwP0Pe900vzm38C7g VITE_APP_STRIPE_PUBLIC_KEY=pk_test_51GqB4TJl3nQjrZ0wCQWAxAhlNF8jKe0tipIa6ExBaxwJGitwvFsIZUEua4dUzaMIAuXp4qwYHXx7lgjyQSwP0Pe900vzm38C7g
VITE_APP_AXIOS_BASE_API_URL=https://api.test.imex.online/ VITE_APP_AXIOS_BASE_API_URL=https://api.test.imex.online/
VITE_APP_REPORTS_SERVER_URL=https://reports3.test.imex.online VITE_APP_REPORTS_SERVER_URL=https://reports.test.imex.online
VITE_APP_IS_TEST=true VITE_APP_IS_TEST=true
VITE_APP_SPLIT_API=ts615lqgnmk84thn72uk18uu5pgce6e0l4rc VITE_APP_SPLIT_API=ts615lqgnmk84thn72uk18uu5pgce6e0l4rc
VITE_APP_INSTANCE=IMEX VITE_APP_INSTANCE=IMEX

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,14 +9,23 @@ export default function ShopInfoLaborRates({ form }) {
const { t } = useTranslation(); const { t } = useTranslation();
return ( return (
<div> <>
<LayoutFormRow header={t("bodyshop.labels.shoprates")}>
<Form.Item label={t("jobs.fields.rate_ats")} name={["shoprates", "rate_ats"]}>
<CurrencyInput min={0} />
</Form.Item>
<Form.Item label={t("jobs.fields.rate_ats_flat")} name={["shoprates", "rate_ats_flat"]}>
<CurrencyInput min={0} />
</Form.Item>
</LayoutFormRow>
<LayoutFormRow header={t("bodyshop.labels.laborrates")}>
<Form.List name={["md_labor_rates"]}> <Form.List name={["md_labor_rates"]}>
{(fields, { add, remove, move }) => { {(fields, { add, remove, move }) => {
return ( return (
<div> <div>
{fields.map((field, index) => ( {fields.map((field, index) => (
<Form.Item key={field.key}> <Form.Item key={field.key}>
<LayoutFormRow> <LayoutFormRow noDivider={index === 0}>
<Form.Item <Form.Item
label={t("jobs.fields.labor_rate_desc")} label={t("jobs.fields.labor_rate_desc")}
key={`${index}rate_label`} key={`${index}rate_label`}
@@ -306,12 +314,14 @@ export default function ShopInfoLaborRates({ form }) {
> >
<CurrencyInput min={0} /> <CurrencyInput min={0} />
</Form.Item> </Form.Item>
<Space>
<DeleteFilled <DeleteFilled
onClick={() => { onClick={() => {
remove(field.name); remove(field.name);
}} }}
/> />
<FormListMoveArrows move={move} index={index} total={fields.rate_length} /> <FormListMoveArrows move={move} index={index} total={fields.rate_length} />
</Space>
</LayoutFormRow> </LayoutFormRow>
</Form.Item> </Form.Item>
))} ))}
@@ -330,6 +340,7 @@ export default function ShopInfoLaborRates({ form }) {
); );
}} }}
</Form.List> </Form.List>
</div> </LayoutFormRow>
</>
); );
} }

View File

@@ -1,11 +1,10 @@
import { Alert, Form, Switch } from "antd"; import { Alert, Form, Select, Switch } from "antd";
import React from "react";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import LayoutFormRow from "../layout-form-row/layout-form-row.component"; import LayoutFormRow from "../layout-form-row/layout-form-row.component";
import {connect} from "react-redux"; import { connect } from "react-redux";
import {createStructuredSelector} from "reselect"; import { createStructuredSelector } from "reselect";
import {selectBodyshop} from "../../redux/user/user.selectors"; import { selectBodyshop } from "../../redux/user/user.selectors";
const mapStateToProps = createStructuredSelector({ const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop bodyshop: selectBodyshop
@@ -16,17 +15,17 @@ const mapDispatchToProps = () => ({
export default connect(mapStateToProps, mapDispatchToProps)(ShopInfoIntellipay); export default connect(mapStateToProps, mapDispatchToProps)(ShopInfoIntellipay);
// noinspection JSUnusedLocalSymbols // noinspection JSUnusedLocalSymbols
export function ShopInfoIntellipay({bodyshop, form}) { export function ShopInfoIntellipay({ bodyshop, form }) {
const {t} = useTranslation(); const { t } = useTranslation();
return ( return (
<> <>
<Form.Item dependencies={[["intellipay_config", "enable_cash_discount"]]}> <Form.Item dependencies={[["intellipay_config", "enable_cash_discount"]]}>
{() => { {() => {
const {intellipay_config} = form.getFieldsValue(); const { intellipay_config } = form.getFieldsValue();
if (intellipay_config?.enable_cash_discount) if (intellipay_config?.enable_cash_discount)
return <Alert message={t("bodyshop.labels.intellipay_cash_discount")}/>; return <Alert message={t("bodyshop.labels.intellipay_cash_discount")} />;
}} }}
</Form.Item> </Form.Item>
@@ -36,7 +35,93 @@ export function ShopInfoIntellipay({bodyshop, form}) {
valuePropName="checked" valuePropName="checked"
name={["intellipay_config", "enable_cash_discount"]} name={["intellipay_config", "enable_cash_discount"]}
> >
<Switch/> <Switch />
</Form.Item>
</LayoutFormRow>
<LayoutFormRow header={t("bodyshop.fields.intellipay_config.payment_type")}>
<Form.Item
label={t("bodyshop.fields.intellipay_config.payment_map.visa")}
name={["intellipay_config", "payment_map", "visa"]}
>
<Select showSearch>
{bodyshop.md_payment_types.map((item, idx) => (
<Select.Option key={idx} value={item}>
{item}
</Select.Option>
))}
</Select>
</Form.Item>
<Form.Item
label={t("bodyshop.fields.intellipay_config.payment_map.mast")}
name={["intellipay_config", "payment_map", "mast"]}
>
<Select showSearch>
{bodyshop.md_payment_types.map((item, idx) => (
<Select.Option key={idx} value={item}>
{item}
</Select.Option>
))}
</Select>
</Form.Item>
<Form.Item
label={t("bodyshop.fields.intellipay_config.payment_map.amex")}
name={["intellipay_config", "payment_map", "amex"]}
>
<Select showSearch>
{bodyshop.md_payment_types.map((item, idx) => (
<Select.Option key={idx} value={item}>
{item}
</Select.Option>
))}
</Select>
</Form.Item>
<Form.Item
label={t("bodyshop.fields.intellipay_config.payment_map.disc")}
name={["intellipay_config", "payment_map", "disc"]}
>
<Select showSearch>
{bodyshop.md_payment_types.map((item, idx) => (
<Select.Option key={idx} value={item}>
{item}
</Select.Option>
))}
</Select>
</Form.Item>
<Form.Item
label={t("bodyshop.fields.intellipay_config.payment_map.dnrs")}
name={["intellipay_config", "payment_map", "dnrs"]}
>
<Select showSearch>
{bodyshop.md_payment_types.map((item, idx) => (
<Select.Option key={idx} value={item}>
{item}
</Select.Option>
))}
</Select>
</Form.Item>
<Form.Item
label={t("bodyshop.fields.intellipay_config.payment_map.jcb")}
name={["intellipay_config", "payment_map", "jcb"]}
>
<Select showSearch>
{bodyshop.md_payment_types.map((item, idx) => (
<Select.Option key={idx} value={item}>
{item}
</Select.Option>
))}
</Select>
</Form.Item>
<Form.Item
label={t("bodyshop.fields.intellipay_config.payment_map.intr")}
name={["intellipay_config", "payment_map", "intr"]}
>
<Select showSearch>
{bodyshop.md_payment_types.map((item, idx) => (
<Select.Option key={idx} value={item}>
{item}
</Select.Option>
))}
</Select>
</Form.Item> </Form.Item>
</LayoutFormRow> </LayoutFormRow>
</> </>

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

@@ -334,7 +334,17 @@
}, },
"intellipay_config": { "intellipay_config": {
"cash_discount_percentage": "Cash Discount %", "cash_discount_percentage": "Cash Discount %",
"enable_cash_discount": "Enable Cash Discounting" "enable_cash_discount": "Enable Cash Discounting",
"payment_type": "Payment Type Map",
"payment_map": {
"amex": "American Express",
"disc": "Discover",
"dnrs": "Diners",
"intr": "Interac",
"jcb": "JCB",
"mast": "MasterCard",
"visa": "Visa"
}
}, },
"invoice_federal_tax_rate": "Invoices - Federal Tax Rate", "invoice_federal_tax_rate": "Invoices - Federal Tax Rate",
"invoice_local_tax_rate": "Invoices - Local Tax Rate", "invoice_local_tax_rate": "Invoices - Local Tax Rate",
@@ -712,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",
@@ -1648,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",
@@ -1747,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",
@@ -1858,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

@@ -334,7 +334,17 @@
}, },
"intellipay_config": { "intellipay_config": {
"cash_discount_percentage": "", "cash_discount_percentage": "",
"enable_cash_discount": "" "enable_cash_discount": "",
"payment_type": "",
"payment_map": {
"amex": "American Express",
"disc": "Discover",
"dnrs": "Diners",
"intr": "Interac",
"jcb": "JCB",
"mast": "MasterCard",
"visa": "Visa"
}
}, },
"invoice_federal_tax_rate": "", "invoice_federal_tax_rate": "",
"invoice_local_tax_rate": "", "invoice_local_tax_rate": "",
@@ -712,6 +722,7 @@
"scoreboardsetup": "", "scoreboardsetup": "",
"shop_enabled_features": "", "shop_enabled_features": "",
"shopinfo": "", "shopinfo": "",
"shoprates": "",
"speedprint": "", "speedprint": "",
"ssbuckets": "", "ssbuckets": "",
"systemsettings": "", "systemsettings": "",
@@ -1747,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.",
@@ -1858,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

@@ -334,7 +334,17 @@
}, },
"intellipay_config": { "intellipay_config": {
"cash_discount_percentage": "", "cash_discount_percentage": "",
"enable_cash_discount": "" "enable_cash_discount": "",
"payment_type": "",
"payment_map": {
"amex": "American Express",
"disc": "Discover",
"dnrs": "Diners",
"intr": "Interac",
"jcb": "JCB",
"mast": "MasterCard",
"visa": "Visa"
}
}, },
"invoice_federal_tax_rate": "", "invoice_federal_tax_rate": "",
"invoice_local_tax_rate": "", "invoice_local_tax_rate": "",
@@ -712,6 +722,7 @@
"scoreboardsetup": "", "scoreboardsetup": "",
"shop_enabled_features": "", "shop_enabled_features": "",
"shopinfo": "", "shopinfo": "",
"shoprates": "",
"speedprint": "", "speedprint": "",
"ssbuckets": "", "ssbuckets": "",
"systemsettings": "", "systemsettings": "",
@@ -1747,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.",
@@ -1858,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

@@ -371,6 +371,7 @@ exports.postback = async (req, res) => {
iprequest: values, iprequest: values,
decodedComment decodedComment
}; };
const ipMapping = req.body?.bodyshop?.intellipay_config?.payment_map;
logger.log("intellipay-postback-received", "DEBUG", req.user?.email, null, logResponseMeta); logger.log("intellipay-postback-received", "DEBUG", req.user?.email, null, logResponseMeta);
@@ -417,7 +418,7 @@ exports.postback = async (req, res) => {
amount: p.amount, amount: p.amount,
transactionid: values.authcode, transactionid: values.authcode,
payer: "Customer", payer: "Customer",
type: values.cardtype, type: ipMapping ? ipMapping[(values.cardtype || "").toLowerCase()] || values.cardtype : values.cardtype,
jobid: p.jobid, jobid: p.jobid,
date: moment(Date.now()), date: moment(Date.now()),
payment_responses: { payment_responses: {
@@ -481,7 +482,7 @@ exports.postback = async (req, res) => {
amount: values.total, amount: values.total,
transactionid: values.authcode, transactionid: values.authcode,
payer: "Customer", payer: "Customer",
type: values.cardtype, type: ipMapping ? ipMapping[(values.cardtype || "").toLowerCase()] || values.cardtype : values.cardtype,
jobid: values.invoice, jobid: values.invoice,
date: moment(Date.now()) date: moment(Date.now())
} }

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) {
} }
}); });
if (result) {
res.status(200).send(); 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 = {
@@ -138,10 +140,11 @@ async function Totals(req, res) {
const client = req.userGraphQLClient; const client = req.userGraphQLClient;
logger.log("job-totals-ssu-USA", "DEBUG", req.user.email, job.id, { logger.log("job-totals-ssu-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 = {
@@ -153,7 +156,7 @@ async function Totals(req, res) {
res.status(200).json(ret); res.status(200).json(ret);
} catch (error) { } catch (error) {
logger.log("job-totals-USA-error", "ERROR", req.user.email, job.id, { logger.log("job-totals-ssu-USA-error", "ERROR", req.user.email, job.id, {
jobid: job.id, jobid: job.id,
error: error.message, error: error.message,
stack: error.stack stack: error.stack
@@ -162,40 +165,45 @@ async function Totals(req, res) {
} }
} }
async function AutoAddAtsIfRequired({ job, client }) { async function AtsAdjustmentsIfRequired({ job, client, user }) {
if (job.auto_add_ats || job.flat_rate_ats) {
let atsAmount = 0;
let atsLineIndex = null;
//Check if ATS should be automatically added. //Check if ATS should be automatically added.
if (job.auto_add_ats) { if (job.auto_add_ats) {
const excludedLaborTypes = new Set(["LAA", "LAG", "LAS", "LAU", "LA1", "LA2", "LA3", "LA4"]);
//Get the total sum of hours that should be the ATS amount. //Get the total sum of hours that should be the ATS amount.
//Check to see if an ATS line exists. //Check to see if an ATS line exists.
let atsLineIndex = null;
const atsHours = job.joblines.reduce((acc, val, index) => { const atsHours = job.joblines.reduce((acc, val, index) => {
if (val.line_desc && val.line_desc.toLowerCase() === "ats amount") { if (val.line_desc?.toLowerCase() === "ats amount") {
atsLineIndex = index; atsLineIndex = index;
} }
if ( if (!excludedLaborTypes.has(val.mod_lbr_ty)) {
val.mod_lbr_ty !== "LA1" &&
val.mod_lbr_ty !== "LA2" &&
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; acc = acc + val.mod_lb_hrs;
} }
return acc; return acc;
}, 0); }, 0);
const atsAmount = atsHours * (job.rate_ats || 0); atsAmount = atsHours * (job.rate_ats || 0);
//If it does, update it in place, and make sure it is updated for local calculations. }
//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,20 +228,43 @@ async function AutoAddAtsIfRequired({ job, client }) {
prt_dsmk_m: 0.0 prt_dsmk_m: 0.0
}; };
try {
const result = await client.request(queries.INSERT_NEW_JOB_LINE, { const result = await client.request(queries.INSERT_NEW_JOB_LINE, {
lineInput: [newAtsLine] lineInput: [newAtsLine]
}); });
if (result) {
job.joblines.push(newAtsLine); job.joblines.push(newAtsLine);
} }
//If it does not, create one for local calculations and insert it. } 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, update it in place, and make sure it is updated for local calculations.
else { else {
try {
const result = await client.request(queries.UPDATE_JOB_LINE, { const result = await client.request(queries.UPDATE_JOB_LINE, {
line: { act_price: atsAmount }, line: { act_price: atsAmount },
lineId: job.joblines[atsLineIndex].id lineId: job.joblines[atsLineIndex].id
}); });
if (result) {
job.joblines[atsLineIndex].act_price = atsAmount; 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,
atsAmount: atsAmount,
jobline: job.joblines[atsLineIndex],
error: error.message,
stack: error.stack
});
}
}
} }
} }
@@ -314,7 +345,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 +595,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 =
@@ -632,6 +663,7 @@ function CalculatePartsTotals(jobLines, parts_tax_rates, job) {
} }
}; };
} }
}
}, },
{ {
parts: { parts: {
@@ -652,7 +684,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 +1051,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
}); });
} }
}); });
@@ -1157,6 +1191,7 @@ function CalculateTaxesTotals(job, otherTotals) {
exports.default = Totals; exports.default = Totals;
//eslint-disable-next-line no-unused-vars
function DiscountNotAlreadyCounted(jobline, joblines) { function DiscountNotAlreadyCounted(jobline, joblines) {
return false; return false;
} }
@@ -1172,27 +1207,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-ssu", "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 = {
@@ -101,48 +104,54 @@ async function Totals(req, res) {
res.status(200).json(ret); res.status(200).json(ret);
} catch (error) { } catch (error) {
logger.log("job-totals-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));
} }
} }
async function AutoAddAtsIfRequired({ job, client }) { async function AtsAdjustmentsIfRequired({ job, client, user }) {
if (job.auto_add_ats || job.flat_rate_ats) {
let atsAmount = 0;
let atsLineIndex = null;
//Check if ATS should be automatically added. //Check if ATS should be automatically added.
if (job.auto_add_ats) { if (job.auto_add_ats) {
const excludedLaborTypes = new Set(["LAA", "LAG", "LAS", "LAU", "LA1", "LA2", "LA3", "LA4"]);
//Get the total sum of hours that should be the ATS amount. //Get the total sum of hours that should be the ATS amount.
//Check to see if an ATS line exists. //Check to see if an ATS line exists.
let atsLineIndex = null;
const atsHours = job.joblines.reduce((acc, val, index) => { const atsHours = job.joblines.reduce((acc, val, index) => {
if (val.line_desc && val.line_desc.toLowerCase() === "ats amount") { if (val.line_desc?.toLowerCase() === "ats amount") {
atsLineIndex = index; atsLineIndex = index;
} }
if ( if (!excludedLaborTypes.has(val.mod_lbr_ty)) {
val.mod_lbr_ty !== "LA1" &&
val.mod_lbr_ty !== "LA2" &&
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; acc = acc + val.mod_lb_hrs;
} }
return acc; return acc;
}, 0); }, 0);
const atsAmount = atsHours * (job.rate_ats || 0); atsAmount = atsHours * (job.rate_ats || 0);
//If it does, update it in place, and make sure it is updated for local calculations. }
//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,43 @@ async function AutoAddAtsIfRequired({ job, client }) {
prt_dsmk_m: 0.0 prt_dsmk_m: 0.0
}; };
try {
const result = await client.request(queries.INSERT_NEW_JOB_LINE, { const result = await client.request(queries.INSERT_NEW_JOB_LINE, {
lineInput: [newAtsLine] lineInput: [newAtsLine]
}); });
if (result) {
job.joblines.push(newAtsLine); job.joblines.push(newAtsLine);
} }
//If it does not, create one for local calculations and insert it. } 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, update it in place, and make sure it is updated for local calculations.
else { else {
try {
const result = await client.request(queries.UPDATE_JOB_LINE, { const result = await client.request(queries.UPDATE_JOB_LINE, {
line: { act_price: atsAmount }, line: { act_price: atsAmount },
lineId: job.joblines[atsLineIndex].id lineId: job.joblines[atsLineIndex].id
}); });
if (result) {
job.joblines[atsLineIndex].act_price = atsAmount; job.joblines[atsLineIndex].act_price = atsAmount;
} }
} catch (error) {
//console.log(job.jobLines); logger.log("job-totals-ssu-ats-error", "ERROR", user?.email, job.id, {
jobid: job.id,
atsLineIndex: atsLineIndex,
atsAmount: atsAmount,
jobline: job.joblines[atsLineIndex],
error: error.message,
stack: error.stack
});
}
}
} }
} }