feat: schedule page UX improvements + CDT combo price skip
- Move Select Procedures above Check Eligibility in appointment right-click menu - Show 3 blank service lines by default when opening Select Procedures with no saved procedures - Fix serviceLines not being preserved when API returns empty procedures list - CDT combo buttons no longer auto-fill price (only fill codes); user maps price via Map Price button - Overlap detection in schedule: shorten earlier appointment display span when a later one starts within its range - Procedures dialog: replace single manual-add row with 3 pre-filled blank rows grid + Add Line / Save Lines buttons Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -31,8 +31,6 @@ import { apiRequest, queryClient } from "@/lib/queryClient";
|
|||||||
import { useToast } from "@/hooks/use-toast";
|
import { useToast } from "@/hooks/use-toast";
|
||||||
import { PROCEDURE_COMBOS } from "@/utils/procedureCombos";
|
import { PROCEDURE_COMBOS } from "@/utils/procedureCombos";
|
||||||
import {
|
import {
|
||||||
CODE_MAP,
|
|
||||||
getPriceForCodeWithAgeFromMap,
|
|
||||||
findPriceMismatches,
|
findPriceMismatches,
|
||||||
type PriceMismatch,
|
type PriceMismatch,
|
||||||
} from "@/utils/procedureCombosMapping";
|
} from "@/utils/procedureCombosMapping";
|
||||||
@@ -67,12 +65,15 @@ export function AppointmentProceduresDialog({
|
|||||||
// NPI provider state — stored per-appointment on the procedure rows
|
// NPI provider state — stored per-appointment on the procedure rows
|
||||||
const [selectedNpiProviderId, setSelectedNpiProviderId] = useState<number | null>(null);
|
const [selectedNpiProviderId, setSelectedNpiProviderId] = useState<number | null>(null);
|
||||||
|
|
||||||
// manual add row state
|
// pending (unsaved) lines — 3 blank rows by default
|
||||||
const [manualCode, setManualCode] = useState("");
|
interface PendingRow { code: string; label: string; fee: string; tooth: string; surface: string; }
|
||||||
const [manualLabel, setManualLabel] = useState("");
|
const emptyRow = (): PendingRow => ({ code: "", label: "", fee: "", tooth: "", surface: "" });
|
||||||
const [manualFee, setManualFee] = useState("");
|
const [pendingRows, setPendingRows] = useState<PendingRow[]>([emptyRow(), emptyRow(), emptyRow()]);
|
||||||
const [manualTooth, setManualTooth] = useState("");
|
|
||||||
const [manualSurface, setManualSurface] = useState("");
|
// reset pending rows when dialog opens
|
||||||
|
useEffect(() => {
|
||||||
|
if (open) setPendingRows([emptyRow(), emptyRow(), emptyRow()]);
|
||||||
|
}, [open]);
|
||||||
|
|
||||||
// inline edit state
|
// inline edit state
|
||||||
const [editingId, setEditingId] = useState<number | null>(null);
|
const [editingId, setEditingId] = useState<number | null>(null);
|
||||||
@@ -180,42 +181,15 @@ export function AppointmentProceduresDialog({
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const addManualMutation = useMutation({
|
|
||||||
mutationFn: async () => {
|
|
||||||
const payload = {
|
|
||||||
appointmentId,
|
|
||||||
patientId,
|
|
||||||
npiProviderId: selectedNpiProviderId ?? null,
|
|
||||||
procedureCode: manualCode,
|
|
||||||
procedureLabel: manualLabel || null,
|
|
||||||
fee: manualFee ? Number(manualFee) : null,
|
|
||||||
toothNumber: manualTooth || null,
|
|
||||||
toothSurface: manualSurface || null,
|
|
||||||
source: "MANUAL",
|
|
||||||
};
|
|
||||||
const res = await apiRequest("POST", "/api/appointment-procedures", payload);
|
|
||||||
if (!res.ok) throw new Error("Failed to add procedure");
|
|
||||||
return res.json();
|
|
||||||
},
|
|
||||||
onSuccess: () => {
|
|
||||||
toast({ title: "Procedure added" });
|
|
||||||
setManualCode(""); setManualLabel(""); setManualFee("");
|
|
||||||
setManualTooth(""); setManualSurface("");
|
|
||||||
queryClient.invalidateQueries({ queryKey: ["appointment-procedures", appointmentId] });
|
|
||||||
},
|
|
||||||
onError: (err: any) => {
|
|
||||||
toast({ title: "Error", description: err.message ?? "Failed to add procedure", variant: "destructive" });
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
const bulkAddMutation = useMutation({
|
const bulkAddMutation = useMutation({
|
||||||
mutationFn: async (rows: any[]) => {
|
mutationFn: async (rows: any[]) => {
|
||||||
const res = await apiRequest("POST", "/api/appointment-procedures/bulk", rows);
|
const res = await apiRequest("POST", "/api/appointment-procedures/bulk", rows);
|
||||||
if (!res.ok) throw new Error("Failed to add combo procedures");
|
if (!res.ok) throw new Error("Failed to add procedures");
|
||||||
return res.json();
|
return res.json();
|
||||||
},
|
},
|
||||||
onSuccess: () => {
|
onSuccess: () => {
|
||||||
toast({ title: "Combo added" });
|
toast({ title: "Procedures saved" });
|
||||||
|
setPendingRows([emptyRow(), emptyRow(), emptyRow()]);
|
||||||
queryClient.invalidateQueries({ queryKey: ["appointment-procedures", appointmentId] });
|
queryClient.invalidateQueries({ queryKey: ["appointment-procedures", appointmentId] });
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
@@ -265,16 +239,7 @@ export function AppointmentProceduresDialog({
|
|||||||
|
|
||||||
const handleAddCombo = (comboKey: string) => {
|
const handleAddCombo = (comboKey: string) => {
|
||||||
const combo = PROCEDURE_COMBOS[comboKey];
|
const combo = PROCEDURE_COMBOS[comboKey];
|
||||||
if (!combo || !patient?.dateOfBirth) return;
|
if (!combo) return;
|
||||||
|
|
||||||
const dob = patient.dateOfBirth;
|
|
||||||
const ref = new Date();
|
|
||||||
const birth = new Date(dob as any);
|
|
||||||
let age = ref.getFullYear() - birth.getFullYear();
|
|
||||||
const hadBirthday =
|
|
||||||
ref.getMonth() > birth.getMonth() ||
|
|
||||||
(ref.getMonth() === birth.getMonth() && ref.getDate() >= birth.getDate());
|
|
||||||
if (!hadBirthday) age -= 1;
|
|
||||||
|
|
||||||
const rows = combo.codes.map((code: string, idx: number) => ({
|
const rows = combo.codes.map((code: string, idx: number) => ({
|
||||||
appointmentId,
|
appointmentId,
|
||||||
@@ -282,7 +247,7 @@ export function AppointmentProceduresDialog({
|
|||||||
npiProviderId: selectedNpiProviderId ?? null,
|
npiProviderId: selectedNpiProviderId ?? null,
|
||||||
procedureCode: code,
|
procedureCode: code,
|
||||||
procedureLabel: combo.label,
|
procedureLabel: combo.label,
|
||||||
fee: getPriceForCodeWithAgeFromMap(CODE_MAP, code, age).toNumber(),
|
fee: 0,
|
||||||
source: "COMBO",
|
source: "COMBO",
|
||||||
comboKey,
|
comboKey,
|
||||||
toothNumber: combo.toothNumbers?.[idx] ?? null,
|
toothNumber: combo.toothNumbers?.[idx] ?? null,
|
||||||
@@ -305,6 +270,24 @@ export function AppointmentProceduresDialog({
|
|||||||
|
|
||||||
const cancelEdit = () => { setEditingId(null); setEditRow({}); };
|
const cancelEdit = () => { setEditingId(null); setEditRow({}); };
|
||||||
|
|
||||||
|
const handleSavePendingRows = () => {
|
||||||
|
const rows = pendingRows
|
||||||
|
.filter((r) => r.code.trim())
|
||||||
|
.map((r) => ({
|
||||||
|
appointmentId,
|
||||||
|
patientId,
|
||||||
|
npiProviderId: selectedNpiProviderId ?? null,
|
||||||
|
procedureCode: r.code.trim().toUpperCase(),
|
||||||
|
procedureLabel: r.label || null,
|
||||||
|
fee: r.fee ? Number(r.fee) : 0,
|
||||||
|
toothNumber: r.tooth || null,
|
||||||
|
toothSurface: r.surface || null,
|
||||||
|
source: "MANUAL",
|
||||||
|
}));
|
||||||
|
if (!rows.length) return;
|
||||||
|
bulkAddMutation.mutate(rows);
|
||||||
|
};
|
||||||
|
|
||||||
const handleDirectClaim = () => {
|
const handleDirectClaim = () => {
|
||||||
setLocation(`/claims?appointmentId=${appointmentId}&mode=direct`);
|
setLocation(`/claims?appointmentId=${appointmentId}&mode=direct`);
|
||||||
onOpenChange(false);
|
onOpenChange(false);
|
||||||
@@ -374,35 +357,62 @@ export function AppointmentProceduresDialog({
|
|||||||
<RegularComboButtons onRegularCombo={handleAddCombo} />
|
<RegularComboButtons onRegularCombo={handleAddCombo} />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* ── Manual Add ─────────────────────────────────────── */}
|
{/* ── Pending Lines ───────────────────────────────────── */}
|
||||||
<div className="mt-8 border rounded-lg p-4 bg-muted/20 space-y-3">
|
<div className="mt-8 border rounded-lg p-4 bg-muted/20 space-y-2">
|
||||||
<div className="font-medium text-sm">Add Manual Procedure</div>
|
<div className="font-medium text-sm mb-3">Add Procedures</div>
|
||||||
<div className="grid grid-cols-1 md:grid-cols-5 gap-3">
|
{/* Column headers */}
|
||||||
<div>
|
<div className="grid grid-cols-[100px_1fr_90px_80px_80px_36px] gap-2 px-1 text-xs font-semibold text-muted-foreground">
|
||||||
<Label>Code</Label>
|
<div>Code</div><div>Label</div><div>Fee</div><div>Tooth</div><div>Surface</div><div />
|
||||||
<Input value={manualCode} onChange={(e) => setManualCode(e.target.value)} placeholder="D0120" />
|
|
||||||
</div>
|
</div>
|
||||||
<div>
|
{pendingRows.map((row, i) => (
|
||||||
<Label>Label</Label>
|
<div key={i} className="grid grid-cols-[100px_1fr_90px_80px_80px_36px] gap-2 items-center">
|
||||||
<Input value={manualLabel} onChange={(e) => setManualLabel(e.target.value)} placeholder="Exam" />
|
<Input
|
||||||
|
placeholder="D0120"
|
||||||
|
value={row.code}
|
||||||
|
onChange={(e) => setPendingRows((prev) => prev.map((r, idx) => idx === i ? { ...r, code: e.target.value } : r))}
|
||||||
|
/>
|
||||||
|
<Input
|
||||||
|
placeholder="Exam"
|
||||||
|
value={row.label}
|
||||||
|
onChange={(e) => setPendingRows((prev) => prev.map((r, idx) => idx === i ? { ...r, label: e.target.value } : r))}
|
||||||
|
/>
|
||||||
|
<Input
|
||||||
|
type="number"
|
||||||
|
placeholder="0.00"
|
||||||
|
value={row.fee}
|
||||||
|
onChange={(e) => setPendingRows((prev) => prev.map((r, idx) => idx === i ? { ...r, fee: e.target.value } : r))}
|
||||||
|
/>
|
||||||
|
<Input
|
||||||
|
placeholder="14"
|
||||||
|
value={row.tooth}
|
||||||
|
onChange={(e) => setPendingRows((prev) => prev.map((r, idx) => idx === i ? { ...r, tooth: e.target.value } : r))}
|
||||||
|
/>
|
||||||
|
<Input
|
||||||
|
placeholder="MO"
|
||||||
|
value={row.surface}
|
||||||
|
onChange={(e) => setPendingRows((prev) => prev.map((r, idx) => idx === i ? { ...r, surface: e.target.value } : r))}
|
||||||
|
/>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className="p-1 rounded hover:bg-red-50"
|
||||||
|
onClick={() => setPendingRows((prev) => prev.filter((_, idx) => idx !== i))}
|
||||||
|
>
|
||||||
|
<Trash2 className="h-4 w-4 text-red-400 hover:text-red-600" />
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
))}
|
||||||
<Label>Fee</Label>
|
<div className="flex justify-between items-center pt-2">
|
||||||
<Input value={manualFee} onChange={(e) => setManualFee(e.target.value)} placeholder="100" type="number" />
|
<Button size="sm" variant="outline" onClick={() => setPendingRows((prev) => [...prev, emptyRow()])}>
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<Label>Tooth</Label>
|
|
||||||
<Input value={manualTooth} onChange={(e) => setManualTooth(e.target.value)} placeholder="14" />
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<Label>Surface</Label>
|
|
||||||
<Input value={manualSurface} onChange={(e) => setManualSurface(e.target.value)} placeholder="MO" />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="flex justify-end">
|
|
||||||
<Button size="sm" onClick={() => runWithPriceCheck(manualCode, Number(manualFee), () => addManualMutation.mutate())} disabled={!manualCode || addManualMutation.isPending}>
|
|
||||||
<Plus className="h-4 w-4 mr-1" />
|
<Plus className="h-4 w-4 mr-1" />
|
||||||
Add Procedure
|
Add Line
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
size="sm"
|
||||||
|
onClick={handleSavePendingRows}
|
||||||
|
disabled={!pendingRows.some((r) => r.code.trim()) || bulkAddMutation.isPending}
|
||||||
|
>
|
||||||
|
<Save className="h-4 w-4 mr-1" />
|
||||||
|
Save Lines
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -456,7 +456,7 @@ export function ClaimForm({
|
|||||||
|
|
||||||
setForm((prev) => ({
|
setForm((prev) => ({
|
||||||
...prev,
|
...prev,
|
||||||
serviceLines: mappedLines,
|
serviceLines: mappedLines.length > 0 ? mappedLines : prev.serviceLines,
|
||||||
...(data.appointmentFiles?.length
|
...(data.appointmentFiles?.length
|
||||||
? { claimFiles: data.appointmentFiles }
|
? { claimFiles: data.appointmentFiles }
|
||||||
: {}),
|
: {}),
|
||||||
@@ -630,7 +630,7 @@ export function ClaimForm({
|
|||||||
insuranceProvider: "",
|
insuranceProvider: "",
|
||||||
insuranceSiteKey: "",
|
insuranceSiteKey: "",
|
||||||
status: "PENDING",
|
status: "PENDING",
|
||||||
serviceLines: Array.from({ length: 10 }, () => ({
|
serviceLines: Array.from({ length: proceduresOnly ? 3 : 10 }, () => ({
|
||||||
procedureCode: "",
|
procedureCode: "",
|
||||||
procedureDate: serviceDate,
|
procedureDate: serviceDate,
|
||||||
quad: "",
|
quad: "",
|
||||||
@@ -1876,7 +1876,7 @@ export function ClaimForm({
|
|||||||
prev,
|
prev,
|
||||||
comboKey as any,
|
comboKey as any,
|
||||||
patient?.dateOfBirth ?? "",
|
patient?.dateOfBirth ?? "",
|
||||||
{ replaceAll: false, lineDate: prev.serviceDate },
|
{ replaceAll: false, lineDate: prev.serviceDate, skipPrice: true },
|
||||||
prev.insuranceSiteKey,
|
prev.insuranceSiteKey,
|
||||||
);
|
);
|
||||||
setTimeout(() => scrollToLine(0), 0);
|
setTimeout(() => scrollToLine(0), 0);
|
||||||
@@ -2116,7 +2116,7 @@ export function ClaimForm({
|
|||||||
prev,
|
prev,
|
||||||
comboKey as any,
|
comboKey as any,
|
||||||
patient?.dateOfBirth ?? "",
|
patient?.dateOfBirth ?? "",
|
||||||
{ replaceAll: false, lineDate: prev.serviceDate },
|
{ replaceAll: false, lineDate: prev.serviceDate, skipPrice: true },
|
||||||
);
|
);
|
||||||
setTimeout(() => scrollToLine(0), 0);
|
setTimeout(() => scrollToLine(0), 0);
|
||||||
return next;
|
return next;
|
||||||
@@ -2679,7 +2679,7 @@ export function ClaimForm({
|
|||||||
prev,
|
prev,
|
||||||
comboKey as any,
|
comboKey as any,
|
||||||
patient?.dateOfBirth ?? "",
|
patient?.dateOfBirth ?? "",
|
||||||
{ replaceAll: false, lineDate: prev.serviceDate },
|
{ replaceAll: false, lineDate: prev.serviceDate, skipPrice: true },
|
||||||
);
|
);
|
||||||
setTimeout(() => scrollToLine(0), 0);
|
setTimeout(() => scrollToLine(0), 0);
|
||||||
return next;
|
return next;
|
||||||
|
|||||||
@@ -597,10 +597,40 @@ export default function AppointmentsPage() {
|
|||||||
return Math.max(1, Math.round(diff / 15));
|
return Math.max(1, Math.round(diff / 15));
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Compute display span — same as getSlotSpan but truncated if a later appointment in the
|
||||||
|
// same staff column starts within this appointment's time range (overlap case).
|
||||||
|
const getDisplaySpan = (apt: ScheduledAppointment): number => {
|
||||||
|
const fullSpan = getSlotSpan(apt);
|
||||||
|
const startStr = (typeof apt.startTime === "string" ? apt.startTime : formatLocalTime(apt.startTime)).substring(0, 5);
|
||||||
|
const [startH, startM] = startStr.split(":").map(Number);
|
||||||
|
const startMinutes = (startH ?? 0) * 60 + (startM ?? 0);
|
||||||
|
|
||||||
|
const nextOverlap = (processedAppointments ?? [])
|
||||||
|
.filter((other) => {
|
||||||
|
if (other.id === apt.id || other.staffId !== apt.staffId) return false;
|
||||||
|
const otherStart = (typeof other.startTime === "string" ? other.startTime : formatLocalTime(other.startTime)).substring(0, 5);
|
||||||
|
const [oh, om] = otherStart.split(":").map(Number);
|
||||||
|
const otherMin = (oh ?? 0) * 60 + (om ?? 0);
|
||||||
|
return otherMin > startMinutes && otherMin < startMinutes + fullSpan * 15;
|
||||||
|
})
|
||||||
|
.sort((a, b) => {
|
||||||
|
const aStart = (typeof a.startTime === "string" ? a.startTime : formatLocalTime(a.startTime)).substring(0, 5);
|
||||||
|
const bStart = (typeof b.startTime === "string" ? b.startTime : formatLocalTime(b.startTime)).substring(0, 5);
|
||||||
|
return aStart.localeCompare(bStart);
|
||||||
|
})[0];
|
||||||
|
|
||||||
|
if (!nextOverlap) return fullSpan;
|
||||||
|
|
||||||
|
const nextStart = (typeof nextOverlap.startTime === "string" ? nextOverlap.startTime : formatLocalTime(nextOverlap.startTime)).substring(0, 5);
|
||||||
|
const [nh, nm] = nextStart.split(":").map(Number);
|
||||||
|
const nextMin = (nh ?? 0) * 60 + (nm ?? 0);
|
||||||
|
return Math.max(1, Math.round((nextMin - startMinutes) / 15));
|
||||||
|
};
|
||||||
|
|
||||||
// Slots that are "continued" rows of a multi-slot appointment (should not render a td)
|
// Slots that are "continued" rows of a multi-slot appointment (should not render a td)
|
||||||
const coveredSlots = new Set<string>();
|
const coveredSlots = new Set<string>();
|
||||||
(processedAppointments ?? []).forEach((apt) => {
|
(processedAppointments ?? []).forEach((apt) => {
|
||||||
const span = getSlotSpan(apt);
|
const span = getDisplaySpan(apt);
|
||||||
if (span <= 1) return;
|
if (span <= 1) return;
|
||||||
const startStr = (typeof apt.startTime === "string" ? apt.startTime : formatLocalTime(apt.startTime)).substring(0, 5);
|
const startStr = (typeof apt.startTime === "string" ? apt.startTime : formatLocalTime(apt.startTime)).substring(0, 5);
|
||||||
const [startH, startM] = startStr.split(":").map(Number);
|
const [startH, startM] = startStr.split(":").map(Number);
|
||||||
@@ -1556,16 +1586,6 @@ export default function AppointmentsPage() {
|
|||||||
</span>
|
</span>
|
||||||
</Item>
|
</Item>
|
||||||
|
|
||||||
{/* Check Eligibility */}
|
|
||||||
<Item
|
|
||||||
onClick={({ props }) => handleCheckEligibility(props.appointmentId)}
|
|
||||||
>
|
|
||||||
<span className="flex items-center gap-2">
|
|
||||||
<Shield className="h-4 w-4" />
|
|
||||||
Check Eligibility
|
|
||||||
</span>
|
|
||||||
</Item>
|
|
||||||
|
|
||||||
{/* Select Procedures */}
|
{/* Select Procedures */}
|
||||||
<Item
|
<Item
|
||||||
onClick={({ props }) => handleSelectProcedures(props.appointmentId)}
|
onClick={({ props }) => handleSelectProcedures(props.appointmentId)}
|
||||||
@@ -1576,6 +1596,16 @@ export default function AppointmentsPage() {
|
|||||||
</span>
|
</span>
|
||||||
</Item>
|
</Item>
|
||||||
|
|
||||||
|
{/* Check Eligibility */}
|
||||||
|
<Item
|
||||||
|
onClick={({ props }) => handleCheckEligibility(props.appointmentId)}
|
||||||
|
>
|
||||||
|
<span className="flex items-center gap-2">
|
||||||
|
<Shield className="h-4 w-4" />
|
||||||
|
Check Eligibility
|
||||||
|
</span>
|
||||||
|
</Item>
|
||||||
|
|
||||||
{/* Claims / PreAuth */}
|
{/* Claims / PreAuth */}
|
||||||
<Item
|
<Item
|
||||||
onClick={({ props }) => handleClaimsPreAuth(props.appointmentId)}
|
onClick={({ props }) => handleClaimsPreAuth(props.appointmentId)}
|
||||||
@@ -1806,7 +1836,7 @@ export default function AppointmentsPage() {
|
|||||||
{staffMembers.map((staff, staffIndex) => {
|
{staffMembers.map((staff, staffIndex) => {
|
||||||
if (coveredSlots.has(`${timeSlot.time}-${staff.id}`)) return null;
|
if (coveredSlots.has(`${timeSlot.time}-${staff.id}`)) return null;
|
||||||
const apt = getAppointmentAtSlot(timeSlot, Number(staff.id));
|
const apt = getAppointmentAtSlot(timeSlot, Number(staff.id));
|
||||||
const span = apt ? getSlotSpan(apt) : 1;
|
const span = apt ? getDisplaySpan(apt) : 1;
|
||||||
return (
|
return (
|
||||||
<DroppableTimeSlot
|
<DroppableTimeSlot
|
||||||
key={`${timeSlot.time}-${staff.id}`}
|
key={`${timeSlot.time}-${staff.id}`}
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ export type ApplyOptions = {
|
|||||||
lineDate?: string;
|
lineDate?: string;
|
||||||
clearTrailing?: boolean;
|
clearTrailing?: boolean;
|
||||||
replaceAll?: boolean;
|
replaceAll?: boolean;
|
||||||
|
skipPrice?: boolean;
|
||||||
};
|
};
|
||||||
/* ----------------------------- Helpers ----------------------------- */
|
/* ----------------------------- Helpers ----------------------------- */
|
||||||
|
|
||||||
@@ -329,9 +330,8 @@ export function applyComboToForm<T extends ClaimFormLike>(
|
|||||||
// Make sure we have enough rows for the whole combo
|
// Make sure we have enough rows for the whole combo
|
||||||
ensureCapacity(next.serviceLines, insertAt + preset.codes.length, lineDate);
|
ensureCapacity(next.serviceLines, insertAt + preset.codes.length, lineDate);
|
||||||
|
|
||||||
// Age on the specific line date we will set
|
const age = options.skipPrice ? 0 : ageOnDate(patientDOB, lineDate);
|
||||||
const age = ageOnDate(patientDOB, lineDate);
|
const map = options.skipPrice ? CODE_MAP : getCodeMap(insuranceSiteKey);
|
||||||
const map = getCodeMap(insuranceSiteKey);
|
|
||||||
|
|
||||||
for (let j = 0; j < preset.codes.length; j++) {
|
for (let j = 0; j < preset.codes.length; j++) {
|
||||||
const i = insertAt + j;
|
const i = insertAt + j;
|
||||||
@@ -340,7 +340,9 @@ export function applyComboToForm<T extends ClaimFormLike>(
|
|||||||
const codeRaw = preset.codes[j];
|
const codeRaw = preset.codes[j];
|
||||||
if (!codeRaw) continue;
|
if (!codeRaw) continue;
|
||||||
const code = normalizeCode(codeRaw);
|
const code = normalizeCode(codeRaw);
|
||||||
const price = getPriceForCodeWithAgeFromMap(map, code, age);
|
const price = options.skipPrice
|
||||||
|
? new Decimal(0)
|
||||||
|
: getPriceForCodeWithAgeFromMap(map, code, age);
|
||||||
|
|
||||||
const original = next.serviceLines[i];
|
const original = next.serviceLines[i];
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user