feature/IO-3255-simplified-parts-management - Checkpoint
This commit is contained in:
@@ -4,6 +4,9 @@
|
||||
const xml2js = require("xml2js");
|
||||
const client = require("../../graphql-client/graphql-client").client;
|
||||
|
||||
// Defaults
|
||||
const FALLBACK_DEFAULT_ORDER_STATUS = "OPEN"; // Default status if not found in bodyshop
|
||||
|
||||
// GraphQL statements
|
||||
const INSERT_JOB_WITH_LINES = `
|
||||
mutation InsertJob($job: jobs_insert_input!) {
|
||||
@@ -14,6 +17,30 @@ const INSERT_JOB_WITH_LINES = `
|
||||
}
|
||||
`;
|
||||
|
||||
const GET_BODYSHOP_STATUS = `
|
||||
query GetBodyshopStatus($id: uuid!) {
|
||||
bodyshops_by_pk(id: $id) {
|
||||
md_order_statuses
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
const INSERT_OWNER = `
|
||||
mutation InsertOwner($owner: owners_insert_input!) {
|
||||
insert_owners_one(object: $owner) {
|
||||
id
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
// Do they call the add call first, future ones will be updates, we need to upcycle. Or we need to send a new add request, we treat it as an upsert.
|
||||
|
||||
/**
|
||||
* Handles the VehicleDamageEstimateAddRq XML request from parts management.
|
||||
* @param req
|
||||
* @param res
|
||||
* @returns {Promise<*>}
|
||||
*/
|
||||
const partsManagementVehicleDamageEstimateAddRq = async (req, res) => {
|
||||
const { logger } = req;
|
||||
const xml = req.body;
|
||||
@@ -23,9 +50,11 @@ const partsManagementVehicleDamageEstimateAddRq = async (req, res) => {
|
||||
try {
|
||||
payload = await xml2js.parseStringPromise(xml, {
|
||||
explicitArray: false,
|
||||
tagNameProcessors: [xml2js.processors.stripPrefix]
|
||||
tagNameProcessors: [xml2js.processors.stripPrefix],
|
||||
attrNameProcessors: [xml2js.processors.stripPrefix]
|
||||
// ignoreAttrs: false,
|
||||
// xmlns: false
|
||||
});
|
||||
logger.log("parts-xml-parse", "debug", null, null, { success: true });
|
||||
} catch (err) {
|
||||
logger.log("parts-xml-parse-error", "error", null, null, { error: err });
|
||||
return res.status(400).send("Invalid XML");
|
||||
@@ -43,6 +72,14 @@ const partsManagementVehicleDamageEstimateAddRq = async (req, res) => {
|
||||
if (!shopId) throw { status: 400, message: "Missing <ShopID> in XML" };
|
||||
const { RefClaimNum } = rq;
|
||||
|
||||
let defaultStatus = FALLBACK_DEFAULT_ORDER_STATUS;
|
||||
|
||||
try {
|
||||
const { bodyshop_by_pk } = await client.request(GET_BODYSHOP_STATUS, { id: shopId });
|
||||
defaultStatus = bodyshop_by_pk?.md_order_statuses?.default_open || defaultStatus;
|
||||
} catch (err) {
|
||||
logger.log("parts-bodyshop-fetch-failed", "warn", shopId, null, { error: err });
|
||||
}
|
||||
// ── DOCUMENT INFO ──────────────────────────────────────────────────────────
|
||||
const doc = rq.DocumentInfo || {};
|
||||
const comment = doc.Comment || null;
|
||||
@@ -55,6 +92,68 @@ const partsManagementVehicleDamageEstimateAddRq = async (req, res) => {
|
||||
const category = doc.DocumentType || null;
|
||||
const classType = doc.DocumentStatus || null;
|
||||
|
||||
// ── PARTS TAX RATES STRUCTURE ───────────────────────────────────────────────
|
||||
// Known rate types that map to your parts_tax_rates keys
|
||||
const knownPartRateTypes = [
|
||||
"PAA",
|
||||
"PAC",
|
||||
"PAG",
|
||||
"PAL",
|
||||
"PAM",
|
||||
"PAN",
|
||||
"PAO",
|
||||
"PAP",
|
||||
"PAR",
|
||||
"PAS",
|
||||
"PASL",
|
||||
"CCC",
|
||||
"CCD",
|
||||
"CCF",
|
||||
"CCM",
|
||||
"CCDR"
|
||||
];
|
||||
|
||||
const profile = rq.ProfileInfo || {};
|
||||
const rateInfos = Array.isArray(profile.RateInfo) ? profile.RateInfo : [profile.RateInfo || {}];
|
||||
|
||||
const parts_tax_rates = {};
|
||||
|
||||
for (const code of knownPartRateTypes) {
|
||||
const rateInfo = rateInfos.find((r) => (r?.RateType || "").toUpperCase() === code);
|
||||
if (!rateInfo) {
|
||||
parts_tax_rates[code] = {};
|
||||
continue;
|
||||
}
|
||||
|
||||
const taxInfo = rateInfo.TaxInfo;
|
||||
const taxTier = taxInfo?.TaxTierInfo;
|
||||
|
||||
// Try to find Percentage first
|
||||
let percentage = parseFloat(taxTier?.Percentage ?? "NaN");
|
||||
if (isNaN(percentage)) {
|
||||
// fallback to RateTierInfo.Rate if that's where it might be
|
||||
const tierRate = Array.isArray(rateInfo.RateTierInfo)
|
||||
? rateInfo.RateTierInfo[0]?.Rate
|
||||
: rateInfo.RateTierInfo?.Rate;
|
||||
|
||||
percentage = parseFloat(tierRate ?? "NaN");
|
||||
}
|
||||
|
||||
// Still no tax rate? fallback to null object
|
||||
if (isNaN(percentage)) {
|
||||
parts_tax_rates[code] = {};
|
||||
continue;
|
||||
}
|
||||
|
||||
parts_tax_rates[code] = {
|
||||
prt_discp: 0,
|
||||
prt_mktyp: false,
|
||||
prt_mkupp: 0,
|
||||
prt_tax_in: true,
|
||||
prt_tax_rt: percentage / 100
|
||||
};
|
||||
}
|
||||
|
||||
// ── EVENT INFO ──────────────────────────────────────────────────────────────
|
||||
const ev = rq.EventInfo || {};
|
||||
const asgn = ev.AssignmentEvent || {};
|
||||
@@ -106,8 +205,7 @@ const partsManagementVehicleDamageEstimateAddRq = async (req, res) => {
|
||||
const est_ct_fn = est_fn;
|
||||
const est_ct_ln = est_ln;
|
||||
|
||||
// TODO: SHould be the estimator insurance company name est_co_name
|
||||
const est_aff = rq.AdminInfo?.Estimator?.Affiliation || null;
|
||||
const est_co_nm = rq.AdminInfo?.Estimator?.Affiliation || null;
|
||||
|
||||
const estComms = Array.isArray(estParty.ContactInfo?.Communications)
|
||||
? estParty.ContactInfo.Communications
|
||||
@@ -193,9 +291,34 @@ const partsManagementVehicleDamageEstimateAddRq = async (req, res) => {
|
||||
notes: line.LineMemo || null
|
||||
}));
|
||||
|
||||
const ownerInput = {
|
||||
shopid: shopId,
|
||||
ownr_fn,
|
||||
ownr_ln,
|
||||
ownr_co_nm,
|
||||
ownr_addr1,
|
||||
ownr_addr2,
|
||||
ownr_city,
|
||||
ownr_st,
|
||||
ownr_zip,
|
||||
ownr_ctry,
|
||||
ownr_ph1,
|
||||
ownr_ph2,
|
||||
ownr_ea
|
||||
};
|
||||
|
||||
let ownerid = null;
|
||||
try {
|
||||
const { insert_owners_one } = await client.request(INSERT_OWNER, { owner: ownerInput });
|
||||
ownerid = insert_owners_one?.id;
|
||||
} catch (err) {
|
||||
logger.log("parts-owner-insert-failed", "warn", null, null, { error: err });
|
||||
}
|
||||
|
||||
// ── BUILD & INSERT THE JOB ──────────────────────────────────────────────────
|
||||
const jobInput = {
|
||||
shopid: shopId,
|
||||
ownerid,
|
||||
ro_number: RefClaimNum,
|
||||
|
||||
// IDs & CIECA metadata
|
||||
@@ -205,10 +328,12 @@ const partsManagementVehicleDamageEstimateAddRq = async (req, res) => {
|
||||
category,
|
||||
class: classType,
|
||||
|
||||
// tax
|
||||
parts_tax_rates,
|
||||
|
||||
// claim & policy
|
||||
clm_no,
|
||||
// default job: bodyshop.md_status.default_open
|
||||
status: status || "OPEN",
|
||||
status: status || defaultStatus,
|
||||
clm_total: cieca_ttl,
|
||||
policy_no,
|
||||
ded_amt,
|
||||
@@ -238,7 +363,7 @@ const partsManagementVehicleDamageEstimateAddRq = async (req, res) => {
|
||||
ownr_ea,
|
||||
|
||||
// estimator
|
||||
// est_co_id: est_aff,
|
||||
est_co_nm,
|
||||
est_ct_fn,
|
||||
est_ct_ln,
|
||||
est_ea,
|
||||
@@ -253,16 +378,13 @@ const partsManagementVehicleDamageEstimateAddRq = async (req, res) => {
|
||||
servicing_dealer,
|
||||
servicing_dealer_contact,
|
||||
|
||||
// stash any extra CIECA stuff we didn’t map above
|
||||
production_vars: {},
|
||||
|
||||
// nested relationships
|
||||
vehicle: { data: vehicleData },
|
||||
joblines: { data: joblinesData }
|
||||
};
|
||||
|
||||
logger.log("parts-insert-job", "debug", null, null, { jobInput });
|
||||
const { insert_jobs_one: newJob } = await client.request(INSERT_JOB_WITH_LINES, { job: jobInput });
|
||||
|
||||
logger.log("parts-job-created", "info", newJob.id, null);
|
||||
|
||||
return res.status(200).json({ success: true, jobId: newJob.id });
|
||||
|
||||
Reference in New Issue
Block a user