npiProvider - v3
This commit is contained in:
@@ -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"
|
||||||
|
|||||||
@@ -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"]')))
|
||||||
|
|||||||
@@ -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"]')))
|
||||||
|
|||||||
@@ -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[];
|
||||||
|
|||||||
Reference in New Issue
Block a user