diff --git a/src/routes/settings.import.tsx b/src/routes/settings.import.tsx index 2329218..0465129 100644 --- a/src/routes/settings.import.tsx +++ b/src/routes/settings.import.tsx @@ -190,40 +190,52 @@ const IMPORTERS: ImporterConfig[] = [ conflict: "external_id", required: ["case_number", "title", "client_id"], aliases: { + // Identifiers id: "external_id", caseid: "external_id", externalid: "external_id", mycaseid: "external_id", casenumber: "case_number", number: "case_number", mattercode: "case_number", caseno: "case_number", casenum: "case_number", + // Title / matter name name: "title", title: "title", mattername: "title", casemattername: "title", casename: "title", + // Client linkage — prefer association name; client number takes precedence over name companyid: "_clientext", clientid: "_clientext", clientno: "_clientext", clientnumber: "_clientext", - company: "_clientname", client: "_clientname", + company: "_clientname", client: "_clientname", client1: "_clientname", communityassociation: "_clientname", associationname: "_clientname", - managementcompany: "_clientname", + managementcompany: "_mgmtcompany", + // Descriptions description: "description", casedescription: "description", summary: "case_summary", casesummary: "case_summary", + // Practice area practicearea: "practice_area", area: "practice_area", caseinquirytype: "practice_area", representation: "practice_area", associationtype: "practice_area", + // Status / stage status: "status", casestage: "status", + // Dates opened: "opened_at", openedat: "opened_at", dateopened: "opened_at", opendate: "opened_at", closed: "closed_at", closedat: "closed_at", dateclosed: "closed_at", closeddate: "closed_at", caseclosed: "_closedflag", + // Court info court: "court", judge: "judge", jurisdiction: "jurisdiction", district: "jurisdiction", county: "jurisdiction", countylocation: "jurisdiction", courtinformation: "court", primarycaselocation: "court", - caseCaption: "case_caption", caption: "case_caption", + casecaption: "case_caption", caption: "case_caption", courtcasenumber: "court_case_number", filingdate: "filing_date", nexthearing: "next_hearing_date", nexthearingdate: "next_hearing_date", pretrialconference: "next_hearing_date", trial: "next_hearing_date", + // Opposing opposingparty: "opposing_party", defendants: "opposing_party", plaintiffs: "opposing_party", opposingcounsel: "opposing_counsel", opposingcounsels: "opposing_counsel", opposingcounselfirm: "opposing_counsel_firm", opposingcounselemail: "opposing_counsel_email", opposingcounselphone: "opposing_counsel_phone", - leadattorney: "_assignedname", originatingattorney: "_originatingname", + // Attorneys + leadattorney: "_assignedname", leadattorney1: "_assignedname", + originatingattorney: "_originatingname", + // Money claimamount: "claim_amount", settlementamount: "settlement_amount", outstandingbalance: "claim_amount", casebalance: "claim_amount", ledgerbalance: "claim_amount", @@ -232,15 +244,25 @@ const IMPORTERS: ImporterConfig[] = [ flatfee: "flat_fee_amount", billingtype: "billing_method", soldate: "statute_of_limitations", + // Archive archived: "_archived", isarchived: "_archived", }, numeric: ["claim_amount", "settlement_amount", "default_hourly_rate", "flat_fee_amount"], dateCols: ["opened_at", "closed_at", "filing_date", "next_hearing_date", "statute_of_limitations"], transform: (r, ctx) => { + // Client resolution priority: + // 1. Client number (external id) + // 2. Client / Community Association / Association Name (by name) + // 3. Management Company (only if nothing else matched) const cid = r._clientext ? ctx.clientByExt.get(String(r._clientext)) : null; - const cidByName = r._clientname ? ctx.clientByName.get(String(r._clientname).toLowerCase()) : null; - r.client_id = cid ?? cidByName ?? null; - delete r._clientext; delete r._clientname; + const cidByName = r._clientname + ? ctx.clientByName.get(String(r._clientname).toLowerCase()) + : null; + const cidByMgmt = r._mgmtcompany + ? ctx.clientByName.get(String(r._mgmtcompany).toLowerCase()) + : null; + r.client_id = cid ?? cidByName ?? cidByMgmt ?? null; + delete r._clientext; delete r._clientname; delete r._mgmtcompany; delete r._assignedname; delete r._originatingname; if (!r.client_id) return null; const closedFlag = String(r._closedflag ?? "").toLowerCase().trim(); @@ -265,7 +287,14 @@ const IMPORTERS: ImporterConfig[] = [ const bm = String(r.billing_method).toLowerCase(); r.billing_method = bm.includes("flat") ? "flat" : bm.includes("contingency") ? "contingency" : bm.includes("hour") ? "hourly" : null; } - if (!r.case_number) r.case_number = `CASE-${Date.now()}-${Math.floor(Math.random() * 1000)}`; + // Generate a collision-free case number when none was provided. + // Uses crypto.randomUUID so simultaneous rows in the same batch never collide. + if (!r.case_number || !String(r.case_number).trim()) { + const uid = (typeof crypto !== "undefined" && "randomUUID" in crypto) + ? crypto.randomUUID().slice(0, 8).toUpperCase() + : `${Date.now().toString(36)}${Math.floor(Math.random() * 1e6).toString(36)}`.toUpperCase(); + r.case_number = `CASE-${uid}`; + } if (!r.title) r.title = r.case_number; return r; },