Scratch
This commit is contained in:
@@ -1,7 +1,7 @@
|
|||||||
/**
|
/**
|
||||||
* THIS IS A COPY of CDKCalculateAllocations, modified to:
|
* THIS IS A COPY of CDKCalculateAllocations, modified to:
|
||||||
* - Only calculate allocations needed for Reynolds & RR exports
|
* - Only calculate allocations needed for Reynolds & RR exports
|
||||||
* - Keep sales broken down into buckets (parts, taxable labor, non-taxable labor, extras)
|
* - Keep sales broken down into buckets (parts, taxable / non-taxable parts, taxable labor, non-taxable labor, extras)
|
||||||
* - Add extra logging for easier debugging
|
* - Add extra logging for easier debugging
|
||||||
*
|
*
|
||||||
* Original comments follow.
|
* Original comments follow.
|
||||||
@@ -49,14 +49,21 @@ const summarizeAllocationsArray = (arr) =>
|
|||||||
/**
|
/**
|
||||||
* Internal per-center bucket shape for *sales*.
|
* Internal per-center bucket shape for *sales*.
|
||||||
* We keep separate buckets for RR so we can split
|
* We keep separate buckets for RR so we can split
|
||||||
* taxable vs non-taxable labor lines later.
|
* taxable vs non-taxable parts and labor lines later.
|
||||||
*/
|
*/
|
||||||
function emptyCenterBucket() {
|
function emptyCenterBucket() {
|
||||||
const zero = Dinero();
|
const zero = Dinero();
|
||||||
return {
|
return {
|
||||||
partsSale: zero, // parts sale
|
// Parts
|
||||||
|
partsSale: zero, // total parts (taxable + non-taxable)
|
||||||
|
partsTaxableSale: zero, // parts that should be taxed in RR
|
||||||
|
partsNonTaxableSale: zero, // parts that should NOT be taxed in RR
|
||||||
|
|
||||||
|
// Labor
|
||||||
laborTaxableSale: zero, // labor that should be taxed in RR
|
laborTaxableSale: zero, // labor that should be taxed in RR
|
||||||
laborNonTaxableSale: zero, // labor that should NOT be taxed in RR
|
laborNonTaxableSale: zero, // labor that should NOT be taxed in RR
|
||||||
|
|
||||||
|
// Extras
|
||||||
extrasSale: zero // MAPA/MASH/towing/storage/PAO/etc
|
extrasSale: zero // MAPA/MASH/towing/storage/PAO/etc
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -161,6 +168,15 @@ function isLaborTaxable(line) {
|
|||||||
return line.tax_part;
|
return line.tax_part;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
function isPartTaxable(line) {
|
||||||
|
return line.tax_part;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Build profitCenterHash from joblines (parts + labor) and detect MAPA/MASH presence.
|
* Build profitCenterHash from joblines (parts + labor) and detect MAPA/MASH presence.
|
||||||
* Now stores *buckets* instead of a single Dinero per center.
|
* Now stores *buckets* instead of a single Dinero per center.
|
||||||
@@ -215,6 +231,15 @@ function buildProfitCenterHash(job, debugLog) {
|
|||||||
amount = amount.add(discount);
|
amount = amount.add(discount);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const taxable = isPartTaxable(val);
|
||||||
|
|
||||||
|
if (taxable) {
|
||||||
|
bucket.partsTaxableSale = bucket.partsTaxableSale.add(amount);
|
||||||
|
} else {
|
||||||
|
bucket.partsNonTaxableSale = bucket.partsNonTaxableSale.add(amount);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Keep total parts for compatibility / convenience
|
||||||
bucket.partsSale = bucket.partsSale.add(amount);
|
bucket.partsSale = bucket.partsSale.add(amount);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -245,6 +270,8 @@ function buildProfitCenterHash(job, debugLog) {
|
|||||||
centers: Object.entries(profitCenterHash).map(([center, b]) => ({
|
centers: Object.entries(profitCenterHash).map(([center, b]) => ({
|
||||||
center,
|
center,
|
||||||
parts: summarizeMoney(b.partsSale),
|
parts: summarizeMoney(b.partsSale),
|
||||||
|
partsTaxable: summarizeMoney(b.partsTaxableSale),
|
||||||
|
partsNonTaxable: summarizeMoney(b.partsNonTaxableSale),
|
||||||
laborTaxable: summarizeMoney(b.laborTaxableSale),
|
laborTaxable: summarizeMoney(b.laborTaxableSale),
|
||||||
laborNonTaxable: summarizeMoney(b.laborNonTaxableSale),
|
laborNonTaxable: summarizeMoney(b.laborNonTaxableSale),
|
||||||
extras: summarizeMoney(b.extrasSale)
|
extras: summarizeMoney(b.extrasSale)
|
||||||
@@ -521,14 +548,6 @@ function applyExtras({ job, bodyshop, selectedDmsAllocationConfig, profitCenterH
|
|||||||
return { profitCenterHash, taxAllocations };
|
return { profitCenterHash, taxAllocations };
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Apply Rome-specific profile adjustments (parts + rates).
|
|
||||||
* These also feed into the *sales* buckets.
|
|
||||||
*/
|
|
||||||
/**
|
|
||||||
* Apply Rome-specific profile adjustments (parts + rates).
|
|
||||||
* These also feed into the *sales* buckets.
|
|
||||||
*/
|
|
||||||
/**
|
/**
|
||||||
* Apply Rome-specific profile adjustments (parts + rates).
|
* Apply Rome-specific profile adjustments (parts + rates).
|
||||||
* These also feed into the *sales* buckets.
|
* These also feed into the *sales* buckets.
|
||||||
@@ -627,6 +646,8 @@ function applyRomeProfileAdjustments({
|
|||||||
* {
|
* {
|
||||||
* center,
|
* center,
|
||||||
* partsSale,
|
* partsSale,
|
||||||
|
* partsTaxableSale,
|
||||||
|
* partsNonTaxableSale,
|
||||||
* laborTaxableSale,
|
* laborTaxableSale,
|
||||||
* laborNonTaxableSale,
|
* laborNonTaxableSale,
|
||||||
* extrasSale,
|
* extrasSale,
|
||||||
@@ -653,10 +674,18 @@ function buildJobAllocations(bodyshop, profitCenterHash, costCenterHash, debugLo
|
|||||||
return {
|
return {
|
||||||
center,
|
center,
|
||||||
|
|
||||||
|
// Parts
|
||||||
partsSale: bucket.partsSale,
|
partsSale: bucket.partsSale,
|
||||||
|
partsTaxableSale: bucket.partsTaxableSale,
|
||||||
|
partsNonTaxableSale: bucket.partsNonTaxableSale,
|
||||||
|
|
||||||
|
// Labor
|
||||||
laborTaxableSale: bucket.laborTaxableSale,
|
laborTaxableSale: bucket.laborTaxableSale,
|
||||||
laborNonTaxableSale: bucket.laborNonTaxableSale,
|
laborNonTaxableSale: bucket.laborNonTaxableSale,
|
||||||
|
|
||||||
|
// Extras
|
||||||
extrasSale: bucket.extrasSale,
|
extrasSale: bucket.extrasSale,
|
||||||
|
|
||||||
totalSale,
|
totalSale,
|
||||||
|
|
||||||
cost: costCenterHash[center] || Dinero(),
|
cost: costCenterHash[center] || Dinero(),
|
||||||
@@ -671,6 +700,8 @@ function buildJobAllocations(bodyshop, profitCenterHash, costCenterHash, debugLo
|
|||||||
jobAllocations.map((row) => ({
|
jobAllocations.map((row) => ({
|
||||||
center: row.center,
|
center: row.center,
|
||||||
parts: summarizeMoney(row.partsSale),
|
parts: summarizeMoney(row.partsSale),
|
||||||
|
partsTaxable: summarizeMoney(row.partsTaxableSale),
|
||||||
|
partsNonTaxable: summarizeMoney(row.partsNonTaxableSale),
|
||||||
laborTaxable: summarizeMoney(row.laborTaxableSale),
|
laborTaxable: summarizeMoney(row.laborTaxableSale),
|
||||||
laborNonTaxable: summarizeMoney(row.laborNonTaxableSale),
|
laborNonTaxable: summarizeMoney(row.laborNonTaxableSale),
|
||||||
extras: summarizeMoney(row.extrasSale),
|
extras: summarizeMoney(row.extrasSale),
|
||||||
@@ -847,6 +878,8 @@ function calculateAllocations(connectionData, job) {
|
|||||||
centers: Object.entries(profitCenterHash || {}).map(([center, b]) => ({
|
centers: Object.entries(profitCenterHash || {}).map(([center, b]) => ({
|
||||||
center,
|
center,
|
||||||
parts: summarizeMoney(b.partsSale),
|
parts: summarizeMoney(b.partsSale),
|
||||||
|
partsTaxable: summarizeMoney(b.partsTaxableSale),
|
||||||
|
partsNonTaxable: summarizeMoney(b.partsNonTaxableSale),
|
||||||
laborTaxable: summarizeMoney(b.laborTaxableSale),
|
laborTaxable: summarizeMoney(b.laborTaxableSale),
|
||||||
laborNonTaxable: summarizeMoney(b.laborNonTaxableSale),
|
laborNonTaxable: summarizeMoney(b.laborNonTaxableSale),
|
||||||
extras: summarizeMoney(b.extrasSale)
|
extras: summarizeMoney(b.extrasSale)
|
||||||
|
|||||||
@@ -60,6 +60,8 @@ const asN2 = (dineroLike) => {
|
|||||||
* {
|
* {
|
||||||
* center,
|
* center,
|
||||||
* partsSale,
|
* partsSale,
|
||||||
|
* partsTaxableSale,
|
||||||
|
* partsNonTaxableSale,
|
||||||
* laborTaxableSale,
|
* laborTaxableSale,
|
||||||
* laborNonTaxableSale,
|
* laborNonTaxableSale,
|
||||||
* extrasSale,
|
* extrasSale,
|
||||||
@@ -69,19 +71,21 @@ const asN2 = (dineroLike) => {
|
|||||||
* costCenter
|
* costCenter
|
||||||
* }
|
* }
|
||||||
*
|
*
|
||||||
* For each center, we can emit up to 3 GOG *segments*:
|
* For each center, we can emit up to 5 GOG *segments*:
|
||||||
* - parts+extras (uses profitCenter.rr_cust_txbl_flag)
|
* - taxable parts (CustTxblNTxblFlag="T")
|
||||||
* - taxable labor (CustTxblNTxblFlag="T")
|
* - non-taxable parts (CustTxblNTxblFlag="N")
|
||||||
* - non-tax labor (CustTxblNTxblFlag="N")
|
* - extras (uses profitCenter.rr_cust_txbl_flag)
|
||||||
|
* - taxable labor (CustTxblNTxblFlag="T")
|
||||||
|
* - non-tax labor (CustTxblNTxblFlag="N")
|
||||||
*
|
*
|
||||||
* IMPORTANT CHANGE:
|
* IMPORTANT:
|
||||||
* Each segment becomes its OWN JobNo / AllGogOpCodeInfo, with exactly one
|
* Each segment becomes its OWN JobNo / AllGogOpCodeInfo, with exactly one
|
||||||
* AllGogLineItmInfo inside. This makes the count of:
|
* AllGogLineItmInfo inside. This keeps a clean 1:1 mapping between:
|
||||||
* - <AllGogOpCodeInfo> (ROGOG)
|
* - <AllGogOpCodeInfo> (ROGOG)
|
||||||
* - <OpCodeLaborInfo> (ROLABOR)
|
* - <OpCodeLaborInfo> (ROLABOR)
|
||||||
* match 1:1, and ensures taxable/non-taxable flags line up by JobNo.
|
* and ensures taxable/non-taxable flags line up by JobNo.
|
||||||
*
|
*
|
||||||
* We now also attach segmentKind/segmentIndex/segmentCount metadata on each op
|
* We attach segmentKind/segmentIndex/segmentCount metadata on each op
|
||||||
* for UI/debug purposes. The XML templates can safely ignore these.
|
* for UI/debug purposes. The XML templates can safely ignore these.
|
||||||
*
|
*
|
||||||
* @param {Array} allocations
|
* @param {Array} allocations
|
||||||
@@ -147,27 +151,43 @@ const buildRogogFromAllocations = (allocations, { opCode, payType = "Cust", roNo
|
|||||||
// Only centers configured for RR GOG are included
|
// Only centers configured for RR GOG are included
|
||||||
if (!breakOut || !itemType) continue;
|
if (!breakOut || !itemType) continue;
|
||||||
|
|
||||||
const partsCents = toCents(alloc.partsSale);
|
const partsTaxableCents = toCents(alloc.partsTaxableSale);
|
||||||
|
const partsNonTaxableCents = toCents(alloc.partsNonTaxableSale);
|
||||||
const extrasCents = toCents(alloc.extrasSale);
|
const extrasCents = toCents(alloc.extrasSale);
|
||||||
const laborTaxableCents = toCents(alloc.laborTaxableSale);
|
const laborTaxableCents = toCents(alloc.laborTaxableSale);
|
||||||
const laborNonTaxableCents = toCents(alloc.laborNonTaxableSale);
|
const laborNonTaxableCents = toCents(alloc.laborNonTaxableSale);
|
||||||
const costCents = toCents(alloc.cost);
|
const costCents = toCents(alloc.cost);
|
||||||
|
|
||||||
// Parts + extras share a single segment
|
|
||||||
const partsExtrasCents = partsCents + extrasCents;
|
|
||||||
|
|
||||||
const segments = [];
|
const segments = [];
|
||||||
|
|
||||||
// 1) Parts + extras segment (respect center's default tax flag)
|
// 1) Taxable parts segment -> "T"
|
||||||
if (partsExtrasCents !== 0) {
|
if (partsTaxableCents !== 0) {
|
||||||
segments.push({
|
segments.push({
|
||||||
kind: "partsExtras",
|
kind: "partsTaxable",
|
||||||
saleCents: partsExtrasCents,
|
saleCents: partsTaxableCents,
|
||||||
|
txFlag: "T"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2) Non-taxable parts segment -> "N"
|
||||||
|
if (partsNonTaxableCents !== 0) {
|
||||||
|
segments.push({
|
||||||
|
kind: "partsNonTaxable",
|
||||||
|
saleCents: partsNonTaxableCents,
|
||||||
|
txFlag: "N"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3) Extras segment (respect center's default tax flag)
|
||||||
|
if (extrasCents !== 0) {
|
||||||
|
segments.push({
|
||||||
|
kind: "extras",
|
||||||
|
saleCents: extrasCents,
|
||||||
txFlag: pc.rr_cust_txbl_flag || "N"
|
txFlag: pc.rr_cust_txbl_flag || "N"
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2) Taxable labor segment -> "T"
|
// 4) Taxable labor segment -> "T"
|
||||||
if (laborTaxableCents !== 0) {
|
if (laborTaxableCents !== 0) {
|
||||||
segments.push({
|
segments.push({
|
||||||
kind: "laborTaxable",
|
kind: "laborTaxable",
|
||||||
@@ -176,7 +196,7 @@ const buildRogogFromAllocations = (allocations, { opCode, payType = "Cust", roNo
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// 3) Non-taxable labor segment -> "N"
|
// 5) Non-taxable labor segment -> "N"
|
||||||
if (laborNonTaxableCents !== 0) {
|
if (laborNonTaxableCents !== 0) {
|
||||||
segments.push({
|
segments.push({
|
||||||
kind: "laborNonTaxable",
|
kind: "laborNonTaxable",
|
||||||
@@ -254,6 +274,12 @@ const buildRogogFromAllocations = (allocations, { opCode, payType = "Cust", roNo
|
|||||||
/**
|
/**
|
||||||
* Build RO.ROLABOR structure for the reynolds-rome-client `createRepairOrder` payload
|
* Build RO.ROLABOR structure for the reynolds-rome-client `createRepairOrder` payload
|
||||||
* from an already-built RO.GOG structure.
|
* from an already-built RO.GOG structure.
|
||||||
|
*
|
||||||
|
* We still keep a 1:1 mapping with GOG ops: each op gets a corresponding
|
||||||
|
* OpCodeLaborInfo entry using the same JobNo and the same tax flag as its
|
||||||
|
* GOG line. Labor-specific details (hrs/rate) remain zeroed out, and the
|
||||||
|
* DMS can ignore non-labor ops by virtue of the zero hours/amounts.
|
||||||
|
*
|
||||||
* @param {Object} rogg - result of buildRogogFromAllocations
|
* @param {Object} rogg - result of buildRogogFromAllocations
|
||||||
* @param {Object} opts
|
* @param {Object} opts
|
||||||
* @param {string} [opts.payType="Cust"]
|
* @param {string} [opts.payType="Cust"]
|
||||||
|
|||||||
Reference in New Issue
Block a user