const client = require("../graphql-client/graphql-client").client; const { GET_JOB_BY_PK } = require("../graphql-client/queries"); // ---------- Internals ---------- function digitsOnly(s) { return String(s || "").replace(/\D/g, ""); } function pickJobId(ctx, explicitId) { return explicitId || ctx?.job?.id || ctx?.payload?.job?.id || ctx?.payload?.jobId || ctx?.jobId || null; } function safeVin(job) { return (job?.v_vin && String(job.v_vin).trim()) || null; } // Combined search helpers expect array-like blocks function blocksFromCombinedSearchResult(res) { const data = Array.isArray(res?.data) ? res.data : Array.isArray(res) ? res : []; return Array.isArray(data) ? data : []; } // ---------- Public API ---------- async function QueryJobData(ctx = {}, jobId) { if (ctx?.job) return ctx.job; if (ctx?.payload?.job) return ctx.payload.job; const id = pickJobId(ctx, jobId); if (!id) throw new Error("QueryJobData: jobId required (none found in ctx or args)"); try { const res = await client.request(GET_JOB_BY_PK, { id }); const job = res?.jobs_by_pk; if (!job) throw new Error(`Job ${id} not found`); return job; } catch (e) { const msg = e?.response?.errors?.[0]?.message || e.message || "unknown"; throw new Error(`QueryJobData failed: ${msg}`); } } /** * Build minimal RR RO payload (keys match RR client expectations). * Provide ALL common variants so downstream ops accept them: * - RO number: outsdRoNo / OutsdRoNo / repairOrderNumber / RepairOrderNumber * - Dept: DeptType / departmentType / deptType * - VIN: Vin / vin * - Customer: CustNo / customerNo / custNo * - Advisor: AdvNo / AdvisorNo / advisorNo / advNo */ function buildRRRepairOrderPayload({ job, selectedCustomer, advisorNo }) { const customerNo = selectedCustomer?.customerNo ? String(selectedCustomer.customerNo).trim() : selectedCustomer?.custNo ? String(selectedCustomer.custNo).trim() : null; if (!customerNo) throw new Error("No RR customer selected (customerNo/CustNo missing)"); const adv = advisorNo != null && String(advisorNo).trim() !== "" ? String(advisorNo).trim() : null; if (!adv) throw new Error("advisorNo is required for RR export"); const vinRaw = job?.v_vin; const vin = typeof vinRaw === "string" ? vinRaw .replace(/[^A-Za-z0-9]/g, "") .toUpperCase() .slice(0, 17) || undefined : undefined; // Use ro_number when present; fallback to job.id const ro = job?.ro_number != null ? job.ro_number : job?.id != null ? job.id : null; if (ro == null) throw new Error("Missing repair order identifier (ro_number/id)"); const mileageIn = job.kmin; const roStr = String(ro); return { // ---- RO Number (all variants; library currently requires `outsdRoNo`) ---- outsdRoNo: roStr, repairOrderNumber: roStr, // ---- Department type (Body) ---- departmentType: "B", // ---- VIN variants ---- vin, // ---- Customer number variants ---- customerNo: String(customerNo), // ---- Advisor number variants ---- advisorNo: adv, // ---- Mileage In (new) ---- mileageIn }; } function makeVehicleSearchPayloadFromJob(job) { const vin = safeVin(job); if (vin) return { kind: "vin", vin }; const plate = job?.plate_no; if (plate) return { kind: "license", license: String(plate).trim() }; return null; } function makeCustomerSearchPayloadFromJob(job) { const phone = job?.ownr_ph1; const d = digitsOnly(phone); if (d.length >= 7) return { kind: "phone", phone: d }; const lastName = job?.ownr_ln; const company = job?.ownr_co_nm; const lnOrCompany = lastName || company; if (lnOrCompany) return { kind: "name", name: { name: String(lnOrCompany).trim() } }; const vin = safeVin(job); if (vin) return { kind: "vin", vin }; return null; } function normalizeCustomerCandidates(res) { const blocks = blocksFromCombinedSearchResult(res); const out = []; for (const blk of blocks) { const serv = Array.isArray(blk?.ServVehicle) ? blk.ServVehicle : []; const custNos = serv.map((sv) => sv?.VehicleServInfo?.CustomerNo).filter(Boolean); const nci = blk?.NameContactId; const ind = nci?.NameId?.IndName; const bus = nci?.NameId?.BusName; const personal = [ind?.FName, ind?.LName].filter(Boolean).join(" ").trim(); const company = bus?.CompanyName; const name = (personal || company || "").trim(); for (const custNo of custNos) { out.push({ custNo, name: name || `Customer ${custNo}`, _blk: blk }); } } const seen = new Set(); return out.filter((c) => { if (!c.custNo || seen.has(c.custNo)) return false; seen.add(c.custNo); return true; }); } function normalizeVehicleCandidates(res) { const blocks = blocksFromCombinedSearchResult(res); const out = []; for (const blk of blocks) { const serv = Array.isArray(blk?.ServVehicle) ? blk.ServVehicle : []; for (const sv of serv) { const v = sv?.Vehicle || {}; const vin = v?.Vin || v?.VIN || v?.vin; if (!vin) continue; const year = v?.VehicleYr || v?.ModelYear || v?.Year; const make = v?.VehicleMake || v?.MakeName || v?.Make; const model = v?.MdlNo || v?.ModelDesc || v?.Model; const label = [year, make, model, vin].filter(Boolean).join(" "); out.push({ vin, year, make, model, label, _blk: blk }); } } const seen = new Set(); return out.filter((v) => { if (!v.vin || seen.has(v.vin)) return false; seen.add(v.vin); return true; }); } module.exports = { QueryJobData, buildRRRepairOrderPayload, makeCustomerSearchPayloadFromJob, makeVehicleSearchPayloadFromJob, normalizeCustomerCandidates, normalizeVehicleCandidates };