feature/IO-3672-Reynolds-Adjustments-V3 - Expand Export logs for Reynolds

This commit is contained in:
Dave
2026-05-05 13:31:03 -04:00
parent c8262da440
commit e6178a613d
5 changed files with 186 additions and 5 deletions

View File

@@ -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,

View File

@@ -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 { resolveRROpCodeFromBodyshop } = require("./rr-utils");
@@ -383,7 +384,7 @@ const updateRRRepairOrderWithFullData = async (args) => {
success = String(roStatus.status).toUpperCase() === "SUCCESS";
}
return {
const exportResult = {
success,
data,
roStatus,
@@ -393,6 +394,8 @@ const updateRRRepairOrderWithFullData = async (args) => {
roNo: String(roNo),
xml: response?.xml
};
exportResult.rrPreview = buildRRPreviewMetadata({ payload, result: exportResult });
return exportResult;
};
/**
@@ -524,7 +527,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,
@@ -534,6 +537,8 @@ const exportJobToRR = async (args) => {
roNo,
xml: response?.xml // expose XML for logging/diagnostics
};
exportResult.rrPreview = buildRRPreviewMetadata({ payload, result: exportResult });
return exportResult;
};
/**

View 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
};

View 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");
});
});

View File

@@ -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