feat: MetLife Dental claim submission via ClaimConnect; MetLife fee schedule
Reuses the MetLife eligibility login (persistent profile, email MFA, OTP from app or browser), then Submit Claims -> ClaimConnect -> DentalXChange Single Claim: billing/rendering provider, member ID, last name, payer Metlife 65978, patient picked by DOB/name, signature on file Yes, one claim item per CDT code (date, code, tooth, surfaces, fee; Add Claim Item past 3), Finish, Submit Claim, then Back to Claim to read the Claim ID and save the page as PDF. Waits out Wicket ajax/veil before each click. MetLife now has its own fee schedule (seeded from DDMA) instead of falling back to MassHealth prices. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -33,6 +33,7 @@ import helpers_tuftssco_preauth as htuftssco_preauth
|
||||
import helpers_uniteducard_claim as huniteducard_claim
|
||||
import helpers_bcbs_ma_eligibility as hbcbs_ma
|
||||
import helpers_metlife_eligibility as hmetlife
|
||||
import helpers_metlife_claim as hmetlife_claim
|
||||
|
||||
# Import startup session-clear functions
|
||||
from ddma_browser_manager import clear_ddma_session_on_startup
|
||||
@@ -503,6 +504,47 @@ async def metlife_eligibility(request: Request):
|
||||
return {"status": "started", "session_id": sid}
|
||||
|
||||
|
||||
async def _metlife_claim_worker_wrapper(sid: str, data: dict, url: str):
|
||||
"""Background worker for MetLife claim submission."""
|
||||
global active_jobs, waiting_jobs
|
||||
async with semaphore:
|
||||
async with lock:
|
||||
waiting_jobs -= 1
|
||||
active_jobs += 1
|
||||
try:
|
||||
await hmetlife_claim.start_metlife_claim_run(sid, data, url)
|
||||
finally:
|
||||
async with lock:
|
||||
active_jobs -= 1
|
||||
|
||||
|
||||
@app.post("/metlife-claim")
|
||||
async def metlife_claim(request: Request):
|
||||
"""
|
||||
Starts a MetLife claim submission session in the background.
|
||||
Reuses the MetLife eligibility persistent browser/login.
|
||||
Body: { "claim": { "metlifeUsername": "...", "metlifePassword": "...", ... } }
|
||||
Returns: { status: "started", session_id: "<uuid>" }
|
||||
"""
|
||||
global waiting_jobs
|
||||
|
||||
body = await request.json()
|
||||
|
||||
sid = hmetlife_claim.make_session_entry()
|
||||
hmetlife_claim.sessions[sid]["type"] = "metlife_claim"
|
||||
hmetlife_claim.sessions[sid]["last_activity"] = time.time()
|
||||
|
||||
async with lock:
|
||||
waiting_jobs += 1
|
||||
|
||||
asyncio.create_task(_metlife_claim_worker_wrapper(
|
||||
sid, body,
|
||||
url="https://dentalprovider.metlife.com/home"
|
||||
))
|
||||
|
||||
return {"status": "started", "session_id": sid}
|
||||
|
||||
|
||||
async def _deltaindiv_worker_wrapper(sid: str, data: dict, url: str):
|
||||
"""Background worker for Delta Indiv eligibility — acquires semaphore, updates counters."""
|
||||
global active_jobs, waiting_jobs
|
||||
@@ -1208,6 +1250,8 @@ async def submit_otp(request: Request):
|
||||
res = hbcbs_ma.submit_otp(sid, otp)
|
||||
elif sid in hmetlife.sessions:
|
||||
res = hmetlife.submit_otp(sid, otp)
|
||||
elif sid in hmetlife_claim.sessions:
|
||||
res = hmetlife_claim.submit_otp(sid, otp)
|
||||
else:
|
||||
raise HTTPException(status_code=404, detail="session not found")
|
||||
|
||||
@@ -1255,6 +1299,8 @@ async def session_status(sid: str):
|
||||
s = hbcbs_ma.get_session_status(sid)
|
||||
elif sid in hmetlife.sessions:
|
||||
s = hmetlife.get_session_status(sid)
|
||||
elif sid in hmetlife_claim.sessions:
|
||||
s = hmetlife_claim.get_session_status(sid)
|
||||
else:
|
||||
s = {"status": "not_found"}
|
||||
if s.get("status") == "not_found":
|
||||
|
||||
Reference in New Issue
Block a user