diff --git a/client/src/components/dms-allocations-summary/rr-dms-allocations-summary.component.jsx b/client/src/components/dms-allocations-summary/rr-dms-allocations-summary.component.jsx index f35daab9e..da9c485e2 100644 --- a/client/src/components/dms-allocations-summary/rr-dms-allocations-summary.component.jsx +++ b/client/src/components/dms-allocations-summary/rr-dms-allocations-summary.component.jsx @@ -4,6 +4,7 @@ import { useCallback, useEffect, useMemo, useState } from "react"; import { useTranslation } from "react-i18next"; import { connect } from "react-redux"; import { createStructuredSelector } from "reselect"; +import { resolveRROpCodeFromBodyshop } from "../../utils/dmsUtils.js"; import { selectBodyshop } from "../../redux/user/user.selectors"; @@ -97,7 +98,7 @@ export function RrAllocationsSummary({ socket, bodyshop, jobId, title }) { fetchAllocations(); }, [fetchAllocations]); - const opCode = bodyshop?.rr_configuration?.baseOpCode || "28TOZ"; + const opCode = resolveRROpCodeFromBodyshop(bodyshop); const segmentLabelMap = { partsExtras: "Parts/Extras", diff --git a/client/src/redux/user/user.sagas.js b/client/src/redux/user/user.sagas.js index 12533865c..663724691 100644 --- a/client/src/redux/user/user.sagas.js +++ b/client/src/redux/user/user.sagas.js @@ -49,8 +49,8 @@ import { validatePasswordResetSuccess } from "./user.actions"; import UserActionTypes from "./user.types"; -//import * as amplitude from '@amplitude/analytics-browser'; import posthog from "posthog-js"; +import { bodyshopHasDmsKey, determineDMSTypeByBodyshop, DMS_MAP } from "../../utils/dmsUtils"; const fpPromise = FingerprintJS.load(); @@ -387,16 +387,15 @@ export function* SetAuthLevelFromShopDetails({ payload }) { ) ]; + const hasDmsKey = bodyshopHasDmsKey(payload); + const dmsType = hasDmsKey ? determineDMSTypeByBodyshop(payload) : null; + const additionalSegments = [ - payload.cdk_dealerid && "CDK", - payload.pbs_serialnumber && "PBS", - // payload.rr_dealerid && "Reynolds", - payload.accountingconfig.qbo === true && "QBO", - payload.accountingconfig.qbo === false && - !payload.cdk_dealerid && - !payload.pbs_serialnumber && - // !payload.rr_dealerid && - "QBD" + dmsType === DMS_MAP.cdk && DMS_MAP.cdk.toUpperCase(), + dmsType === DMS_MAP.pbs && DMS_MAP.pbs.toUpperCase(), + dmsType === DMS_MAP.reynolds && DMS_MAP.reynolds.toUpperCase(), + payload.accountingconfig?.qbo === true && "QBO", + payload.accountingconfig?.qbo === false && !hasDmsKey && "QBD" ].filter(Boolean); featureSegments.push(...additionalSegments); diff --git a/client/src/utils/dmsUtils.js b/client/src/utils/dmsUtils.js index e4ae68f03..bd0dda284 100644 --- a/client/src/utils/dmsUtils.js +++ b/client/src/utils/dmsUtils.js @@ -70,3 +70,26 @@ export const isWssMode = (mode) => { */ export const bodyshopHasDmsKey = (bodyshop) => bodyshop.cdk_dealerid || bodyshop.pbs_serialnumber || bodyshop.rr_dealerid; + +/** + * Resolve RR OpCode from bodyshop.rr_configuration.defaults + * Is Duplicated on server/rr/rr-utils.js + * @param bodyshop + * @returns {`${string}${string}${string}`} + */ +export const resolveRROpCodeFromBodyshop = (bodyshop) => { + if (!bodyshop) throw new Error("bodyshop is required"); + + const cfg = bodyshop?.rr_configuration || {}; + const defaults = cfg?.defaults || {}; + + const prefix = (defaults.prefix ?? "").toString().trim(); + const base = (defaults.base ?? "").toString().trim(); + const suffix = (defaults.suffix ?? "").toString().trim(); + + if (!prefix && !base && !suffix) { + throw new Error("No RR OpCode parts found in bodyshop configuration"); + } + + return `${prefix}${base}${suffix}`; +}; diff --git a/server/rr/rr-job-export.js b/server/rr/rr-job-export.js index eafca89a7..498f0334a 100644 --- a/server/rr/rr-job-export.js +++ b/server/rr/rr-job-export.js @@ -3,7 +3,7 @@ const { buildClientAndOpts } = require("./rr-lookup"); const CreateRRLogEvent = require("./rr-logger-event"); const { extractRrResponsibilityCenters } = require("./rr-responsibility-centers"); const CdkCalculateAllocations = require("./rr-calculate-allocations").default; - +const { resolveRROpCodeFromBodyshop } = require("./rr-utils"); /** * Derive RR status information from response object. * @param rrRes @@ -137,19 +137,17 @@ const exportJobToRR = async (args) => { allocations = []; } - // 3) OpCode (global, but overridable) - // - baseOpCode can come from bodyshop or rrCentersConfig (you'll map it in onboarding) - // - txEnvelope can carry an explicit override field (opCode/opcode/op_code) - const baseOpCode = bodyshop?.rr_configuration?.baseOpCode || "28TOZ"; // TODO Change / implement default handling policy + const resolvedBaseOpCode = resolveRROpCodeFromBodyshop(bodyshop); const opCodeOverride = txEnvelope?.opCode || txEnvelope?.opcode || txEnvelope?.op_code || null; - if (opCodeOverride || baseOpCode) { - opCode = String(opCodeOverride || baseOpCode).trim() || null; + if (opCodeOverride || resolvedBaseOpCode) { + opCode = String(opCodeOverride || resolvedBaseOpCode).trim() || null; } CreateRRLogEvent(socket, "SILLY", "RR OP config resolved", { - opCode + opCode, + baseFromConfig: resolvedBaseOpCode }); // Build RO payload for create. diff --git a/server/rr/rr-register-socket-events.js b/server/rr/rr-register-socket-events.js index 556fdb6d5..026577afe 100644 --- a/server/rr/rr-register-socket-events.js +++ b/server/rr/rr-register-socket-events.js @@ -16,6 +16,7 @@ const { defaultRRTTL, RRCacheEnums } = require("./rr-utils"); +const { resolveRROpCodeFromBodyshop } = require("./rr-utils"); const { GraphQLClient } = require("graphql-request"); const queries = require("../graphql-client/queries"); @@ -924,13 +925,17 @@ const registerRREvents = ({ socket, redisHelpers }) => { jobAllocations = Array.isArray(ack.jobAllocations) ? ack.jobAllocations : []; } - // Try to derive OpCode from bodyshop; fall back to default - let opCode = "28TOZ"; + // Try to derive OpCode from bodyshop.rr_configuration.defaults; fall back to default + let opCode; try { const { bodyshopId } = await getSessionOrSocket(redisHelpers, socket); const bodyshop = await getBodyshopForSocket({ bodyshopId, socket }); - opCode = bodyshop?.rr_configuration?.baseOpCode || opCode; + opCode = resolveRROpCodeFromBodyshop(bodyshop, opCode); + + CreateRRLogEvent(socket, "DEBUG", "rr-calculate-allocations: resolved OpCode", { + opCode + }); } catch (e) { CreateRRLogEvent(socket, "WARN", "rr-calculate-allocations: bodyshop lookup failed, using default OpCode", { error: e.message diff --git a/server/rr/rr-utils.js b/server/rr/rr-utils.js index dd898b12a..0a8cb49ab 100644 --- a/server/rr/rr-utils.js +++ b/server/rr/rr-utils.js @@ -182,6 +182,29 @@ const getTransactionType = (jobid) => `rr:${jobid}`; */ const defaultRRTTL = 60 * 60; +/** + * Resolve RR OpCode from bodyshop.rr_configuration.defaults + * Is Duplicated on client/src/utils/dmsUtils.js + * @param bodyshop + * @returns {string} + */ +const resolveRROpCodeFromBodyshop = (bodyshop) => { + if (!bodyshop) throw new Error("bodyshop is required"); + + const cfg = bodyshop?.rr_configuration || {}; + const defaults = cfg?.defaults || {}; + + const prefix = (defaults.prefix ?? "").toString().trim(); + const base = (defaults.base ?? "").toString().trim(); + const suffix = (defaults.suffix ?? "").toString().trim(); + + if (!prefix && !base && !suffix) { + throw new Error("No RR OpCode parts found in bodyshop configuration"); + } + + return `${prefix}${base}${suffix}`; +}; + module.exports = { RRCacheEnums, defaultRRTTL, @@ -189,5 +212,6 @@ module.exports = { ownersFromVinBlocks, makeVehicleSearchPayloadFromJob, normalizeCustomerCandidates, - readAdvisorNo + readAdvisorNo, + resolveRROpCodeFromBodyshop };