From 1196e2afee589b32a309569b5db0f250ce24ae51 Mon Sep 17 00:00:00 2001 From: Gitead Date: Wed, 6 May 2026 15:12:56 -0400 Subject: [PATCH] feat: add Check MH Payment automation and MH Paid column - Add selenium_MHPaymentCheckWorker.py: logs into MassHealth portal, navigates to Search Claims, enters claim number, extracts totalPaidAmount from results table - Register /mh-payment-check endpoint in Selenium agent - Add mhPaidAmount field to Payment model with migration - Add PATCH /api/payments/:id/mh-payment-check backend route: fetches MH credentials, calls selenium, stores result - Add Claim No. column (MassHealth claim number) as first data column in payments table - Move Payment ID and Claim ID columns to end of table - Add MH Paid column showing mhPaidAmount in green - Wire Check MH Payment button to call API for each selected payment and refresh table Co-Authored-By: Claude Sonnet 4.6 --- apps/Backend/src/routes/payments.ts | 49 ++++ .../payments/payments-recent-table.tsx | 80 +++++-- apps/SeleniumService/agent.py | 29 +++ .../selenium_MHPaymentCheckWorker.py | 218 ++++++++++++++++++ .../migration.sql | 1 + packages/db/prisma/schema.prisma | 1 + 6 files changed, 364 insertions(+), 14 deletions(-) create mode 100644 apps/SeleniumService/selenium_MHPaymentCheckWorker.py create mode 100644 packages/db/prisma/migrations/20260506000000_add_mh_paid_amount/migration.sql diff --git a/apps/Backend/src/routes/payments.ts b/apps/Backend/src/routes/payments.ts index 1a7bc082..2813e0bb 100755 --- a/apps/Backend/src/routes/payments.ts +++ b/apps/Backend/src/routes/payments.ts @@ -15,6 +15,7 @@ import { import { prisma } from "@repo/db/client"; import { PaymentStatusSchema } from "@repo/db/types"; import * as paymentService from "../services/paymentService"; +import { callPythonSync } from "../queue/processors/_shared"; const paymentFilterSchema = z.object({ from: z.string().datetime(), @@ -426,6 +427,54 @@ router.patch( } ); +// PATCH /api/payments/:id/mh-payment-check +router.patch( + "/:id/mh-payment-check", + async (req: Request, res: Response): Promise => { + try { + const userId = req.user?.id; + if (!userId) return res.status(401).json({ message: "Unauthorized" }); + + const paymentId = parseIntOrError(req.params.id, "Payment ID"); + + const payment = await storage.getPaymentById(paymentId); + if (!payment) return res.status(404).json({ message: "Payment not found" }); + + const claimNumber = payment.claim?.claimNumber; + if (!claimNumber) { + return res.status(400).json({ message: "No claim number found for this payment" }); + } + + const credentials = await storage.getInsuranceCredentialByUserAndSiteKey(userId, "MH"); + if (!credentials) { + return res.status(404).json({ + message: "No MassHealth credentials found. Please add them in Settings.", + }); + } + + const seleniumResult = await callPythonSync("/mh-payment-check", { + data: { + massdhpUsername: credentials.username, + massdhpPassword: credentials.password, + claimNumber, + }, + }); + + const mhPaidAmount = seleniumResult?.mhPaidAmount ?? 0; + + const updated = await prisma.payment.update({ + where: { id: paymentId }, + data: { mhPaidAmount, updatedById: userId }, + }); + + return res.json({ ...updated, mhPaidAmount: Number(updated.mhPaidAmount) }); + } catch (err: unknown) { + const message = err instanceof Error ? err.message : "MH payment check failed"; + return res.status(500).json({ message }); + } + } +); + // DELETE /api/payments/:id router.delete("/:id", async (req: Request, res: Response): Promise => { try { diff --git a/apps/Frontend/src/components/payments/payments-recent-table.tsx b/apps/Frontend/src/components/payments/payments-recent-table.tsx index 061258c6..2f195754 100755 --- a/apps/Frontend/src/components/payments/payments-recent-table.tsx +++ b/apps/Frontend/src/components/payments/payments-recent-table.tsx @@ -98,6 +98,7 @@ export default function PaymentsRecentTable({ >(undefined); const [selectedPaymentId, setSelectedPaymentId] = useState(null); const [checkedPaymentIds, setCheckedPaymentIds] = useState>(new Set()); + const [isMhChecking, setIsMhChecking] = useState(false); const [isRevertOpen, setIsRevertOpen] = useState(false); const [revertPaymentId, setRevertPaymentId] = useState(null); @@ -517,11 +518,44 @@ export default function PaymentsRecentTable({