npiProvider - v3

This commit is contained in:
2026-01-23 10:02:26 +05:30
parent eca21f398c
commit c2167a65dd
4 changed files with 151 additions and 2 deletions

View File

@@ -39,6 +39,7 @@ import {
InputServiceLine, InputServiceLine,
InsertAppointment, InsertAppointment,
MissingTeethStatus, MissingTeethStatus,
NpiProvider,
Patient, Patient,
Staff, Staff,
UpdateAppointment, UpdateAppointment,
@@ -134,6 +135,17 @@ export function ClaimForm({
} }
}, [staffMembersRaw, staff]); }, [staffMembersRaw, staff]);
// fetching npi providers
const { data: npiProviders = [] } = useQuery<NpiProvider[]>({
queryKey: ["/api/npiProviders/"],
queryFn: async () => {
const res = await apiRequest("GET", "/api/npiProviders/");
if (!res.ok) throw new Error("Failed to fetch NPI providers");
return res.json();
},
});
// Service date state // Service date state
const [serviceDateValue, setServiceDateValue] = useState<Date>(new Date()); const [serviceDateValue, setServiceDateValue] = useState<Date>(new Date());
const [serviceDate, setServiceDate] = useState<string>( const [serviceDate, setServiceDate] = useState<string>(
@@ -550,6 +562,16 @@ export function ClaimForm({
return; return;
} }
if (!f.npiProvider?.npiNumber) {
toast({
title: "NPI Provider Required",
description: "Please select a NPI Provider.",
variant: "destructive",
});
return;
}
// 1. Create or update appointment // 1. Create or update appointment
let appointmentIdToUse = appointmentId; let appointmentIdToUse = appointmentId;
@@ -611,6 +633,7 @@ export function ClaimForm({
dateOfBirth: toMMDDYYYY(f.dateOfBirth), dateOfBirth: toMMDDYYYY(f.dateOfBirth),
serviceLines: filteredServiceLines, serviceLines: filteredServiceLines,
staffId: Number(staff?.id), staffId: Number(staff?.id),
npiProvider: f.npiProvider,
patientId: patientId, patientId: patientId,
insuranceProvider: "Mass Health", insuranceProvider: "Mass Health",
appointmentId: appointmentIdToUse!, appointmentId: appointmentIdToUse!,
@@ -659,6 +682,16 @@ export function ClaimForm({
return; return;
} }
if (!f.npiProvider?.npiNumber) {
toast({
title: "NPI Provider Required",
description: "Please select a NPI Provider.",
variant: "destructive",
});
return;
}
// 2. Update patient // 2. Update patient
if (patient && typeof patient.id === "number") { if (patient && typeof patient.id === "number") {
const { id, createdAt, userId, ...sanitizedFields } = patient; const { id, createdAt, userId, ...sanitizedFields } = patient;
@@ -682,6 +715,7 @@ export function ClaimForm({
dateOfBirth: toMMDDYYYY(f.dateOfBirth), dateOfBirth: toMMDDYYYY(f.dateOfBirth),
serviceLines: filteredServiceLines, serviceLines: filteredServiceLines,
staffId: Number(staff?.id), staffId: Number(staff?.id),
npiProvider: f.npiProvider,
patientId: patientId, patientId: patientId,
insuranceProvider: "Mass Health", insuranceProvider: "Mass Health",
insuranceSiteKey: "MH", insuranceSiteKey: "MH",
@@ -1000,6 +1034,41 @@ export function ClaimForm({
</SelectContent> </SelectContent>
</Select> </Select>
{/* Rendering Npi Provider */}
<Label className="flex items-center ml-2">
Rendering Provider
</Label>
<Select
value={form.npiProvider?.npiNumber || ""}
onValueChange={(npiNumber) => {
const selected = npiProviders.find(
(p) => p.npiNumber === npiNumber,
);
if (!selected) return;
setForm((prev) => ({
...prev,
renderingNpi: {
npiNumber: selected.npiNumber,
providerName: selected.providerName,
},
}));
}}
>
<SelectTrigger className="w-56">
<SelectValue placeholder="Select NPI Provider" />
</SelectTrigger>
<SelectContent>
{npiProviders.map((p) => (
<SelectItem key={p.id} value={p.npiNumber}>
{p.npiNumber} {p.providerName}
</SelectItem>
))}
</SelectContent>
</Select>
{/* Map Price Button */} {/* Map Price Button */}
<Button <Button
className="ml-4" className="ml-4"

View File

@@ -27,6 +27,9 @@ class AutomationMassHealth:
self.remarks = self.claim.get("remarks", "") self.remarks = self.claim.get("remarks", "")
self.massdhp_username = self.claim.get("massdhpUsername", "") self.massdhp_username = self.claim.get("massdhpUsername", "")
self.massdhp_password = self.claim.get("massdhpPassword", "") self.massdhp_password = self.claim.get("massdhpPassword", "")
self.npiProvider = self.claim.get("npiProvider", {})
self.npiNumber = self.npiProvider.get("npiNumber", "")
self.npiName = self.npiProvider.get("providerName", "")
self.serviceLines = self.claim.get("serviceLines", []) self.serviceLines = self.claim.get("serviceLines", [])
self.missingTeethStatus = self.claim.get("missingTeethStatus", "") self.missingTeethStatus = self.claim.get("missingTeethStatus", "")
self.missingTeeth = self.claim.get("missingTeeth", {}) self.missingTeeth = self.claim.get("missingTeeth", {})
@@ -40,6 +43,39 @@ class AutomationMassHealth:
driver = webdriver.Chrome(service=s, options=options) driver = webdriver.Chrome(service=s, options=options)
self.driver = driver self.driver = driver
def select_rendering_npi(self, select_element):
options = select_element.options
target_npi = (self.renderingNpiNumber or "").strip()
target_name = (self.renderingNpiName or "").strip().lower()
# 1⃣ Exact NPI match (value or text)
for opt in options:
value = (opt.get_attribute("value") or "").strip()
text = (opt.text or "").lower()
if target_npi and (
value == target_npi or target_npi in text
):
opt.click()
return True
# 2⃣ Name match fallback
for opt in options:
text = (opt.text or "").lower()
if target_name and target_name in text:
opt.click()
return True
# 3⃣ Last fallback → first valid option
for opt in options:
if opt.get_attribute("value"):
opt.click()
return True
raise Exception("No valid Rendering Provider NPI found")
def login(self): def login(self):
wait = WebDriverWait(self.driver, 30) wait = WebDriverWait(self.driver, 30)
@@ -98,7 +134,7 @@ class AutomationMassHealth:
# Rendering Provider NPI dropdown # Rendering Provider NPI dropdown
npi_dropdown = wait.until(EC.presence_of_element_located((By.XPATH, '//*[@id="Select1"]'))) npi_dropdown = wait.until(EC.presence_of_element_located((By.XPATH, '//*[@id="Select1"]')))
select_npi = Select(npi_dropdown) select_npi = Select(npi_dropdown)
select_npi.select_by_index(1) self.select_rendering_npi(select_npi)
# Office Location dropdown # Office Location dropdown
location_dropdown = wait.until(EC.presence_of_element_located((By.XPATH, '//*[@id="Select2"]'))) location_dropdown = wait.until(EC.presence_of_element_located((By.XPATH, '//*[@id="Select2"]')))

View File

@@ -26,6 +26,9 @@ class AutomationMassHealthPreAuth:
self.remarks = self.claim.get("remarks", "") self.remarks = self.claim.get("remarks", "")
self.massdhp_username = self.claim.get("massdhpUsername", "") self.massdhp_username = self.claim.get("massdhpUsername", "")
self.massdhp_password = self.claim.get("massdhpPassword", "") self.massdhp_password = self.claim.get("massdhpPassword", "")
self.npiProvider = self.claim.get("npiProvider", {})
self.npiNumber = self.npiProvider.get("npiNumber", "")
self.npiName = self.npiProvider.get("providerName", "")
self.serviceLines = self.claim.get("serviceLines", []) self.serviceLines = self.claim.get("serviceLines", [])
self.missingTeethStatus = self.claim.get("missingTeethStatus", "") self.missingTeethStatus = self.claim.get("missingTeethStatus", "")
self.missingTeeth = self.claim.get("missingTeeth", {}) self.missingTeeth = self.claim.get("missingTeeth", {})
@@ -39,6 +42,39 @@ class AutomationMassHealthPreAuth:
driver = webdriver.Chrome(service=s, options=options) driver = webdriver.Chrome(service=s, options=options)
self.driver = driver self.driver = driver
def select_rendering_npi(self, select_element):
options = select_element.options
target_npi = (self.renderingNpiNumber or "").strip()
target_name = (self.renderingNpiName or "").strip().lower()
# 1⃣ Exact NPI match (value or text)
for opt in options:
value = (opt.get_attribute("value") or "").strip()
text = (opt.text or "").lower()
if target_npi and (
value == target_npi or target_npi in text
):
opt.click()
return True
# 2⃣ Name match fallback
for opt in options:
text = (opt.text or "").lower()
if target_name and target_name in text:
opt.click()
return True
# 3⃣ Last fallback → first valid option
for opt in options:
if opt.get_attribute("value"):
opt.click()
return True
raise Exception("No valid Rendering Provider NPI found")
def login(self): def login(self):
wait = WebDriverWait(self.driver, 30) wait = WebDriverWait(self.driver, 30)
@@ -97,7 +133,7 @@ class AutomationMassHealthPreAuth:
# Rendering Provider NPI dropdown # Rendering Provider NPI dropdown
npi_dropdown = wait.until(EC.presence_of_element_located((By.XPATH, '//*[@id="Select1"]'))) npi_dropdown = wait.until(EC.presence_of_element_located((By.XPATH, '//*[@id="Select1"]')))
select_npi = Select(npi_dropdown) select_npi = Select(npi_dropdown)
select_npi.select_by_index(1) self.select_rendering_npi(select_npi)
# Office Location dropdown # Office Location dropdown
location_dropdown = wait.until(EC.presence_of_element_located((By.XPATH, '//*[@id="Select2"]'))) location_dropdown = wait.until(EC.presence_of_element_located((By.XPATH, '//*[@id="Select2"]')))

View File

@@ -99,6 +99,10 @@ export interface ClaimFormData {
serviceDate: string; // YYYY-MM-DD serviceDate: string; // YYYY-MM-DD
insuranceProvider: string; insuranceProvider: string;
insuranceSiteKey?: string; insuranceSiteKey?: string;
npiProvider?: {
npiNumber: string;
providerName: string;
};
status: string; // default "pending" status: string; // default "pending"
serviceLines: InputServiceLine[]; serviceLines: InputServiceLine[];
claimId?: number; claimId?: number;
@@ -118,6 +122,10 @@ export interface ClaimPreAuthData {
serviceDate: string; // YYYY-MM-DD serviceDate: string; // YYYY-MM-DD
insuranceProvider: string; insuranceProvider: string;
insuranceSiteKey?: string; insuranceSiteKey?: string;
npiProvider?: {
npiNumber: string;
providerName: string;
};
status: string; // default "pending" status: string; // default "pending"
serviceLines: InputServiceLine[]; serviceLines: InputServiceLine[];
claimFiles?: ClaimFileMeta[]; claimFiles?: ClaimFileMeta[];