// Helpers to use md_responsibility_centers (including RR GOG metadata) // to build the data needed for ROLABOR + ROGOG lines. /** * Extracts responsibility center configuration into a convenient structure. * * Expects bodyshop.md_responsibility_centers to look like: * * { * costs: [{ name, ... }], * profits: [{ name, rr_gogcode, rr_item_type, rr_cust_txbl_flag, ... }], * dms_defaults: { * costs: { LAB: "Body Labor Cost", ... }, * profits: { LAB: "Body Labor Sales", ... } * }, * defaults: { * costs: { LAB: "Some Cost Center", ... }, * profits: { LAB: "Some Profit Center", ... } * } * } */ function extractRrResponsibilityCenters(bodyshop = {}) { const centers = bodyshop.md_responsibility_centers || {}; const { costs = [], profits = [], dms_defaults = {}, defaults = {} } = centers; const indexByName = (arr = []) => (arr || []).reduce((acc, center) => { if (center && center.name) { acc[center.name] = center; } return acc; }, {}); return { costsByName: indexByName(costs), profitsByName: indexByName(profits), dmsCostDefaults: (dms_defaults && dms_defaults.costs) || {}, dmsProfitDefaults: (dms_defaults && dms_defaults.profits) || {}, defaultCosts: defaults.costs || {}, defaultProfits: defaults.profits || {} }; } /** * Resolve cost + profit centers for a given internal code (ATS/LAB/etc.). * Prioritizes dms_defaults, then defaults. * * @param {string} code * @param {ReturnType} cfg * @returns {{ costCenter: object|null, profitCenter: object|null }} */ function resolveCentersForCode(code, cfg) { if (!code || !cfg) return { costCenter: null, profitCenter: null }; const costCenterName = cfg.dmsCostDefaults[code] || cfg.defaultCosts[code] || null; const profitCenterName = cfg.dmsProfitDefaults[code] || cfg.defaultProfits[code] || null; const costCenter = costCenterName && cfg.costsByName[costCenterName] ? cfg.costsByName[costCenterName] : null; const profitCenter = profitCenterName && cfg.profitsByName[profitCenterName] ? cfg.profitsByName[profitCenterName] : null; return { costCenter, profitCenter }; } /** * Build a single RR GOG line payload from a jobline + code. * * NOTE: This returns a neutral JS object. You still need to map it into * the exact Mustache / XML shape used for AllGogLineItmInfo. * * @param {Object} params * @param {Object} params.jobline // jobline record from Hasura * @param {string} params.code // e.g. "LAB", "ATS", ... * @param {object} params.centersConfig // result of extractRrResponsibilityCenters() * @param {number} params.sale // CustPrice * @param {number} params.cost // DollarCost */ function buildRrGogLine({ jobline, code, centersConfig, sale, cost }) { const { profitCenter } = resolveCentersForCode(code, centersConfig); if (!profitCenter || !profitCenter.rr_gogcode) { throw new Error('RR: missing rr_gogcode for profit center mapping of code "' + code + '"'); } return { // For your own reference: code, joblineId: jobline && jobline.id, // Fields you ultimately care about for ROGOG: BreakOut: profitCenter.rr_gogcode, // GOG code / BreakOut ItemType: profitCenter.rr_item_type || "G", // P / G / F / ... CustTxblNTxblFlag: profitCenter.rr_cust_txbl_flag || "T", // T/N CustPrice: Number(sale || 0), DollarCost: Number(cost || 0), // Surface full profit center in case templates / logs need it: profitCenter }; } /** * Build a minimal RR labor (ROLABOR) line "shell". * Amounts are zero if all financials are in GOG. * * @param {Object} params * @param {Object} params.job // job record (for RO header context) * @param {string} params.opCode // global opcode (already resolved) * @param {Object} [params.profitCenter] // optional profit center for context */ function buildRrLaborLine({ job, opCode, profitCenter }) { if (!opCode) { throw new Error("RR: opCode is required to build ROLABOR line"); } return { jobId: job && job.id, OpCode: opCode, PayType: "Cust", // always Cust per your notes CustPrice: 0, DollarCost: 0, profitCenter }; } module.exports = { extractRrResponsibilityCenters, resolveCentersForCode, buildRrGogLine, buildRrLaborLine };