617 lines
22 KiB
JavaScript
617 lines
22 KiB
JavaScript
import {gql, useApolloClient, useLazyQuery, useMutation, useQuery,} from "@apollo/client";
|
|
import {useSplitTreatments} from "@splitsoftware/splitio-react";
|
|
import {Button, Col, notification, Row} from "antd";
|
|
import Axios from "axios";
|
|
import _ from "lodash";
|
|
import dayjs from "../../utils/day";
|
|
import queryString from "query-string";
|
|
import React, {useCallback, useEffect, useState} from "react";
|
|
import {useTranslation} from "react-i18next";
|
|
import {connect} from "react-redux";
|
|
import {useLocation, useNavigate} from "react-router-dom";
|
|
import {createStructuredSelector} from "reselect";
|
|
import {logImEXEvent} from "../../firebase/firebase.utils";
|
|
import {
|
|
DELETE_AVAILABLE_JOB,
|
|
QUERY_AVAILABLE_JOBS,
|
|
QUERY_AVAILABLE_NEW_JOBS_EST_DATA_BY_PK,
|
|
} from "../../graphql/available-jobs.queries";
|
|
import {INSERT_NEW_JOB, UPDATE_JOB} from "../../graphql/jobs.queries";
|
|
import {INSERT_NEW_NOTE} from "../../graphql/notes.queries";
|
|
import {SEARCH_VEHICLE_BY_VIN} from "../../graphql/vehicles.queries";
|
|
import {insertAuditTrail} from "../../redux/application/application.actions";
|
|
import {selectBodyshop, selectCurrentUser,} from "../../redux/user/user.selectors";
|
|
import confirmDialog from "../../utils/asyncConfirm";
|
|
import AuditTrailMapping from "../../utils/AuditTrailMappings";
|
|
import CriticalPartsScan from "../../utils/criticalPartsScan";
|
|
import AlertComponent from "../alert/alert.component";
|
|
import JobsAvailableScan from "../jobs-available-scan/jobs-available-scan.component";
|
|
import JobsFindModalContainer from "../jobs-find-modal/jobs-find-modal.container";
|
|
import LoadingSpinner from "../loading-spinner/loading-spinner.component";
|
|
import OwnerFindModalContainer from "../owner-find-modal/owner-find-modal.container";
|
|
import {GetSupplementDelta} from "./jobs-available-supplement.estlines.util";
|
|
import HeaderFields from "./jobs-available-supplement.headerfields";
|
|
import JobsAvailableTableComponent from "./jobs-available-table.component";
|
|
import InstanceRenderManager from "../../utils/instanceRenderMgr";
|
|
|
|
const mapStateToProps = createStructuredSelector({
|
|
bodyshop: selectBodyshop,
|
|
currentUser: selectCurrentUser,
|
|
});
|
|
const mapDispatchToProps = (dispatch) => ({
|
|
insertAuditTrail: ({jobid, operation}) =>
|
|
dispatch(insertAuditTrail({jobid, operation})),
|
|
});
|
|
|
|
export function JobsAvailableContainer({bodyshop, currentUser, insertAuditTrail,}) {
|
|
|
|
const {treatments: {CriticalPartsScanning}} = useSplitTreatments({
|
|
attributes: {},
|
|
names: ["CriticalPartsScanning"],
|
|
splitKey: bodyshop.imexshopid,
|
|
});
|
|
|
|
const {loading, error, data, refetch} = useQuery(QUERY_AVAILABLE_JOBS, {
|
|
fetchPolicy: "network-only",
|
|
nextFetchPolicy: "network-only",
|
|
});
|
|
const {clm_no, availableJobId} = queryString.parse(useLocation().search);
|
|
const history = useNavigate();
|
|
const {t} = useTranslation();
|
|
|
|
const [ownerModalVisible, setOwnerModalVisible] = useState(false);
|
|
const [jobModalVisible, setJobModalVisible] = useState(false);
|
|
|
|
const [selectedJob, setSelectedJob] = useState(null);
|
|
const [selectedOwner, setSelectedOwner] = useState(null);
|
|
const [partsQueueToggle, setPartsQueueToggle] = useState(bodyshop.md_functionality_toggles.parts_queue_toggle);
|
|
|
|
const [insertLoading, setInsertLoading] = useState(false);
|
|
|
|
const [insertNote] = useMutation(INSERT_NEW_NOTE);
|
|
const [deleteJob] = useMutation(DELETE_AVAILABLE_JOB);
|
|
const [updateJob] = useMutation(UPDATE_JOB);
|
|
|
|
const [insertNewJob] = useMutation(INSERT_NEW_JOB);
|
|
const client = useApolloClient();
|
|
|
|
const estDataLazyLoad = useLazyQuery(QUERY_AVAILABLE_NEW_JOBS_EST_DATA_BY_PK);
|
|
const [loadEstData, estDataRaw] = estDataLazyLoad;
|
|
|
|
const importOptionsState = useState({overrideHeaders: false});
|
|
const importOptions = importOptionsState[0];
|
|
const modalSearchState = useState("");
|
|
|
|
//Import Scenario
|
|
const onOwnerFindModalOk = async (lazyData) => {
|
|
logImEXEvent("job_import_new");
|
|
|
|
setOwnerModalVisible(false);
|
|
|
|
setInsertLoading(true);
|
|
const estData = replaceEmpty(
|
|
lazyData?.available_jobs_by_pk || estDataRaw.data.available_jobs_by_pk
|
|
);
|
|
|
|
if (!(estData && estData.est_data)) {
|
|
//We don't have the right data. Error!
|
|
setInsertLoading(false);
|
|
notification["error"]({
|
|
message: t("jobs.errors.creating", {error: "No job data present."}),
|
|
});
|
|
return;
|
|
}
|
|
// if (process.env.REACT_APP_COUNTRY === "USA") {
|
|
//Massage the CCC file set to remove duplicate UNQ_SEQ.
|
|
InstanceRenderManager({executeFunction:true,rome: ResolveCCCLineIssues(estData.est_data, bodyshop) })
|
|
|
|
// } else {
|
|
//IO-539 Check for Parts Rate on PAL for SGI use case.
|
|
//TODO:AIO Check that the async function is actually waiting before moving on.
|
|
InstanceRenderManager({executeFunction: true, imex: await CheckTaxRates(estData.est_data, bodyshop), rome: await CheckTaxRatesUSA(estData.est_data, bodyshop)})
|
|
|
|
// }
|
|
// const newTotals = (
|
|
// await Axios.post("/job/totals", {
|
|
// job: {
|
|
// ...estData.est_data,
|
|
// joblines: estData.est_data.joblines.data,
|
|
// },
|
|
// })
|
|
// ).data;
|
|
|
|
let existingVehicles;
|
|
if (estData.est_data.v_vin) {
|
|
//There's vehicle data, need to double-check the VIN.
|
|
existingVehicles = await client.query({
|
|
query: SEARCH_VEHICLE_BY_VIN,
|
|
variables: {
|
|
vin: estData.est_data.v_vin || estData.est_data.vehicle.data.v_vin,
|
|
},
|
|
});
|
|
}
|
|
|
|
const newJob = {
|
|
...estData.est_data,
|
|
// clm_total: Dinero(newTotals.totals.total_repairs).toFormat("0.00"),
|
|
// owner_owing: Dinero(newTotals.totals.custPayable.total).toFormat("0.00"),
|
|
// job_totals: newTotals,
|
|
date_open: dayjs(),
|
|
status: bodyshop.md_ro_statuses.default_imported,
|
|
notes: {
|
|
data: {
|
|
created_by: currentUser.email,
|
|
audit: true,
|
|
text: t("jobs.labels.importnote"),
|
|
},
|
|
},
|
|
queued_for_parts: partsQueueToggle,
|
|
...(existingVehicles && existingVehicles.data.vehicles.length > 0
|
|
? {vehicleid: existingVehicles.data.vehicles[0].id, vehicle: null}
|
|
: {}),
|
|
};
|
|
|
|
if (selectedOwner) {
|
|
newJob.ownerid = selectedOwner;
|
|
delete newJob.owner;
|
|
}
|
|
if (newJob.vehicleid) {
|
|
delete newJob.vehicle;
|
|
}
|
|
|
|
if (typeof newJob.kmin === "string") {
|
|
newJob.kmin = null;
|
|
}
|
|
|
|
try {
|
|
const r = await insertNewJob({
|
|
variables: {
|
|
job: newJob,
|
|
},
|
|
});
|
|
await Axios.post("/job/totalsssu", {
|
|
id: r.data.insert_jobs.returning[0].id,
|
|
});
|
|
|
|
if (CriticalPartsScanning.treatment === "on") {
|
|
CriticalPartsScan(r.data.insert_jobs.returning[0].id);
|
|
}
|
|
notification["success"]({
|
|
message: t("jobs.successes.created"),
|
|
onClick: () => {
|
|
history(`/manage/jobs/${r.data.insert_jobs.returning[0].id}`);
|
|
},
|
|
});
|
|
//Job has been inserted. Clean up the available jobs record.
|
|
|
|
insertAuditTrail({
|
|
jobid: r.data.insert_jobs.returning[0].id,
|
|
operation: AuditTrailMapping.jobimported(),
|
|
});
|
|
|
|
await deleteJob({
|
|
variables: {id: estData.id},
|
|
}).then((r) => {
|
|
refetch();
|
|
setInsertLoading(false);
|
|
});
|
|
|
|
setPartsQueueToggle(bodyshop.md_functionality_toggles.parts_queue_toggle);
|
|
} catch (r) {
|
|
//error while inserting
|
|
notification["error"]({
|
|
message: t("jobs.errors.creating", {error: r.message}),
|
|
});
|
|
refetch();
|
|
setInsertLoading(false);
|
|
setPartsQueueToggle(bodyshop.md_functionality_toggles.parts_queue_toggle);
|
|
}
|
|
};
|
|
|
|
//Supplement scenario
|
|
const onJobFindModalOk = async () => {
|
|
logImEXEvent("job_import_supplement");
|
|
|
|
setJobModalVisible(false);
|
|
setInsertLoading(true);
|
|
|
|
const estData = estDataRaw.data.available_jobs_by_pk;
|
|
|
|
if (!(estData && estData.est_data)) {
|
|
//We don't have the right data. Error!
|
|
setInsertLoading(false);
|
|
notification["error"]({
|
|
message: t("jobs.errors.creating", {error: "No job data present."}),
|
|
});
|
|
} else {
|
|
//create upsert job
|
|
let supp = replaceEmpty({...estData.est_data});
|
|
//IO-539 Check for Parts Rate on PAL for SGI use case.
|
|
|
|
InstanceRenderManager({executeFunction:true, imex: await CheckTaxRates(supp, bodyshop), rome: await CheckTaxRatesUSA(supp, bodyshop)})
|
|
InstanceRenderManager({executeFunction:true ,rome: ResolveCCCLineIssues(supp, bodyshop) })
|
|
|
|
delete supp.owner;
|
|
delete supp.vehicle;
|
|
delete supp.ins_co_nm;
|
|
if (!importOptions.overrideHeaders) {
|
|
HeaderFields.forEach((item) => delete supp[item]);
|
|
}
|
|
|
|
let suppDelta = await GetSupplementDelta(
|
|
client,
|
|
selectedJob,
|
|
supp.joblines.data
|
|
);
|
|
|
|
delete supp.joblines;
|
|
if (suppDelta !== null) {
|
|
await client.mutate({
|
|
mutation: gql`
|
|
${suppDelta}
|
|
`,
|
|
});
|
|
}
|
|
const updateResult = await updateJob({
|
|
variables: {
|
|
jobId: selectedJob,
|
|
job: {
|
|
...supp,
|
|
// clm_total: Dinero(newTotals.totals.total_repairs).toFormat("0.00"),
|
|
// owner_owing: Dinero(newTotals.totals.custPayable.total).toFormat(
|
|
// "0.00"
|
|
// ),
|
|
// job_totals: newTotals,
|
|
queued_for_parts: partsQueueToggle,
|
|
},
|
|
},
|
|
});
|
|
|
|
setPartsQueueToggle(bodyshop.md_functionality_toggles.parts_queue_toggle);
|
|
|
|
if (CriticalPartsScanning.treatment === "on") {
|
|
CriticalPartsScan(updateResult.data.update_jobs.returning[0].id);
|
|
}
|
|
if (updateResult.errors) {
|
|
//error while inserting
|
|
notification["error"]({
|
|
message: t("jobs.errors.creating", {
|
|
error: JSON.stringify(updateResult.errors),
|
|
}),
|
|
});
|
|
refetch();
|
|
setInsertLoading(false);
|
|
return;
|
|
}
|
|
|
|
const newTotals = await Axios.post("/job/totalsssu", {
|
|
id: selectedJob,
|
|
});
|
|
|
|
if (newTotals.status !== 200) {
|
|
notification["error"]({
|
|
message: t("jobs.errors.totalscalc"),
|
|
});
|
|
setInsertLoading(false);
|
|
return;
|
|
}
|
|
notification["success"]({
|
|
message: t("jobs.successes.supplemented"),
|
|
onClick: () => {
|
|
history(
|
|
`/manage/jobs/${updateResult.data.update_jobs.returning[0].id}`
|
|
);
|
|
},
|
|
});
|
|
//Job has been inserted. Clean up the available jobs record.
|
|
|
|
deleteJob({
|
|
variables: {id: estData.id},
|
|
}).then((r) => {
|
|
refetch();
|
|
setInsertLoading(false);
|
|
});
|
|
|
|
await insertNote({
|
|
variables: {
|
|
noteInput: [
|
|
{
|
|
jobid: selectedJob,
|
|
created_by: currentUser.email,
|
|
audit: true,
|
|
text: t("jobs.labels.supplementnote"),
|
|
},
|
|
],
|
|
},
|
|
});
|
|
insertAuditTrail({
|
|
jobid: selectedJob,
|
|
operation: AuditTrailMapping.jobsupplement(),
|
|
});
|
|
}
|
|
};
|
|
|
|
const owner =
|
|
estDataRaw.data &&
|
|
estDataRaw.data.available_jobs_by_pk &&
|
|
estDataRaw.data.available_jobs_by_pk.est_data &&
|
|
estDataRaw.data.available_jobs_by_pk.est_data.owner &&
|
|
estDataRaw.data.available_jobs_by_pk.est_data.owner.data &&
|
|
!estDataRaw.data.available_jobs_by_pk.issupplement
|
|
? estDataRaw.data.available_jobs_by_pk.est_data.owner.data
|
|
: null;
|
|
|
|
const onOwnerModalCancel = () => {
|
|
setOwnerModalVisible(false);
|
|
setSelectedOwner(null);
|
|
setPartsQueueToggle(bodyshop.md_functionality_toggles.parts_queue_toggle);
|
|
};
|
|
|
|
const onJobModalCancel = () => {
|
|
setJobModalVisible(false);
|
|
modalSearchState[1]("");
|
|
setSelectedJob(null);
|
|
setPartsQueueToggle(bodyshop.md_functionality_toggles.parts_queue_toggle);
|
|
};
|
|
|
|
const addJobAsNew = (record) => {
|
|
loadEstData({variables: {id: record.id}});
|
|
setOwnerModalVisible(true);
|
|
};
|
|
|
|
const addJobAsSupp = useCallback((record) => {
|
|
loadEstData({variables: {id: record.id}});
|
|
modalSearchState[1](record.clm_no);
|
|
setJobModalVisible(true);
|
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
}, []);
|
|
|
|
useEffect(() => {
|
|
if (availableJobId && clm_no)
|
|
addJobAsSupp({id: availableJobId, clm_no: clm_no});
|
|
}, [addJobAsSupp, availableJobId, clm_no]);
|
|
|
|
if (error) return <AlertComponent type="error" message={error.message}/>;
|
|
|
|
|
|
return (
|
|
<LoadingSpinner
|
|
loading={insertLoading}
|
|
message={t("jobs.labels.creating_new_job")}
|
|
>
|
|
<OwnerFindModalContainer
|
|
loading={estDataRaw.loading}
|
|
error={estDataRaw.error}
|
|
owner={owner}
|
|
partsQueueToggle={partsQueueToggle}
|
|
setPartsQueueToggle={setPartsQueueToggle}
|
|
selectedOwner={selectedOwner}
|
|
setSelectedOwner={setSelectedOwner}
|
|
open={ownerModalVisible}
|
|
onOk={onOwnerFindModalOk}
|
|
onCancel={onOwnerModalCancel}
|
|
|
|
/>
|
|
<JobsFindModalContainer
|
|
loading={estDataRaw.loading}
|
|
error={estDataRaw.error}
|
|
selectedJob={selectedJob}
|
|
setSelectedJob={setSelectedJob}
|
|
importOptionsState={importOptionsState}
|
|
open={jobModalVisible}
|
|
onOk={onJobFindModalOk}
|
|
onCancel={onJobModalCancel}
|
|
modalSearchState={modalSearchState}
|
|
partsQueueToggle={partsQueueToggle}
|
|
setPartsQueueToggle={setPartsQueueToggle}
|
|
/>
|
|
{
|
|
|
|
|
|
// currentUser.email.includes("@rome.") ||
|
|
// currentUser.email.includes("@imex.") ? (
|
|
// <Button
|
|
// onClick={async () => {
|
|
// for (const record of data.available_jobs) {
|
|
// //Query the data
|
|
// console.log("Start Job", record.id);
|
|
// const {data} = await loadEstData({
|
|
// variables: {id: record.id},
|
|
// });
|
|
// console.log("Query has been awaited and is complete");
|
|
// await onOwnerFindModalOk(data);
|
|
// }
|
|
// }}
|
|
// >
|
|
// Add all jobs as new.
|
|
// </Button>
|
|
// ) : null
|
|
}
|
|
<Row gutter={[16, 16]}>
|
|
<Col span={24}>
|
|
<JobsAvailableTableComponent
|
|
loading={loading}
|
|
data={data}
|
|
refetch={refetch}
|
|
addJobAsNew={addJobAsNew}
|
|
addJobAsSupp={addJobAsSupp}
|
|
/>
|
|
</Col>
|
|
<Col span={24}>
|
|
<JobsAvailableScan refetch={refetch}/>
|
|
</Col>
|
|
</Row>
|
|
</LoadingSpinner>
|
|
);
|
|
}
|
|
|
|
export default connect(
|
|
mapStateToProps,
|
|
mapDispatchToProps
|
|
)(JobsAvailableContainer);
|
|
|
|
function replaceEmpty(someObj, replaceValue = null) {
|
|
const replacer = (key, value) =>
|
|
value === "" ? replaceValue || null : value;
|
|
//^ because you seem to want to replace (strings) "null" or "undefined" too
|
|
const temp = JSON.stringify(someObj, replacer);
|
|
return JSON.parse(temp);
|
|
}
|
|
|
|
async function CheckTaxRatesUSA(estData,bodyshop){
|
|
if (!estData.parts_tax_rates?.PAM) {
|
|
estData.parts_tax_rates.PAM = estData.parts_tax_rates.PAC;
|
|
}
|
|
|
|
}
|
|
|
|
async function CheckTaxRates(estData, bodyshop) {
|
|
//LKQ Check
|
|
if (
|
|
!estData.parts_tax_rates?.PAL ||
|
|
estData.parts_tax_rates?.PAL?.prt_tax_rt === null ||
|
|
estData.parts_tax_rates?.PAL?.prt_tax_rt === 0
|
|
) {
|
|
const res = await confirmDialog(
|
|
`ImEX Online has detected that there is a missing tax rate for LKQ parts. Pressing OK will set the tax rate to ${bodyshop.bill_tax_rates.state_tax_rate}% and enable the rate. Pressing cancel will keep the tax rate as is.`
|
|
);
|
|
if (res) {
|
|
if (!estData.parts_tax_rates.PAL) {
|
|
estData.parts_tax_rates.PAL = {
|
|
prt_discp: 0,
|
|
prt_mktyp: true,
|
|
prt_mkupp: 0,
|
|
prt_type: "PAL",
|
|
};
|
|
}
|
|
estData.parts_tax_rates.PAL.prt_tax_rt =
|
|
bodyshop.bill_tax_rates.state_tax_rate / 100;
|
|
estData.parts_tax_rates.PAL.prt_tax_in = true;
|
|
}
|
|
}
|
|
//PAC Check
|
|
if (
|
|
!estData.parts_tax_rates?.PAC ||
|
|
estData.parts_tax_rates?.PAC?.prt_tax_rt === null ||
|
|
estData.parts_tax_rates?.PAC?.prt_tax_rt === 0
|
|
) {
|
|
const res = await confirmDialog(
|
|
`ImEX Online has detected that there is a missing tax rate for rechromed parts. Pressing OK will set the tax rate to ${bodyshop.bill_tax_rates.state_tax_rate}% and enable the rate. Pressing cancel will keep the tax rate as is.`
|
|
);
|
|
if (res) {
|
|
if (!estData.parts_tax_rates.PAC) {
|
|
estData.parts_tax_rates.PAC = {
|
|
prt_discp: 0,
|
|
prt_mktyp: true,
|
|
prt_mkupp: 0,
|
|
prt_type: "PAC",
|
|
};
|
|
}
|
|
estData.parts_tax_rates.PAC.prt_tax_rt =
|
|
bodyshop.bill_tax_rates.state_tax_rate / 100;
|
|
estData.parts_tax_rates.PAC.prt_tax_in = true;
|
|
}
|
|
}
|
|
//PAM Check
|
|
if (
|
|
!estData.parts_tax_rates?.PAM ||
|
|
estData.parts_tax_rates?.PAM?.prt_tax_rt === null ||
|
|
estData.parts_tax_rates?.PAM?.prt_tax_rt === 0
|
|
) {
|
|
const res = await confirmDialog(
|
|
`ImEX Online has detected that there is a missing tax rate for remanufactured parts. Pressing OK will set the tax rate to ${bodyshop.bill_tax_rates.state_tax_rate}% and enable the rate. Pressing cancel will keep the tax rate as is.`
|
|
);
|
|
if (res) {
|
|
if (!estData.parts_tax_rates.PAM) {
|
|
estData.parts_tax_rates.PAM = {
|
|
prt_discp: 0,
|
|
prt_mktyp: true,
|
|
prt_mkupp: 0,
|
|
prt_type: "PAM",
|
|
};
|
|
}
|
|
estData.parts_tax_rates.PAM.prt_tax_rt =
|
|
bodyshop.bill_tax_rates.state_tax_rate / 100;
|
|
estData.parts_tax_rates.PAM.prt_tax_in = true;
|
|
}
|
|
}
|
|
|
|
if (
|
|
!estData.parts_tax_rates?.PAR ||
|
|
estData.parts_tax_rates?.PAR?.prt_tax_rt === null ||
|
|
estData.parts_tax_rates?.PAR?.prt_tax_rt === 0
|
|
) {
|
|
const res = await confirmDialog(
|
|
`ImEX Online has detected that there is a missing tax rate for recored parts. Pressing OK will set the tax rate to ${bodyshop.bill_tax_rates.state_tax_rate}% and enable the rate. Pressing cancel will keep the tax rate as is.`
|
|
);
|
|
if (res) {
|
|
if (!estData.parts_tax_rates.PAR) {
|
|
estData.parts_tax_rates.PAR = {
|
|
prt_discp: 0,
|
|
prt_mktyp: true,
|
|
prt_mkupp: 0,
|
|
prt_type: "PAR",
|
|
};
|
|
}
|
|
estData.parts_tax_rates.PAR.prt_tax_rt =
|
|
bodyshop.bill_tax_rates.state_tax_rate / 100;
|
|
estData.parts_tax_rates.PAR.prt_tax_in = true;
|
|
}
|
|
}
|
|
|
|
//IO-1387 If a sublet line is NOT R&R, use the labor tax. If it is, use the sublet tax rate.
|
|
//Currently limited to SK shops only.
|
|
//if (bodyshop.region_config === "CA_SK") {
|
|
estData.joblines.data.forEach((jl, index) => {
|
|
if (
|
|
(jl.part_type === "PASL" || jl.part_type === "PAS") &&
|
|
jl.lbr_op !== "OP11"
|
|
) {
|
|
estData.joblines.data[index].tax_part = jl.lbr_tax;
|
|
}
|
|
|
|
//Set markup lines and tax lines as taxable.
|
|
//900510 is a mark up. 900510 is a discount.
|
|
if (jl.db_ref === "900510") {
|
|
estData.joblines.data[index].tax_part = true;
|
|
}
|
|
});
|
|
//}
|
|
}
|
|
function ResolveCCCLineIssues(estData, bodyshop) {
|
|
//Find all misc amounts, populate them to the act price.
|
|
//TODO Ensure that this doesnt get violated
|
|
//This needs to be done before cleansing unq_seq since some misc prices could move over.
|
|
estData.joblines.data.forEach((line) => {
|
|
if (line.misc_amt && line.misc_amt !== 0) {
|
|
line.act_price = line.act_price + line.misc_amt;
|
|
line.tax_part = !!line.misc_tax;
|
|
}
|
|
});
|
|
|
|
//Generate the list of duplicated UNQ_SEQ that will feed into the next section to scrub the lines.
|
|
const unqSeqHash = _.groupBy(estData.joblines.data, "unq_seq");
|
|
const duplicatedUnqSeq = Object.keys(unqSeqHash).filter(
|
|
(key) => unqSeqHash[key].length > 1
|
|
);
|
|
|
|
duplicatedUnqSeq.forEach((unq_seq) => {
|
|
//Keys are strings, convert to int.
|
|
const int_unq_seq = parseInt(unq_seq);
|
|
|
|
//When line splitting, the first line is always the non-refinish line. We will keep it as is.
|
|
//We will cleanse the second line, which is always the next line.
|
|
const nonRefLineIndex = estData.joblines.data.findIndex(
|
|
(line) => line.unq_seq === int_unq_seq
|
|
);
|
|
estData.joblines.data[nonRefLineIndex + 1] = {
|
|
...estData.joblines.data[nonRefLineIndex + 1],
|
|
part_type: null,
|
|
act_price: 0,
|
|
db_price: 0,
|
|
prt_dsmk_p: 0,
|
|
prt_dsmk_m: 0,
|
|
};
|
|
});
|
|
}
|