Merged in feature/IO-3672-Reynolds-Adjustments-V3 (pull request #3219)
feature/IO-3672-Reynolds-Adjustments-V3 - Expand Export logs for Reynolds
This commit is contained in:
@@ -88,6 +88,15 @@ describe("server/rr/rr-export-logs", () => {
|
||||
statusCode: "0",
|
||||
message: "Finalized"
|
||||
}
|
||||
},
|
||||
metaExtra: {
|
||||
rrPreview: {
|
||||
provider: "rr",
|
||||
previewFormat: "rr-rogog-preview.v1",
|
||||
rogg: {
|
||||
rows: [{ jobNo: "1", opCode: "BODY", custPrice: "125.00", dlrCost: "50.00" }]
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -106,7 +115,17 @@ describe("server/rr/rr-export-logs", () => {
|
||||
bodyshopid: "bodyshop-1",
|
||||
jobid: "job-1",
|
||||
successful: true,
|
||||
useremail: "tech@example.com"
|
||||
useremail: "tech@example.com",
|
||||
metadata: {
|
||||
provider: "rr",
|
||||
rrPreview: {
|
||||
provider: "rr",
|
||||
previewFormat: "rr-rogog-preview.v1",
|
||||
rogg: {
|
||||
rows: [{ jobNo: "1", opCode: "BODY", custPrice: "125.00", dlrCost: "50.00" }]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
bill: {
|
||||
exported: true,
|
||||
|
||||
@@ -2,6 +2,7 @@ const { buildRRRepairOrderPayload, buildMinimalRolaborFromJob } = require("./rr-
|
||||
const { buildClientAndOpts } = require("./rr-lookup");
|
||||
const CreateRRLogEvent = require("./rr-logger-event");
|
||||
const { withRRRequestXml } = require("./rr-log-xml");
|
||||
const { buildRRPreviewMetadata } = require("./rr-preview-metadata");
|
||||
const { extractRrResponsibilityCenters } = require("./rr-responsibility-centers");
|
||||
const CdkCalculateAllocations = require("./rr-calculate-allocations").default;
|
||||
const { isEnhancedEarlyROEnabled, resolveRROpCodeFromBodyshop } = require("./rr-utils");
|
||||
@@ -392,7 +393,7 @@ const updateRRRepairOrderWithFullData = async (args) => {
|
||||
success = String(roStatus.status).toUpperCase() === "SUCCESS";
|
||||
}
|
||||
|
||||
return {
|
||||
const exportResult = {
|
||||
success,
|
||||
data,
|
||||
roStatus,
|
||||
@@ -402,6 +403,8 @@ const updateRRRepairOrderWithFullData = async (args) => {
|
||||
roNo: String(roNo),
|
||||
xml: response?.xml
|
||||
};
|
||||
exportResult.rrPreview = buildRRPreviewMetadata({ payload, result: exportResult });
|
||||
return exportResult;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -533,7 +536,7 @@ const exportJobToRR = async (args) => {
|
||||
// Extract canonical roNo you'll need for finalize step
|
||||
const roNo = data?.dmsRoNo ?? data?.outsdRoNo ?? roStatus?.dmsRoNo ?? null;
|
||||
|
||||
return {
|
||||
const exportResult = {
|
||||
success,
|
||||
data,
|
||||
roStatus,
|
||||
@@ -543,6 +546,8 @@ const exportJobToRR = async (args) => {
|
||||
roNo,
|
||||
xml: response?.xml // expose XML for logging/diagnostics
|
||||
};
|
||||
exportResult.rrPreview = buildRRPreviewMetadata({ payload, result: exportResult });
|
||||
return exportResult;
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
85
server/rr/rr-preview-metadata.js
Normal file
85
server/rr/rr-preview-metadata.js
Normal file
@@ -0,0 +1,85 @@
|
||||
const segmentLabelMap = {
|
||||
partsTaxable: "Parts Taxable",
|
||||
partsNonTaxable: "Parts Non-Taxable",
|
||||
extrasTaxable: "Extras Taxable",
|
||||
extrasNonTaxable: "Extras Non-Taxable",
|
||||
laborTaxable: "Labor Taxable",
|
||||
laborNonTaxable: "Labor Non-Taxable"
|
||||
};
|
||||
|
||||
const toCentsFromAmountString = (value) => {
|
||||
const parsed = Number.parseFloat(value || "0");
|
||||
return Number.isNaN(parsed) ? 0 : Math.round(parsed * 100);
|
||||
};
|
||||
|
||||
const buildRoggRows = (rogg) => {
|
||||
if (!rogg || !Array.isArray(rogg.ops)) return [];
|
||||
|
||||
const rows = [];
|
||||
|
||||
rogg.ops.forEach((op) => {
|
||||
(op.lines || []).forEach((line, idx) => {
|
||||
const segmentKind = op.segmentKind;
|
||||
const segmentCount = op.segmentCount || 0;
|
||||
const segmentLabel = segmentLabelMap[segmentKind] || segmentKind;
|
||||
const itemDesc =
|
||||
segmentCount > 1 && segmentLabel ? `${line.itemDesc} (${segmentLabel})` : line.itemDesc;
|
||||
|
||||
rows.push({
|
||||
key: `${op.jobNo}-${idx}`,
|
||||
opCode: op.opCode,
|
||||
jobNo: op.jobNo,
|
||||
breakOut: line.breakOut,
|
||||
itemType: line.itemType,
|
||||
itemDesc,
|
||||
custQty: line.custQty,
|
||||
custPayTypeFlag: line.custPayTypeFlag,
|
||||
custTxblNtxblFlag: line.custTxblNtxblFlag,
|
||||
custPrice: line.amount?.custPrice,
|
||||
dlrCost: line.amount?.dlrCost,
|
||||
segmentKind,
|
||||
segmentCount
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
return rows;
|
||||
};
|
||||
|
||||
const buildRoggTotals = (roggRows) => {
|
||||
const totals = roggRows.reduce(
|
||||
(acc, row) => {
|
||||
acc.totalCustPriceCents += toCentsFromAmountString(row.custPrice);
|
||||
acc.totalDlrCostCents += toCentsFromAmountString(row.dlrCost);
|
||||
return acc;
|
||||
},
|
||||
{ totalCustPriceCents: 0, totalDlrCostCents: 0 }
|
||||
);
|
||||
|
||||
return {
|
||||
...totals,
|
||||
totalCustPrice: (totals.totalCustPriceCents / 100).toFixed(2),
|
||||
totalDlrCost: (totals.totalDlrCostCents / 100).toFixed(2)
|
||||
};
|
||||
};
|
||||
|
||||
const buildRRPreviewMetadata = ({ payload, result } = {}) => {
|
||||
const rogg = payload?.rogg || null;
|
||||
const roggRows = buildRoggRows(rogg);
|
||||
|
||||
return {
|
||||
provider: "rr",
|
||||
previewFormat: "rr-rogog-preview.v1",
|
||||
roNo: result?.roNo || payload?.roNo || null,
|
||||
outsdRoNo: payload?.outsdRoNo || null,
|
||||
rogg: {
|
||||
raw: rogg,
|
||||
rows: roggRows,
|
||||
totals: buildRoggTotals(roggRows)
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
buildRRPreviewMetadata
|
||||
};
|
||||
68
server/rr/rr-preview-metadata.test.js
Normal file
68
server/rr/rr-preview-metadata.test.js
Normal file
@@ -0,0 +1,68 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { createRequire } from "node:module";
|
||||
|
||||
const require = createRequire(import.meta.url);
|
||||
const { buildRRPreviewMetadata } = require("./rr-preview-metadata");
|
||||
|
||||
describe("server/rr/rr-preview-metadata", () => {
|
||||
it("captures ROGOG preview rows and totals", () => {
|
||||
const metadata = buildRRPreviewMetadata({
|
||||
payload: {
|
||||
outsdRoNo: "RO-100",
|
||||
rogg: {
|
||||
ops: [
|
||||
{
|
||||
opCode: "BODY",
|
||||
jobNo: "1",
|
||||
segmentKind: "laborTaxable",
|
||||
segmentCount: 2,
|
||||
lines: [
|
||||
{
|
||||
breakOut: "B",
|
||||
itemType: "LAB",
|
||||
itemDesc: "Body Labor",
|
||||
custQty: "1.0",
|
||||
custPayTypeFlag: "C",
|
||||
custTxblNtxblFlag: "T",
|
||||
amount: {
|
||||
custPrice: "125.00",
|
||||
dlrCost: "50.00"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
result: { roNo: "12345" }
|
||||
});
|
||||
|
||||
expect(metadata).toMatchObject({
|
||||
provider: "rr",
|
||||
previewFormat: "rr-rogog-preview.v1",
|
||||
roNo: "12345",
|
||||
outsdRoNo: "RO-100",
|
||||
rogg: {
|
||||
rows: [
|
||||
{
|
||||
opCode: "BODY",
|
||||
jobNo: "1",
|
||||
breakOut: "B",
|
||||
itemType: "LAB",
|
||||
itemDesc: "Body Labor (Labor Taxable)",
|
||||
custTxblNtxblFlag: "T",
|
||||
custPrice: "125.00",
|
||||
dlrCost: "50.00"
|
||||
}
|
||||
],
|
||||
totals: {
|
||||
totalCustPriceCents: 12500,
|
||||
totalDlrCostCents: 5000,
|
||||
totalCustPrice: "125.00",
|
||||
totalDlrCost: "50.00"
|
||||
}
|
||||
}
|
||||
});
|
||||
expect(metadata).not.toHaveProperty("rolabor");
|
||||
});
|
||||
});
|
||||
@@ -1497,7 +1497,8 @@ const registerRREvents = ({ socket, redisHelpers }) => {
|
||||
dmsRoNo,
|
||||
customerNo: String(effectiveCustNo),
|
||||
advisorNo: String(advisorNo),
|
||||
vin: job?.v_vin || null
|
||||
vin: job?.v_vin || null,
|
||||
rrPreview: result?.rrPreview || null
|
||||
},
|
||||
defaultRRTTL
|
||||
);
|
||||
@@ -1705,7 +1706,10 @@ const registerRREvents = ({ socket, redisHelpers }) => {
|
||||
jobId: rid,
|
||||
job,
|
||||
bodyshop,
|
||||
result: finalizeResult
|
||||
result: finalizeResult,
|
||||
metaExtra: {
|
||||
rrPreview: pending?.rrPreview || null
|
||||
}
|
||||
});
|
||||
|
||||
// Clean pending key
|
||||
|
||||
Reference in New Issue
Block a user