diff --git a/server/integrations/partsManagement/endpoints/vehicleDamageEstimateChgRq.js b/server/integrations/partsManagement/endpoints/vehicleDamageEstimateChgRq.js
index f2aafbd8d..947e0f3c7 100644
--- a/server/integrations/partsManagement/endpoints/vehicleDamageEstimateChgRq.js
+++ b/server/integrations/partsManagement/endpoints/vehicleDamageEstimateChgRq.js
@@ -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) {
diff --git a/server/integrations/partsManagement/partsManagement.queries.js b/server/integrations/partsManagement/partsManagement.queries.js
index 3e682c4f8..e4baf8f28 100644
--- a/server/integrations/partsManagement/partsManagement.queries.js
+++ b/server/integrations/partsManagement/partsManagement.queries.js
@@ -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
};