hotfix/2026-02-27 - Reynolds Estimate amounts on second call, + RR Docs in _ref
This commit is contained in:
@@ -52,6 +52,122 @@ const asN2 = (dineroLike) => {
|
||||
return amount.toFixed(2);
|
||||
};
|
||||
|
||||
/**
|
||||
* Normalize various "money-like" shapes to integer cents.
|
||||
* Supports:
|
||||
* - Dinero instances (getAmount / toUnit)
|
||||
* - { cents }
|
||||
* - { amount, precision }
|
||||
* - plain numbers (treated as units, e.g. dollars)
|
||||
* - numeric strings (treated as units, e.g. "123.45")
|
||||
* @param value
|
||||
* @returns {number}
|
||||
*/
|
||||
const toMoneyCents = (value) => {
|
||||
if (value == null || value === "") return 0;
|
||||
|
||||
if (typeof value.getAmount === "function") {
|
||||
return value.getAmount();
|
||||
}
|
||||
|
||||
if (typeof value.toUnit === "function") {
|
||||
const unit = value.toUnit();
|
||||
return Number.isFinite(unit) ? Math.round(unit * 100) : 0;
|
||||
}
|
||||
|
||||
if (typeof value.cents === "number") {
|
||||
return value.cents;
|
||||
}
|
||||
|
||||
if (typeof value.amount === "number") {
|
||||
const precision = typeof value.precision === "number" ? value.precision : 2;
|
||||
if (precision === 2) return value.amount;
|
||||
const factor = Math.pow(10, 2 - precision);
|
||||
return Math.round(value.amount * factor);
|
||||
}
|
||||
|
||||
if (typeof value === "number") {
|
||||
return Math.round(value * 100);
|
||||
}
|
||||
|
||||
if (typeof value === "string") {
|
||||
const parsed = Number.parseFloat(value);
|
||||
return Number.isFinite(parsed) ? Math.round(parsed * 100) : 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
};
|
||||
|
||||
const asN2FromCents = (cents) => asN2({ amount: Number.isFinite(cents) ? cents : 0, precision: 2 });
|
||||
|
||||
/**
|
||||
* Build RR estimate block from allocation totals.
|
||||
* @param {Array} allocations
|
||||
* @returns {{parts: string, labor: string, total: string}|null}
|
||||
*/
|
||||
const buildEstimateFromAllocations = (allocations) => {
|
||||
if (!Array.isArray(allocations) || allocations.length === 0) return null;
|
||||
|
||||
const totals = allocations.reduce(
|
||||
(acc, alloc) => {
|
||||
acc.parts += toMoneyCents(alloc?.partsSale);
|
||||
acc.labor += toMoneyCents(alloc?.laborTaxableSale);
|
||||
acc.labor += toMoneyCents(alloc?.laborNonTaxableSale);
|
||||
acc.total += toMoneyCents(alloc?.totalSale);
|
||||
return acc;
|
||||
},
|
||||
{ parts: 0, labor: 0, total: 0 }
|
||||
);
|
||||
|
||||
// If totalSale wasn't provided, keep total coherent with parts + labor.
|
||||
if (!totals.total) {
|
||||
totals.total = totals.parts + totals.labor;
|
||||
}
|
||||
|
||||
return {
|
||||
parts: asN2FromCents(totals.parts),
|
||||
labor: asN2FromCents(totals.labor),
|
||||
total: asN2FromCents(totals.total)
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Build RR estimate block from precomputed job totals.
|
||||
* @param job
|
||||
* @returns {{parts: string, labor: string, total: string}|null}
|
||||
*/
|
||||
const buildEstimateFromJobTotals = (job) => {
|
||||
const totals = job?.job_totals;
|
||||
if (!totals) return null;
|
||||
|
||||
const partsCents = toMoneyCents(totals?.parts?.parts?.total) + toMoneyCents(totals?.parts?.sublets?.total);
|
||||
const laborCents = toMoneyCents(totals?.rates?.rates_subtotal ?? totals?.rates?.subtotal);
|
||||
let totalCents = toMoneyCents(totals?.totals?.subtotal);
|
||||
|
||||
if (!totalCents) {
|
||||
totalCents = partsCents + laborCents;
|
||||
}
|
||||
|
||||
// If we truly have no numbers from totals, omit estimate entirely.
|
||||
if (!partsCents && !laborCents && !totalCents) return null;
|
||||
|
||||
return {
|
||||
parts: asN2FromCents(partsCents),
|
||||
labor: asN2FromCents(laborCents),
|
||||
total: asN2FromCents(totalCents)
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Build RR estimate block from the best available source.
|
||||
* @param job
|
||||
* @param allocations
|
||||
* @returns {{parts: string, labor: string, total: string}|null}
|
||||
*/
|
||||
const buildRREstimate = ({ job, allocations } = {}) => {
|
||||
return buildEstimateFromAllocations(allocations) || buildEstimateFromJobTotals(job);
|
||||
};
|
||||
|
||||
/**
|
||||
* Build RO.GOG structure for the reynolds-rome-client `createRepairOrder` payload
|
||||
* from allocations.
|
||||
@@ -103,44 +219,6 @@ const buildRogogFromAllocations = (allocations, { opCode, payType = "Cust", roNo
|
||||
|
||||
const ops = [];
|
||||
|
||||
/**
|
||||
* Normalize various "money-like" shapes to integer cents.
|
||||
* Supports:
|
||||
* - Dinero instances (getAmount / toUnit)
|
||||
* - { cents }
|
||||
* - { amount, precision }
|
||||
* - plain numbers (treated as units, e.g. dollars)
|
||||
*/
|
||||
const toCents = (value) => {
|
||||
if (!value) return 0;
|
||||
|
||||
if (typeof value.getAmount === "function") {
|
||||
return value.getAmount();
|
||||
}
|
||||
|
||||
if (typeof value.toUnit === "function") {
|
||||
const unit = value.toUnit();
|
||||
return Number.isFinite(unit) ? Math.round(unit * 100) : 0;
|
||||
}
|
||||
|
||||
if (typeof value.cents === "number") {
|
||||
return value.cents;
|
||||
}
|
||||
|
||||
if (typeof value.amount === "number") {
|
||||
const precision = typeof value.precision === "number" ? value.precision : 2;
|
||||
if (precision === 2) return value.amount;
|
||||
const factor = Math.pow(10, 2 - precision);
|
||||
return Math.round(value.amount * factor);
|
||||
}
|
||||
|
||||
if (typeof value === "number") {
|
||||
return Math.round(value * 100);
|
||||
}
|
||||
|
||||
return 0;
|
||||
};
|
||||
|
||||
const asMoneyLike = (amountCents) => ({
|
||||
amount: amountCents || 0,
|
||||
precision: 2
|
||||
@@ -154,13 +232,13 @@ const buildRogogFromAllocations = (allocations, { opCode, payType = "Cust", roNo
|
||||
// Only centers configured for RR GOG are included
|
||||
if (!breakOut || !itemType) continue;
|
||||
|
||||
const partsTaxableCents = toCents(alloc.partsTaxableSale);
|
||||
const partsNonTaxableCents = toCents(alloc.partsNonTaxableSale);
|
||||
const extrasTaxableCents = toCents(alloc.extrasTaxableSale);
|
||||
const extrasNonTaxableCents = toCents(alloc.extrasNonTaxableSale);
|
||||
const laborTaxableCents = toCents(alloc.laborTaxableSale);
|
||||
const laborNonTaxableCents = toCents(alloc.laborNonTaxableSale);
|
||||
const costCents = toCents(alloc.cost);
|
||||
const partsTaxableCents = toMoneyCents(alloc.partsTaxableSale);
|
||||
const partsNonTaxableCents = toMoneyCents(alloc.partsNonTaxableSale);
|
||||
const extrasTaxableCents = toMoneyCents(alloc.extrasTaxableSale);
|
||||
const extrasNonTaxableCents = toMoneyCents(alloc.extrasNonTaxableSale);
|
||||
const laborTaxableCents = toMoneyCents(alloc.laborTaxableSale);
|
||||
const laborNonTaxableCents = toMoneyCents(alloc.laborNonTaxableSale);
|
||||
const costCents = toMoneyCents(alloc.cost);
|
||||
|
||||
const segments = [];
|
||||
|
||||
@@ -418,6 +496,11 @@ const buildRRRepairOrderPayload = ({
|
||||
mileageIn: job.kmin
|
||||
};
|
||||
|
||||
const estimate = buildRREstimate({ job, allocations });
|
||||
if (estimate) {
|
||||
payload.estimate = estimate;
|
||||
}
|
||||
|
||||
if (story) {
|
||||
payload.roComment = String(story).trim();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user