Merged in feature/IO-3255-simplified-part-management (pull request #2509)

Feature/IO-3255 simplified part management
This commit is contained in:
Dave Richer
2025-08-25 16:58:43 +00:00
3 changed files with 60 additions and 67 deletions

View File

@@ -37,7 +37,7 @@ export function PrintCenterJobsPartsComponent({ printCenterModal, bodyshop, tech
.filter(
(temp) =>
(!temp.regions ||
(temp.regions && temp.regions[bodyshop.region_config]) ||
temp.regions?.[bodyshop.region_config] ||
(temp.regions && bodyshop.region_config.includes(Object.keys(temp.regions)) === true)) &&
(!temp.dms || temp.dms === false)
)
@@ -46,7 +46,7 @@ export function PrintCenterJobsPartsComponent({ printCenterModal, bodyshop, tech
.filter(
(temp) =>
!temp.regions ||
(temp.regions && temp.regions[bodyshop.region_config]) ||
temp.regions?.[bodyshop.region_config] ||
(temp.regions && bodyshop.region_config.includes(Object.keys(temp.regions)) === true)
);
@@ -82,7 +82,7 @@ export function PrintCenterJobsPartsComponent({ printCenterModal, bodyshop, tech
variables: { id: jobId }
},
{
to: job && job.ownr_ea,
to: job?.ownr_ea,
subject: cards.find((c) => c.key === key)?.subject
},
"e",
@@ -129,7 +129,7 @@ export function PrintCenterJobsPartsComponent({ printCenterModal, bodyshop, tech
const columns = `repeat(${actions.length}, 1fr)`;
return (
<Col key={item.key} xs={24} sm={12}>
<Col key={item.key} xs={24} sm={24} md={24} lg={24} xl={24}>
<Card hoverable style={{ minHeight: 100 }}>
<div style={{ display: "flex", alignItems: "center", gap: 12 }}>
<div style={{ flex: "1 1 70%", minWidth: 0 }}>

View File

@@ -9,7 +9,8 @@ const {
GET_JOB_BY_CLAIM,
UPDATE_JOB_BY_ID,
SOFT_DELETE_JOBLINES_BY_IDS,
INSERT_JOBLINES
INSERT_JOBLINES,
GET_JOBLINES_NOTES_BY_JOBID_UNQSEQ
} = require("../partsManagement.queries");
/**
@@ -60,8 +61,9 @@ const extractUpdatedJobData = (rq) => {
* - Keep part and labor on the same jobline
* - Aggregate RefinishLabor into secondary labor fields and add its amount to lbr_amt
* - SUBLET-only lines become PAS part_type with act_price = SubletAmount
* Accepts currentJobLineNotes map for notes merging.
*/
const extractUpdatedJobLines = (addsChgs = {}, jobId) => {
const extractUpdatedJobLines = (addsChgs = {}, jobId, currentJobLineNotes = {}) => {
const linesIn = Array.isArray(addsChgs.DamageLineInfo) ? addsChgs.DamageLineInfo : [addsChgs.DamageLineInfo || {}];
const coerceManual = (val) =>
@@ -83,12 +85,33 @@ const extractUpdatedJobLines = (addsChgs = {}, jobId) => {
unq_seq: parseInt(line.UniqueSequenceNum || 0, 10),
status: line.LineStatusCode || null,
line_desc: line.LineDesc || null,
notes: line.LineMemo || null,
// notes will be set below
manual_line: line.ManualLineInd !== undefined ? coerceManual(line.ManualLineInd) : null
};
const lineOut = { ...base };
// --- Notes merge logic ---
const unqSeq = lineOut.unq_seq;
const currentNotes = currentJobLineNotes?.[unqSeq] || null;
const newNotes = line.LineMemo || null;
if (newNotes && currentNotes) {
if (currentNotes === newNotes) {
lineOut.notes = currentNotes;
} else if (currentNotes.includes(newNotes)) {
lineOut.notes = currentNotes;
} else {
lineOut.notes = `${currentNotes} | ${newNotes}`;
}
} else if (newNotes) {
lineOut.notes = newNotes;
} else if (currentNotes) {
lineOut.notes = currentNotes;
} else {
lineOut.notes = null;
}
// --- End notes merge logic ---
const hasPart = Object.keys(partInfo).length > 0;
const hasSublet = Object.keys(subletInfo).length > 0;
@@ -198,17 +221,30 @@ const partsManagementVehicleDamageEstimateChgRq = async (req, res) => {
const job = await findJob(shopId, claimNum, logger);
if (!job) return res.status(404).send("Job not found");
// --- Get updated lines and their unq_seq ---
const linesIn = Array.isArray(rq.AddsChgs?.DamageLineInfo)
? rq.AddsChgs.DamageLineInfo
: [rq.AddsChgs?.DamageLineInfo || {}];
const updatedSeqs = Array.from(
new Set((linesIn || []).map((l) => parseInt(l?.UniqueSequenceNum || 0, 10)).filter((v) => Number.isInteger(v)))
);
let currentJobLineNotes = {};
if (updatedSeqs.length > 0) {
const resp = await client.request(GET_JOBLINES_NOTES_BY_JOBID_UNQSEQ, { jobid: job.id, unqSeqs: updatedSeqs });
if (resp?.joblines) {
for (const jl of resp.joblines) {
currentJobLineNotes[jl.unq_seq] = jl.notes;
}
}
}
// --- End fetch current notes ---
const updatedJobData = extractUpdatedJobData(rq);
const updatedLines = extractUpdatedJobLines(rq.AddsChgs, job.id);
const updatedLines = extractUpdatedJobLines(rq.AddsChgs, job.id, currentJobLineNotes);
const deletedLineIds = extractDeletions(rq.Deletions);
await client.request(UPDATE_JOB_BY_ID, { id: job.id, job: updatedJobData });
// Build a set of unq_seq that will be updated (replaced). We delete them first to avoid duplicates.
const updatedSeqs = Array.from(
new Set((updatedLines || []).map((l) => l?.unq_seq).filter((v) => Number.isInteger(v)))
);
if (deletedLineIds?.length || updatedSeqs?.length) {
const allToDelete = Array.from(new Set([...(deletedLineIds || []), ...(updatedSeqs || [])]));
if (allToDelete.length) {

View File

@@ -52,56 +52,6 @@ const UPDATE_JOB_BY_ID = `
}
`;
const UPSERT_JOBLINES = `
mutation UpsertJoblines($joblines: [joblines_insert_input!]!) {
insert_joblines(
objects: $joblines
on_conflict: {
constraint: joblines_pkey
update_columns: [
jobid
status
line_desc
part_type
part_qty
oem_partno
db_price
act_price
mod_lbr_ty
mod_lb_hrs
lbr_op
lbr_amt
notes
manual_line
]
}
) {
affected_rows
}
}
`;
const DELETE_JOBLINES_BY_JOBID = `
mutation DeleteJoblinesByJobId($jobid: uuid!) {
delete_joblines(where: { jobid: { _eq: $jobid } }) {
affected_rows
}
}
`;
const DELETE_JOBLINES_BY_IDS = `
mutation DeleteJoblinesByIds($jobid: uuid!, $unqSeqs: [Int!]!) {
delete_joblines(
where: {
jobid: { _eq: $jobid },
unq_seq: { _in: $unqSeqs }
}
) {
affected_rows
}
}
`;
// Soft delete joblines by marking removed=true instead of hard-deleting
const SOFT_DELETE_JOBLINES_BY_IDS = `
mutation SoftDeleteJoblinesByIds($jobid: uuid!, $unqSeqs: [Int!]!) {
@@ -245,6 +195,15 @@ const DELETE_AUDIT_TRAIL_BY_SHOP = `
}
`;
const GET_JOBLINES_NOTES_BY_JOBID_UNQSEQ = `
query GetJoblinesNotesByJobIdUnqSeq($jobid: uuid!, $unqSeqs: [Int!]!) {
joblines(where: { jobid: { _eq: $jobid }, unq_seq: { _in: $unqSeqs }, removed: { _neq: true } }) {
unq_seq
notes
}
}
`;
module.exports = {
GET_BODYSHOP_STATUS,
GET_VEHICLE_BY_SHOP_VIN,
@@ -252,9 +211,6 @@ module.exports = {
INSERT_JOB_WITH_LINES,
GET_JOB_BY_CLAIM,
UPDATE_JOB_BY_ID,
UPSERT_JOBLINES,
DELETE_JOBLINES_BY_JOBID,
DELETE_JOBLINES_BY_IDS,
SOFT_DELETE_JOBLINES_BY_IDS,
INSERT_JOBLINES,
CHECK_EXTERNAL_SHOP_ID,
@@ -271,5 +227,6 @@ module.exports = {
GET_JOBS_BY_SHOP,
DELETE_JOBLINES_BY_JOB_IDS,
DELETE_JOBS_BY_IDS,
DELETE_AUDIT_TRAIL_BY_SHOP
DELETE_AUDIT_TRAIL_BY_SHOP,
GET_JOBLINES_NOTES_BY_JOBID_UNQSEQ
};