feat: DDMA claim submission with OTP, PDF, claim number extraction
- Add full DDMA claim Selenium flow (steps 1-8): search patient, open member page, create claim, fill form, attach files, next, submit, extract claim number and save confirmation PDF - Add fee schedule price-mismatch dialog for all claim buttons (MH, CCA, DDMA, United, Tufts, Save) with optional price update to JSON - Add OTP modal for DDMA claim when session expires, mirroring eligibility OTP flow - Close Chrome after claim submission via quit_driver() (session preserved in profile) - Move Map Price button between Direct Submission and procedure table, right-aligned above Billed Amount column - Add fee-schedule update-price backend route - Add DDMA claim processor with claimNumber/pdf_url result handling Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -2,6 +2,7 @@ import { InputServiceLine } from "@repo/db/types";
|
||||
import Decimal from "decimal.js";
|
||||
import rawCodeTable from "@/assets/data/procedureCodesMH.json";
|
||||
import rawCCACodeTable from "@/assets/data/procedureCodesCCA.json";
|
||||
import rawDDMACodeTable from "@/assets/data/procedureCodesDDMA.json";
|
||||
import { PROCEDURE_COMBOS } from "./procedureCombos";
|
||||
|
||||
/* ----------------------------- Types ----------------------------- */
|
||||
@@ -15,6 +16,7 @@ export type CodeRow = {
|
||||
};
|
||||
const CODE_TABLE = rawCodeTable as CodeRow[];
|
||||
const CCA_CODE_TABLE = rawCCACodeTable as CodeRow[];
|
||||
const DDMA_CODE_TABLE = rawDDMACodeTable as CodeRow[];
|
||||
|
||||
export type ClaimFormLike = {
|
||||
serviceDate: string; // form-level service date
|
||||
@@ -56,9 +58,19 @@ const CCA_CODE_MAP: Map<string, CodeRow> = (() => {
|
||||
return m;
|
||||
})();
|
||||
|
||||
const DDMA_CODE_MAP: Map<string, CodeRow> = (() => {
|
||||
const m = new Map<string, CodeRow>();
|
||||
for (const r of DDMA_CODE_TABLE) {
|
||||
const k = normalizeCode(String(r["Procedure Code"] || ""));
|
||||
if (k && !m.has(k)) m.set(k, r);
|
||||
}
|
||||
return m;
|
||||
})();
|
||||
|
||||
/** Return the correct fee-schedule map for the given insurance type. */
|
||||
function getCodeMap(insuranceSiteKey?: string): Map<string, CodeRow> {
|
||||
if (insuranceSiteKey === "CCA") return CCA_CODE_MAP;
|
||||
if (insuranceSiteKey === "DDMA") return DDMA_CODE_MAP;
|
||||
return CODE_MAP; // default: MassHealth
|
||||
}
|
||||
|
||||
@@ -333,4 +345,43 @@ export function applyComboToForm<T extends ClaimFormLike>(
|
||||
}
|
||||
|
||||
|
||||
export { CODE_MAP, CCA_CODE_MAP, getCodeMap, getPriceForCodeWithAgeFromMap };
|
||||
export { CODE_MAP, CCA_CODE_MAP, DDMA_CODE_MAP, getCodeMap, getPriceForCodeWithAgeFromMap };
|
||||
|
||||
export type PriceMismatch = {
|
||||
procedureCode: string;
|
||||
enteredPrice: number;
|
||||
schedulePrice: number;
|
||||
};
|
||||
|
||||
/** Compare each service line's totalBilled against the fee schedule.
|
||||
* Returns lines where the entered price differs from the schedule price.
|
||||
* Returns empty array if the siteKey has no schedule (United, Tufts, etc.). */
|
||||
export function findPriceMismatches(
|
||||
serviceLines: InputServiceLine[],
|
||||
insuranceSiteKey: string | undefined,
|
||||
patientDOB: string,
|
||||
serviceDate: string,
|
||||
): PriceMismatch[] {
|
||||
const supported = ["MH", "MASSHEALTH", "CCA", "DDMA"];
|
||||
if (!insuranceSiteKey || !supported.includes(insuranceSiteKey.toUpperCase())) return [];
|
||||
|
||||
const map = getCodeMap(insuranceSiteKey);
|
||||
const mismatches: PriceMismatch[] = [];
|
||||
|
||||
for (const line of serviceLines) {
|
||||
const code = normalizeCode(line.procedureCode || "");
|
||||
if (!code) continue;
|
||||
const enteredPrice = new Decimal(Number(line.totalBilled) || 0);
|
||||
if (enteredPrice.isZero()) continue;
|
||||
const age = ageOnDate(patientDOB, serviceDate);
|
||||
const schedulePrice = getPriceForCodeWithAgeFromMap(map, code, age);
|
||||
if (!schedulePrice.isZero() && !enteredPrice.equals(schedulePrice)) {
|
||||
mismatches.push({
|
||||
procedureCode: code,
|
||||
enteredPrice: enteredPrice.toNumber(),
|
||||
schedulePrice: schedulePrice.toNumber(),
|
||||
});
|
||||
}
|
||||
}
|
||||
return mismatches;
|
||||
}
|
||||
Reference in New Issue
Block a user