RrScratch3 - Tax / Extras Improvements

This commit is contained in:
Dave
2025-12-05 13:17:25 -05:00
parent 56738f800c
commit 288c8e6347
3 changed files with 377 additions and 80 deletions

View File

@@ -63,8 +63,10 @@ function emptyCenterBucket() {
laborTaxableSale: zero, // labor that should be taxed in RR
laborNonTaxableSale: zero, // labor that should NOT be taxed in RR
// Extras
extrasSale: zero // MAPA/MASH/towing/storage/PAO/etc
// Extras (MAPA/MASH/towing/PAO/etc)
extrasSale: zero, // total extras (taxable + non-taxable)
extrasTaxableSale: zero,
extrasNonTaxableSale: zero
};
}
@@ -162,26 +164,228 @@ function buildTaxAllocations(bodyshop, job) {
}
/**
* Decide if a labor line is taxable vs non-taxable for RR.
* ============================
* Tax Context & Helpers
* ============================
*/
function isLaborTaxable(line) {
return line.tax_part;
/**
* Build a small "tax context" object from the job + current instance.
*
* This centralises all of the "is this category taxable?" logic so that
* the rest of the allocation code just asks simple yes/no questions.
*
* IMPORTANT: we are **not** calculating any tax **amounts** here that is
* still handled by job-costing. We only need to know if a given sale bucket
* should be treated as taxable vs non-taxable for RR (CustTxblNTxblFlag).
*/
function buildTaxContext(job = {}) {
const isImex = !!InstanceManager({ imex: true }); // Canada
const isRome = !!InstanceManager({ rome: true }); // US
const toNumber = (v) => (v == null ? 0 : Number(v) || 0);
const federalTaxRate = toNumber(job.federal_tax_rate);
const stateTaxRate = toNumber(job.state_tax_rate);
const localTaxRate = toNumber(job.local_tax_rate);
const hasFederalRate = federalTaxRate > 0;
const hasState = stateTaxRate > 0;
const hasLocal = localTaxRate > 0;
// "hasFederal" kept for backwards compatibility / logging (Canada only)
const hasFederal = isImex && hasFederalRate;
// Canada: if ANY of federal / state / local > 0, treat the job as
// "everything taxable by default", then let line-level flags override
// for parts where applicable.
const globalAllTaxCanada = isImex && (hasFederalRate || hasState || hasLocal);
const hasAnySalesTax = hasFederalRate || hasState || hasLocal;
// Parts tax rate map (PAA/PAC/…)
let partTaxRates = job.part_tax_rates || job.parts_tax_rates || {};
if (typeof partTaxRates === "string") {
try {
partTaxRates = JSON.parse(partTaxRates);
} catch {
partTaxRates = {};
}
}
if (!partTaxRates || typeof partTaxRates !== "object") {
partTaxRates = {};
}
const tax_lbr_rt = toNumber(job.tax_lbr_rt); // labour
const tax_paint_mat_rt = toNumber(job.tax_paint_mat_rt || job.tax_paint_mt_rate); // MAPA
const tax_shop_mat_rt = toNumber(job.tax_shop_mat_rt); // MASH
const tax_tow_rt = toNumber(job.tax_tow_rt); // towing
const tax_sub_rt = toNumber(job.tax_sub_rt); // sublet (rarely used directly)
const hasAnyPartsWithTax = Object.values(partTaxRates).some(
(entry) => entry && entry.prt_tax_in && toNumber(entry.prt_tax_rt) > 0
);
const hasAnyTax =
hasAnySalesTax ||
tax_lbr_rt > 0 ||
tax_paint_mat_rt > 0 ||
tax_shop_mat_rt > 0 ||
tax_tow_rt > 0 ||
tax_sub_rt > 0 ||
hasAnyPartsWithTax;
return {
isImex,
isRome,
federalTaxRate,
stateTaxRate,
localTaxRate,
hasFederal,
hasState,
hasLocal,
hasAnySalesTax,
globalAllTaxCanada,
partTaxRates,
tax_lbr_rt,
tax_paint_mat_rt,
tax_shop_mat_rt,
tax_tow_rt,
tax_sub_rt,
hasAnyPartsWithTax,
hasAnyTax
};
}
/**
* Resolve the "PA" / part-type code (PAA/PAC/…) from a job line.
*/
function resolvePartType(line = {}) {
return line.part_type || line.partType || line.pa_code || line.pa || null;
}
/**
* Decide if a *part* line is taxable vs non-taxable for RR.
* For now we mirror the same flag; this can be extended with CAD-specific
* logic (federal_tax_rate, parts_tax_rate, prt_tax_in, etc.) later.
*
* Rules:
* - Canada (IMEX):
* - If ANY of federal_tax_rate / state_tax_rate / local_tax_rate > 0
* => everything is taxable by default (globalAllTaxCanada),
* unless tax_part is explicitly false.
* - Otherwise, use part_tax_rates[part_type] (prt_tax_in && prt_tax_rt > 0),
* with tax_part as final override.
* - US (ROME):
* - Use part_tax_rates[part_type] (prt_tax_in && prt_tax_rt > 0),
* with tax_part as final override.
*
* - line.tax_part is treated as the *final* check:
* - tax_part === false => always non-taxable.
* - tax_part === true => always taxable, even if we have no table entry.
*/
function isPartTaxable(line) {
return line.tax_part;
function isPartTaxable(line = {}, taxCtx) {
if (!taxCtx) return !!line.tax_part;
const { globalAllTaxCanada, partTaxRates } = taxCtx;
// Explicit per-line override to *not* tax.
if (typeof line.tax_part === "boolean" && line.tax_part === false) {
return false;
}
// Canada: any federal/state/local tax rate set => all parts taxable,
// unless explicitly turned off above.
if (globalAllTaxCanada) {
return true;
}
let taxable = false;
const partType = resolvePartType(line);
if (partType && partTaxRates && partTaxRates[partType]) {
const entry = partTaxRates[partType];
const rate = Number(entry?.prt_tax_rt || 0);
const indicator = !!entry?.prt_tax_in;
taxable = indicator && rate > 0;
}
// tax_part === true is treated as "final yes" even if we didn't find
// a matching part_tax_rate entry.
if (typeof line.tax_part === "boolean" && line.tax_part === true) {
taxable = true;
}
return taxable;
}
/**
* Decide if *labour* for this job is taxable.
*
* - Canada (IMEX):
* - If ANY of federal_tax_rate / state_tax_rate / local_tax_rate > 0
* (globalAllTaxCanada) => all labour is taxable.
* - Else if tax_lbr_rt > 0 => labour taxable.
* - Else => non-taxable.
* - US (ROME):
* - tax_lbr_rt > 0 => labour taxable, otherwise not.
*/
function isLaborTaxable(_line, taxCtx) {
if (!taxCtx) return false;
const { isImex, globalAllTaxCanada, tax_lbr_rt } = taxCtx;
if (isImex && globalAllTaxCanada) return true;
return tax_lbr_rt > 0;
}
/**
* Taxability helpers for "extras" buckets.
* These are all job-level decisions; there are no per-line flags for them
* in the data we currently work with.
*
* Canada: if globalAllTaxCanada is true, we treat these as taxable.
*/
function isMapaTaxable(taxCtx) {
if (!taxCtx) return false;
const { isImex, globalAllTaxCanada, tax_paint_mat_rt } = taxCtx;
if (isImex && globalAllTaxCanada) return true;
return tax_paint_mat_rt > 0;
}
function isMashTaxable(taxCtx) {
if (!taxCtx) return false;
const { isImex, globalAllTaxCanada, tax_shop_mat_rt } = taxCtx;
if (isImex && globalAllTaxCanada) return true;
return tax_shop_mat_rt > 0;
}
function isTowTaxable(taxCtx) {
if (!taxCtx) return false;
const { isImex, globalAllTaxCanada, tax_tow_rt } = taxCtx;
if (isImex && globalAllTaxCanada) return true;
return tax_tow_rt > 0;
}
/**
* Helper to push an "extra" (MAPA/MASH/towing/PAO/etc) amount into the
* appropriate taxable / non-taxable buckets for a given center.
*/
function addExtras(bucket, dineroAmount, isTaxable) {
if (!bucket || !dineroAmount || typeof dineroAmount.add !== "function") return;
bucket.extrasSale = bucket.extrasSale.add(dineroAmount);
if (isTaxable) {
bucket.extrasTaxableSale = bucket.extrasTaxableSale.add(dineroAmount);
} else {
bucket.extrasNonTaxableSale = bucket.extrasNonTaxableSale.add(dineroAmount);
}
}
/**
* Build profitCenterHash from joblines (parts + labor) and detect MAPA/MASH presence.
* Now stores *buckets* instead of a single Dinero per center.
*/
function buildProfitCenterHash(job, debugLog) {
function buildProfitCenterHash(job, debugLog, taxContext) {
let hasMapaLine = false;
let hasMashLine = false;
@@ -231,7 +435,7 @@ function buildProfitCenterHash(job, debugLog) {
amount = amount.add(discount);
}
const taxable = isPartTaxable(val);
const taxable = isPartTaxable(val, taxContext);
if (taxable) {
bucket.partsTaxableSale = bucket.partsTaxableSale.add(amount);
@@ -254,7 +458,7 @@ function buildProfitCenterHash(job, debugLog) {
amount: Math.round(rate * 100)
}).multiply(val.mod_lb_hrs);
if (isLaborTaxable(val)) {
if (isLaborTaxable(val, taxContext)) {
bucket.laborTaxableSale = bucket.laborTaxableSale.add(laborAmount);
} else {
bucket.laborNonTaxableSale = bucket.laborNonTaxableSale.add(laborAmount);
@@ -274,7 +478,9 @@ function buildProfitCenterHash(job, debugLog) {
partsNonTaxable: summarizeMoney(b.partsNonTaxableSale),
laborTaxable: summarizeMoney(b.laborTaxableSale),
laborNonTaxable: summarizeMoney(b.laborNonTaxableSale),
extras: summarizeMoney(b.extrasSale)
extras: summarizeMoney(b.extrasSale),
extrasTaxable: summarizeMoney(b.extrasTaxableSale),
extrasNonTaxable: summarizeMoney(b.extrasNonTaxableSale)
}))
});
@@ -353,39 +559,48 @@ function applyMapaMashManualLines({
profitCenterHash,
hasMapaLine,
hasMashLine,
debugLog
debugLog,
taxContext
}) {
// MAPA
// MAPA (paint materials)
if (!hasMapaLine && job.job_totals.rates.mapa.total.amount > 0) {
const mapaAccountName = selectedDmsAllocationConfig.profits.MAPA;
const mapaAccount = bodyshop.md_responsibility_centers.profits.find((c) => c.name === mapaAccountName);
if (mapaAccount) {
const amount = Dinero(job.job_totals.rates.mapa.total);
const taxable = isMapaTaxable(taxContext);
debugLog("Adding MAPA Line Manually", {
mapaAccountName,
amount: summarizeMoney(Dinero(job.job_totals.rates.mapa.total))
amount: summarizeMoney(amount),
taxable
});
const bucket = ensureCenterBucket(profitCenterHash, mapaAccountName);
bucket.extrasSale = bucket.extrasSale.add(Dinero(job.job_totals.rates.mapa.total));
addExtras(bucket, amount, taxable);
} else {
debugLog("NO MAPA ACCOUNT FOUND!!", { mapaAccountName });
}
}
// MASH
// MASH (shop materials)
if (!hasMashLine && job.job_totals.rates.mash.total.amount > 0) {
const mashAccountName = selectedDmsAllocationConfig.profits.MASH;
const mashAccount = bodyshop.md_responsibility_centers.profits.find((c) => c.name === mashAccountName);
if (mashAccount) {
const amount = Dinero(job.job_totals.rates.mash.total);
const taxable = isMashTaxable(taxContext);
debugLog("Adding MASH Line Manually", {
mashAccountName,
amount: summarizeMoney(Dinero(job.job_totals.rates.mash.total))
amount: summarizeMoney(amount),
taxable
});
const bucket = ensureCenterBucket(profitCenterHash, mashAccountName);
bucket.extrasSale = bucket.extrasSale.add(Dinero(job.job_totals.rates.mash.total));
addExtras(bucket, amount, taxable);
} else {
debugLog("NO MASH ACCOUNT FOUND!!", { mashAccountName });
}
@@ -467,9 +682,17 @@ function applyMaterialsCosting({ job, bodyshop, selectedDmsAllocationConfig, cos
/**
* Apply non-tax extras (PVRT, towing, storage, PAO).
* Extras go into the extrasSale bucket.
* Extras go into the extrasSale bucket (split taxable / non-taxable).
*/
function applyExtras({ job, bodyshop, selectedDmsAllocationConfig, profitCenterHash, taxAllocations, debugLog }) {
function applyExtras({
job,
bodyshop,
selectedDmsAllocationConfig,
profitCenterHash,
taxAllocations,
debugLog,
taxContext
}) {
const { ca_bc_pvrt } = job;
// BC PVRT -> state tax
@@ -485,17 +708,19 @@ function applyExtras({ job, bodyshop, selectedDmsAllocationConfig, profitCenterH
const towAccount = bodyshop.md_responsibility_centers.profits.find((c) => c.name === towAccountName);
if (towAccount) {
const amount = Dinero({
amount: Math.round((job.towing_payable || 0) * 100)
});
const taxable = isTowTaxable(taxContext);
debugLog("Adding towing_payable to TOW account", {
towAccountName,
towing_payable: job.towing_payable
towing_payable: job.towing_payable,
taxable
});
const bucket = ensureCenterBucket(profitCenterHash, towAccountName);
bucket.extrasSale = bucket.extrasSale.add(
Dinero({
amount: Math.round((job.towing_payable || 0) * 100)
})
);
addExtras(bucket, amount, taxable);
} else {
debugLog("NO TOW ACCOUNT FOUND!!", { towAccountName });
}
@@ -507,17 +732,19 @@ function applyExtras({ job, bodyshop, selectedDmsAllocationConfig, profitCenterH
const towAccount = bodyshop.md_responsibility_centers.profits.find((c) => c.name === storageAccountName);
if (towAccount) {
const amount = Dinero({
amount: Math.round((job.storage_payable || 0) * 100)
});
const taxable = isTowTaxable(taxContext);
debugLog("Adding storage_payable to TOW account", {
storageAccountName,
storage_payable: job.storage_payable
storage_payable: job.storage_payable,
taxable
});
const bucket = ensureCenterBucket(profitCenterHash, storageAccountName);
bucket.extrasSale = bucket.extrasSale.add(
Dinero({
amount: Math.round((job.storage_payable || 0) * 100)
})
);
addExtras(bucket, amount, taxable);
} else {
debugLog("NO STORAGE/TOW ACCOUNT FOUND!!", { storageAccountName });
}
@@ -529,17 +756,19 @@ function applyExtras({ job, bodyshop, selectedDmsAllocationConfig, profitCenterH
const otherAccount = bodyshop.md_responsibility_centers.profits.find((c) => c.name === otherAccountName);
if (otherAccount) {
const amount = Dinero({
amount: Math.round((job.adjustment_bottom_line || 0) * 100)
});
const taxable = !!(taxContext && taxContext.hasAnyTax);
debugLog("Adding adjustment_bottom_line to PAO", {
otherAccountName,
adjustment_bottom_line: job.adjustment_bottom_line
adjustment_bottom_line: job.adjustment_bottom_line,
taxable
});
const bucket = ensureCenterBucket(profitCenterHash, otherAccountName);
bucket.extrasSale = bucket.extrasSale.add(
Dinero({
amount: Math.round((job.adjustment_bottom_line || 0) * 100)
})
);
addExtras(bucket, amount, taxable);
} else {
debugLog("NO PAO ACCOUNT FOUND!!", { otherAccountName });
}
@@ -558,7 +787,8 @@ function applyRomeProfileAdjustments({
selectedDmsAllocationConfig,
profitCenterHash,
debugLog,
connectionData
connectionData,
taxContext
}) {
// Only relevant for Rome instances
if (!InstanceManager({ rome: true })) return profitCenterHash;
@@ -576,6 +806,8 @@ function applyRomeProfileAdjustments({
rateKeys: Object.keys(rateMap)
});
const extrasTaxable = !!(taxContext && taxContext.hasAnyTax);
// Parts adjustments
Object.keys(partsAdjustments).forEach((key) => {
const accountName = selectedDmsAllocationConfig.profits[key];
@@ -585,12 +817,13 @@ function applyRomeProfileAdjustments({
const bucket = ensureCenterBucket(profitCenterHash, accountName);
const adjMoney = Dinero(partsAdjustments[key]);
bucket.extrasSale = bucket.extrasSale.add(adjMoney);
addExtras(bucket, adjMoney, extrasTaxable);
debugLog("Added parts adjustment", {
key,
accountName,
adjustment: summarizeMoney(adjMoney)
adjustment: summarizeMoney(adjMoney),
taxable: extrasTaxable
});
} else {
CreateRRLogEvent(
@@ -619,12 +852,13 @@ function applyRomeProfileAdjustments({
// Note: we intentionally use `rate.adjustments` here to mirror CDK behaviour
const adjMoney = Dinero(rate.adjustments);
bucket.extrasSale = bucket.extrasSale.add(adjMoney);
addExtras(bucket, adjMoney, extrasTaxable);
debugLog("Added rate adjustment", {
key,
accountName,
adjustment: summarizeMoney(adjMoney)
adjustment: summarizeMoney(adjMoney),
taxable: extrasTaxable
});
} else {
CreateRRLogEvent(
@@ -651,6 +885,8 @@ function applyRomeProfileAdjustments({
* laborTaxableSale,
* laborNonTaxableSale,
* extrasSale,
* extrasTaxableSale,
* extrasNonTaxableSale,
* totalSale,
* cost,
* profitCenter,
@@ -663,10 +899,8 @@ function buildJobAllocations(bodyshop, profitCenterHash, costCenterHash, debugLo
const jobAllocations = centers.map((center) => {
const bucket = profitCenterHash[center] || emptyCenterBucket();
const totalSale = bucket.partsSale
.add(bucket.laborTaxableSale)
.add(bucket.laborNonTaxableSale)
.add(bucket.extrasSale);
const extrasSale = bucket.extrasSale;
const totalSale = bucket.partsSale.add(bucket.laborTaxableSale).add(bucket.laborNonTaxableSale).add(extrasSale);
const profitCenter = bodyshop.md_responsibility_centers.profits.find((c) => c.name === center);
const costCenter = bodyshop.md_responsibility_centers.costs.find((c) => c.name === center);
@@ -684,7 +918,9 @@ function buildJobAllocations(bodyshop, profitCenterHash, costCenterHash, debugLo
laborNonTaxableSale: bucket.laborNonTaxableSale,
// Extras
extrasSale: bucket.extrasSale,
extrasSale,
extrasTaxableSale: bucket.extrasTaxableSale,
extrasNonTaxableSale: bucket.extrasNonTaxableSale,
totalSale,
@@ -705,6 +941,8 @@ function buildJobAllocations(bodyshop, profitCenterHash, costCenterHash, debugLo
laborTaxable: summarizeMoney(row.laborTaxableSale),
laborNonTaxable: summarizeMoney(row.laborNonTaxableSale),
extras: summarizeMoney(row.extrasSale),
extrasTaxable: summarizeMoney(row.extrasTaxableSale),
extrasNonTaxable: summarizeMoney(row.extrasNonTaxableSale),
totalSale: summarizeMoney(row.totalSale),
cost: summarizeMoney(row.cost)
}))
@@ -812,12 +1050,35 @@ function calculateAllocations(connectionData, job) {
timetickets: Array.isArray(job.timetickets) ? job.timetickets.length : 0
});
const taxContext = buildTaxContext(job);
debugLog("Tax context initialised", {
isImex: taxContext.isImex,
isRome: taxContext.isRome,
federalTaxRate: taxContext.federalTaxRate,
stateTaxRate: taxContext.stateTaxRate,
localTaxRate: taxContext.localTaxRate,
hasFederal: taxContext.hasFederal,
hasState: taxContext.hasState,
hasLocal: taxContext.hasLocal,
globalAllTaxCanada: taxContext.globalAllTaxCanada,
tax_lbr_rt: taxContext.tax_lbr_rt,
tax_paint_mat_rt: taxContext.tax_paint_mat_rt,
tax_shop_mat_rt: taxContext.tax_shop_mat_rt,
tax_tow_rt: taxContext.tax_tow_rt,
hasAnyPartsWithTax: taxContext.hasAnyPartsWithTax,
hasAnyTax: taxContext.hasAnyTax
});
// 1) Tax allocations
let taxAllocations = buildTaxAllocations(bodyshop, job);
debugLog("Initial taxAllocations", summarizeTaxAllocations(taxAllocations));
// 2) Profit centers from job lines + MAPA/MASH detection
const { profitCenterHash: initialProfitHash, hasMapaLine, hasMashLine } = buildProfitCenterHash(job, debugLog);
const {
profitCenterHash: initialProfitHash,
hasMapaLine,
hasMashLine
} = buildProfitCenterHash(job, debugLog, taxContext);
// 3) DMS allocation config
const selectedDmsAllocationConfig =
@@ -842,7 +1103,8 @@ function calculateAllocations(connectionData, job) {
profitCenterHash: initialProfitHash,
hasMapaLine,
hasMashLine,
debugLog
debugLog,
taxContext
});
// 6) Materials costing (MAPA/MASH cost side)
@@ -861,7 +1123,8 @@ function calculateAllocations(connectionData, job) {
selectedDmsAllocationConfig,
profitCenterHash,
taxAllocations,
debugLog
debugLog,
taxContext
}));
// 8) Rome-only profile-level adjustments
@@ -871,7 +1134,8 @@ function calculateAllocations(connectionData, job) {
selectedDmsAllocationConfig,
profitCenterHash,
debugLog,
connectionData
connectionData,
taxContext
});
debugLog("profitCenterHash before jobAllocations build", {
@@ -882,7 +1146,9 @@ function calculateAllocations(connectionData, job) {
partsNonTaxable: summarizeMoney(b.partsNonTaxableSale),
laborTaxable: summarizeMoney(b.laborTaxableSale),
laborNonTaxable: summarizeMoney(b.laborNonTaxableSale),
extras: summarizeMoney(b.extrasSale)
extras: summarizeMoney(b.extrasSale),
extrasTaxable: summarizeMoney(b.extrasTaxableSale),
extrasNonTaxable: summarizeMoney(b.extrasNonTaxableSale)
}))
});
debugLog("costCenterHash before jobAllocations build", {

View File

@@ -56,7 +56,7 @@ const asN2 = (dineroLike) => {
* Build RO.GOG structure for the reynolds-rome-client `createRepairOrder` payload
* from allocations.
*
* Supports the new allocation shape:
* Supports the allocation shape:
* {
* center,
* partsSale,
@@ -65,18 +65,21 @@ const asN2 = (dineroLike) => {
* laborTaxableSale,
* laborNonTaxableSale,
* extrasSale,
* extrasTaxableSale,
* extrasNonTaxableSale,
* totalSale,
* cost,
* profitCenter,
* costCenter
* }
*
* For each center, we can emit up to 5 GOG *segments*:
* - taxable parts (CustTxblNTxblFlag="T")
* - non-taxable parts (CustTxblNTxblFlag="N")
* - extras (uses profitCenter.rr_cust_txbl_flag)
* - taxable labor (CustTxblNTxblFlag="T")
* - non-tax labor (CustTxblNTxblFlag="N")
* For each center, we can emit up to 6 GOG *segments*:
* - taxable parts (CustTxblNTxblFlag="T")
* - non-taxable parts (CustTxblNTxblFlag="N")
* - taxable extras (CustTxblNTxblFlag="T")
* - non-taxable extras (CustTxblNTxblFlag="N")
* - taxable labor (CustTxblNTxblFlag="T")
* - non-taxable labor (CustTxblNTxblFlag="N")
*
* IMPORTANT:
* Each segment becomes its OWN JobNo / AllGogOpCodeInfo, with exactly one
@@ -153,7 +156,8 @@ const buildRogogFromAllocations = (allocations, { opCode, payType = "Cust", roNo
const partsTaxableCents = toCents(alloc.partsTaxableSale);
const partsNonTaxableCents = toCents(alloc.partsNonTaxableSale);
const extrasCents = toCents(alloc.extrasSale);
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);
@@ -178,16 +182,25 @@ const buildRogogFromAllocations = (allocations, { opCode, payType = "Cust", roNo
});
}
// 3) Extras segment (respect center's default tax flag)
if (extrasCents !== 0) {
// 3) Taxable extras -> "T"
if (extrasTaxableCents !== 0) {
segments.push({
kind: "extras",
saleCents: extrasCents,
txFlag: pc.rr_cust_txbl_flag || "N"
kind: "extrasTaxable",
saleCents: extrasTaxableCents,
txFlag: "T"
});
}
// 4) Taxable labor segment -> "T"
// 4) Non-taxable extras -> "N"
if (extrasNonTaxableCents !== 0) {
segments.push({
kind: "extrasNonTaxable",
saleCents: extrasNonTaxableCents,
txFlag: "N"
});
}
// 5) Taxable labor segment -> "T"
if (laborTaxableCents !== 0) {
segments.push({
kind: "laborTaxable",
@@ -196,7 +209,7 @@ const buildRogogFromAllocations = (allocations, { opCode, payType = "Cust", roNo
});
}
// 5) Non-taxable labor segment -> "N"
// 6) Non-tax labor segment -> "N"
if (laborNonTaxableCents !== 0) {
segments.push({
kind: "laborNonTaxable",
@@ -356,10 +369,8 @@ const QueryJobData = async (ctx = {}, jobId) => {
* @param advisorNo
* @param story
* @param makeOverride
* @param bodyshop
* @param allocations
* @param {string} [opCode] - RR OpCode for this RO (global default / override)
* @param {string} [taxCode] - RR tax code for header tax (e.g. state/prov code)
* @returns {Object}
*/
const buildRRRepairOrderPayload = ({
@@ -370,7 +381,6 @@ const buildRRRepairOrderPayload = ({
makeOverride,
allocations,
opCode
// taxCode
} = {}) => {
const customerNo = selectedCustomer?.customerNo
? String(selectedCustomer.customerNo).trim()
@@ -421,7 +431,6 @@ const buildRRRepairOrderPayload = ({
if (haveAllocations) {
const effectiveOpCode = (opCode && String(opCode).trim()) || null;
// const effectiveTaxCode = (taxCode && String(taxCode).trim()) || null;
if (effectiveOpCode) {
// Build RO.GOG and RO.LABOR in the new normalized shape