diff --git a/apps/Backend/src/routes/documents.ts b/apps/Backend/src/routes/documents.ts index 239f376..f72d4c1 100644 --- a/apps/Backend/src/routes/documents.ts +++ b/apps/Backend/src/routes/documents.ts @@ -7,107 +7,217 @@ const upload = multer({ storage: multer.memoryStorage() }); const router = Router(); -router.post("/claim-pdf/upload", upload.single("file"), async (req: Request, res: Response): Promise => { - try { - const { patientId, claimId } = req.body; - const file = req.file; +// ----------- PDF GROUPS ------------------ +router.post( + "/pdf-groups", + async (req: Request, res: Response): Promise => { + try { + const { title, category, patientId } = req.body; + if (!title || !category || !patientId) { + return res + .status(400) + .json({ error: "Missing title, category, or patientId" }); + } - if (!patientId || !claimId) { - return res.status(400).json({ error: "Missing patientId, or claimId" }); + const group = await storage.createPdfGroup( + parseInt(patientId), + title, + category + ); + + res.json(group); + } catch (err) { + console.error("Error creating PDF group:", err); + res.status(500).json({ error: "Internal server error" }); } - - if (!file){ - return res.status(400).json({ error: "Missing file" }); - } - - const created = await storage.createClaimPdf( - parseInt(patientId), - parseInt(claimId), - file.originalname, - file.buffer - ); - - res.json(created); - } catch (err) { - console.error("Error uploading claim PDF:", err); - res.status(500).json({ error: "Internal server error" }); } -}); - -router.get("/claim-pdf/recent", async (req: Request, res: Response): Promise => { - try { - const limit = parseInt(req.query.limit as string) || 5; - const offset = parseInt(req.query.offset as string) || 0; - - const recent = await storage.getRecentClaimPdfs(limit, offset); - res.json(recent); - } catch (err) { - console.error("Error fetching recent PDFs:", err); - res.status(500).json({ error: "Internal server error" }); - } -}); - -router.get("/claim-pdf/:id", async (req: Request, res: Response): Promise => { - try { - const idParam = req.params.id; - if (!idParam) return res.status(400).json({ error: "Missing ID" }); - - const id = parseInt(idParam); - const pdf = await storage.getClaimPdfById(id); - - if (!pdf || !pdf.pdfData) return res.status(404).json({ error: "PDF not found" }); - - // Fix bad objectified Buffer - if (!Buffer.isBuffer(pdf.pdfData)) { - pdf.pdfData = Buffer.from(Object.values(pdf.pdfData)); - } - res.setHeader("Content-Type", "application/pdf"); - res.setHeader( - "Content-Disposition", - `attachment; filename="${pdf.filename}"; filename*=UTF-8''${encodeURIComponent(pdf.filename)}` ); - res.send(pdf.pdfData); - } catch (err) { - console.error("Error fetching PDF by ID:", err); - res.status(500).json({ error: "Internal server error" }); - } -}); -router.delete("/claim-pdf/:id", async (req: Request, res: Response): Promise => { +router.get( + "/pdf-groups/:id", + async (req: Request, res: Response): Promise => { + try { + const idParam = req.params.id; + if (!idParam) { + return res.status(400).json({ error: "Missing ID" }); + } + const id = parseInt(idParam); + const group = await storage.getPdfGroupById(id); + if (!group) return res.status(404).json({ error: "Group not found" }); + res.json(group); + } catch (err) { + console.error("Error fetching PDF group:", err); + res.status(500).json({ error: "Internal server error" }); + } + } +); + +router.get("/pdf-groups", async (req: Request, res: Response): Promise => { try { - const idParam = req.params.id; - if (!idParam) return res.status(400).json({ error: "Missing ID" }); - - const id = parseInt(idParam); - const success = await storage.deleteClaimPdf(id); - - res.json({ success }); + const groups = await storage.getAllPdfGroups(); + res.json(groups); } catch (err) { - console.error("Error deleting PDF:", err); + console.error("Error listing PDF groups:", err); res.status(500).json({ error: "Internal server error" }); } }); -router.put("/claim-pdf/:id", upload.single("file"), async (req: Request, res: Response): Promise => { - try { - const idParam = req.params.id; - if (!idParam) return res.status(400).json({ error: "Missing ID" }); - - const id = parseInt(idParam); - const file = req.file; - - const updated = await storage.updateClaimPdf(id, { - filename: file?.originalname, - pdfData: file?.buffer, - }); - - if (!updated) return res.status(404).json({ error: "PDF not found or update failed" }); - - res.json(updated); - } catch (err) { - console.error("Error updating PDF:", err); - res.status(500).json({ error: "Internal server error" }); +router.put( + "/pdf-groups/:id", + async (req: Request, res: Response): Promise => { + try { + const idParam = req.params.id; + if (!idParam) { + return res.status(400).json({ error: "Missing ID" }); + } + const id = parseInt(idParam); + const { title, category } = req.body; + const updated = await storage.updatePdfGroup(id, { title, category }); + if (!updated) return res.status(404).json({ error: "Group not found" }); + res.json(updated); + } catch (err) { + console.error("Error updating PDF group:", err); + res.status(500).json({ error: "Internal server error" }); + } } -}); +); + +router.delete( + "/pdf-groups/:id", + async (req: Request, res: Response): Promise => { + try { + const idParam = req.params.id; + if (!idParam) { + return res.status(400).json({ error: "Missing ID" }); + } + const id = parseInt(idParam); + const success = await storage.deletePdfGroup(id); + res.json({ success }); + } catch (err) { + console.error("Error deleting PDF group:", err); + res.status(500).json({ error: "Internal server error" }); + } + } +); + +// ----------- PDF FILES ------------------ +router.post( + "/pdf-files", + upload.single("file"), + async (req: Request, res: Response): Promise => { + try { + const { groupId } = req.body; + const file = req.file; + if (!groupId || !file) { + return res.status(400).json({ error: "Missing groupId or file" }); + } + + const pdf = await storage.createPdfFile( + parseInt(groupId), + file.originalname, + file.buffer + ); + + res.json(pdf); + } catch (err) { + console.error("Error uploading PDF file:", err); + res.status(500).json({ error: "Internal server error" }); + } + } +); + +router.get( + "/pdf-files/:id", + async (req: Request, res: Response): Promise => { + try { + const idParam = req.params.id; + if (!idParam) { + return res.status(400).json({ error: "Missing ID" }); + } + const id = parseInt(idParam); + const pdf = await storage.getPdfFileById(id); + if (!pdf || !pdf.pdfData) + return res.status(404).json({ error: "PDF not found" }); + + if (!Buffer.isBuffer(pdf.pdfData)) { + pdf.pdfData = Buffer.from(Object.values(pdf.pdfData)); + } + + res.setHeader("Content-Type", "application/pdf"); + res.setHeader( + "Content-Disposition", + `attachment; filename="${pdf.filename}"; filename*=UTF-8''${encodeURIComponent(pdf.filename)}` + ); + res.send(pdf.pdfData); + } catch (err) { + console.error("Error downloading PDF file:", err); + res.status(500).json({ error: "Internal server error" }); + } + } +); + +router.get( + "/pdf-files/recent", + async (req: Request, res: Response): Promise => { + try { + const limit = parseInt(req.query.limit as string) || 5; + const offset = parseInt(req.query.offset as string) || 0; + const files = await storage.getRecentPdfFiles(limit, offset); + res.json(files); + } catch (err) { + console.error("Error getting recent PDF files:", err); + res.status(500).json({ error: "Internal server error" }); + } + } +); + +router.put( + "/pdf-files/:id", + upload.single("file"), + async (req: Request, res: Response): Promise => { + try { + const idParam = req.params.id; + if (!idParam) { + return res.status(400).json({ error: "Missing ID" }); + } + const id = parseInt(idParam); + const file = req.file; + + const updated = await storage.updatePdfFile(id, { + filename: file?.originalname, + pdfData: file?.buffer, + }); + + if (!updated) + return res + .status(404) + .json({ error: "PDF not found or update failed" }); + + res.json(updated); + } catch (err) { + console.error("Error updating PDF file:", err); + res.status(500).json({ error: "Internal server error" }); + } + } +); + +router.delete( + "/pdf-files/:id", + async (req: Request, res: Response): Promise => { + try { + const idParam = req.params.id; + if (!idParam) { + return res.status(400).json({ error: "Missing ID" }); + } + const id = parseInt(idParam); + + const success = await storage.deletePdfFile(id); + res.json({ success }); + } catch (err) { + console.error("Error deleting PDF file:", err); + res.status(500).json({ error: "Internal server error" }); + } + } +); export default router; diff --git a/apps/Backend/src/routes/insuranceEligibility.ts b/apps/Backend/src/routes/insuranceEligibility.ts index fa52d17..40f94af 100644 --- a/apps/Backend/src/routes/insuranceEligibility.ts +++ b/apps/Backend/src/routes/insuranceEligibility.ts @@ -5,50 +5,64 @@ import { forwardToSeleniumInsuranceEligibilityAgent } from "../services/selenium const router = Router(); -router.post( - "/check", - async (req: Request, res: Response): Promise => { - if (!req.body.data) { - return res - .status(400) - .json({ error: "Missing Insurance Eligibility data for selenium" }); - } +router.post("/check", async (req: Request, res: Response): Promise => { + if (!req.body.data) { + return res + .status(400) + .json({ error: "Missing Insurance Eligibility data for selenium" }); + } - if (!req.user || !req.user.id) { - return res.status(401).json({ error: "Unauthorized: user info missing" }); - } + if (!req.user || !req.user.id) { + return res.status(401).json({ error: "Unauthorized: user info missing" }); + } - try { - const insuranceEligibilityData = JSON.parse(req.body.data); + try { + const insuranceEligibilityData = JSON.parse(req.body.data); - const credentials = await storage.getInsuranceCredentialByUserAndSiteKey( - req.user.id, - insuranceEligibilityData.insuranceSiteKey - ); - if (!credentials) { - return res.status(404).json({ - error: - "No insurance credentials found for this provider, Kindly Update this at Settings Page.", - }); - } - - const enrichedData = { - ...insuranceEligibilityData, - massdhpUsername: credentials.username, - massdhpPassword: credentials.password, - }; - - const result = - await forwardToSeleniumInsuranceEligibilityAgent(enrichedData); - - res.json(result); - } catch (err: any) { - console.error(err); - return res.status(500).json({ - error: err.message || "Failed to forward to selenium agent", + const credentials = await storage.getInsuranceCredentialByUserAndSiteKey( + req.user.id, + insuranceEligibilityData.insuranceSiteKey + ); + if (!credentials) { + return res.status(404).json({ + error: + "No insurance credentials found for this provider, Kindly Update this at Settings Page.", }); } + + const enrichedData = { + ...insuranceEligibilityData, + massdhpUsername: credentials.username, + massdhpPassword: credentials.password, + }; + + const result = + await forwardToSeleniumInsuranceEligibilityAgent(enrichedData); + + // ✅ Step 1: Check result and update patient status + const patient = await storage.getPatientByInsuranceId( + insuranceEligibilityData.memberId + ); + + if (patient && patient.id !== undefined) { + const newStatus = result.eligibility === "Y" ? "active" : "inactive"; + await storage.updatePatient(patient.id, { status: newStatus }); + result.patientUpdateStatus = `Patient status updated to ${newStatus}`; + } else { + console.warn( + `No patient found with insuranceId: ${insuranceEligibilityData.memberId}` + ); + result.patientUpdateStatus = + "Patient not found or missing ID; no update performed"; + } + + res.json(result); + } catch (err: any) { + console.error(err); + return res.status(500).json({ + error: err.message || "Failed to forward to selenium agent", + }); } -); +}); export default router; diff --git a/apps/Backend/src/storage/index.ts b/apps/Backend/src/storage/index.ts index a3d0a38..6b9d9ed 100644 --- a/apps/Backend/src/storage/index.ts +++ b/apps/Backend/src/storage/index.ts @@ -6,7 +6,9 @@ import { StaffUncheckedCreateInputObjectSchema, ClaimUncheckedCreateInputObjectSchema, InsuranceCredentialUncheckedCreateInputObjectSchema, - ClaimPdfUncheckedCreateInputObjectSchema, + PdfFileUncheckedCreateInputObjectSchema, + PdfGroupUncheckedCreateInputObjectSchema, + PdfCategorySchema, } from "@repo/db/usedSchemas"; import { z } from "zod"; @@ -146,8 +148,10 @@ type ClaimWithServiceLines = Claim & { }[]; }; -// claim types: -type ClaimPdf = z.infer; +// Pdf types: +type PdfGroup = z.infer; +type PdfFile = z.infer; +type PdfCategory = z.infer; export interface ClaimPdfMetadata { id: number; @@ -245,29 +249,36 @@ export interface IStorage { siteKey: string ): Promise; - // Claim PDF Methods - createClaimPdf( - patientId: number, - claimId: number, + // General PDF Methods + createPdfFile( + groupId: number, filename: string, pdfData: Buffer - ): Promise; - - getClaimPdfById(id: number): Promise; - - getAllClaimPdfs(): Promise; - - getRecentClaimPdfs( - limit: number, - offset: number - ): Promise; - - deleteClaimPdf(id: number): Promise; - - updateClaimPdf( + ): Promise; + getPdfFileById(id: number): Promise; + getPdfFilesByGroupId(groupId: number): Promise; + getRecentPdfFiles(limit: number, offset: number): Promise; + deletePdfFile(id: number): Promise; + updatePdfFile( id: number, - updates: Partial> - ): Promise; + updates: Partial> + ): Promise; + + // Group management + createPdfGroup( + patientId: number, + title: string, + category: PdfCategory +): Promise; + + getAllPdfGroups(): Promise; + getPdfGroupById(id: number): Promise; + getPdfGroupsByPatientId(patientId: number): Promise; + updatePdfGroup( + id: number, + updates: Partial> + ): Promise; + deletePdfGroup(id: number): Promise; } export const storage: IStorage = { @@ -610,73 +621,107 @@ export const storage: IStorage = { }); }, - // pdf claims - async createClaimPdf( - patientId, - claimId, - filename, - pdfData - ): Promise { - return db.claimPdf.create({ + // PDF Files + async createPdfFile(groupId, filename, pdfData) { + return db.pdfFile.create({ data: { - patientId, - claimId, + groupId, filename, pdfData, }, }); }, - async getClaimPdfById(id: number): Promise { - const pdf = await db.claimPdf.findUnique({ where: { id } }); - return pdf ?? undefined; - }, - - async getAllClaimPdfs(): Promise { - return db.claimPdf.findMany({ - select: { id: true, filename: true, uploadedAt: true }, - orderBy: { uploadedAt: "desc" }, - }); - }, - - async getRecentClaimPdfs( - limit: number, - offset: number - ): Promise { - return db.claimPdf.findMany({ - skip: offset, - take: limit, - orderBy: { uploadedAt: "desc" }, - select: { - id: true, - filename: true, - uploadedAt: true, - patient: true, + async getAllPdfGroups(): Promise { + return db.pdfGroup.findMany({ + orderBy: { + createdAt: "desc", }, }); }, - async deleteClaimPdf(id: number): Promise { + async getPdfFileById(id) { + return (await db.pdfFile.findUnique({ where: { id } })) ?? undefined; + }, + + async getPdfFilesByGroupId(groupId) { + return db.pdfFile.findMany({ + where: { groupId }, + orderBy: { uploadedAt: "desc" }, + }); + }, + + async getRecentPdfFiles(limit: number, offset: number): Promise { + return db.pdfFile.findMany({ + skip: offset, + take: limit, + orderBy: { uploadedAt: "desc" }, + include: { group: true }, + }); + }, + + async updatePdfFile(id, updates) { try { - await db.claimPdf.delete({ where: { id } }); + return await db.pdfFile.update({ + where: { id }, + data: updates, + }); + } catch { + return undefined; + } + }, + + async deletePdfFile(id) { + try { + await db.pdfFile.delete({ where: { id } }); return true; } catch { return false; } }, - async updateClaimPdf( - id: number, - updates: Partial> - ): Promise { + // ---------------------- + // PdfGroup CRUD + // ---------------------- + + async createPdfGroup(patientId, title, category) { + return db.pdfGroup.create({ + data: { + patientId, + title, + category, + }, + }); + }, + + async getPdfGroupById(id) { + return (await db.pdfGroup.findUnique({ where: { id } })) ?? undefined; + }, + + async getPdfGroupsByPatientId(patientId) { + return db.pdfGroup.findMany({ + where: { patientId }, + orderBy: { createdAt: "desc" }, + }); + }, + + async updatePdfGroup(id, updates) { try { - const updated = await db.claimPdf.update({ + return await db.pdfGroup.update({ where: { id }, data: updates, }); - return updated; } catch { return undefined; } }, + + async deletePdfGroup(id) { + try { + await db.pdfGroup.delete({ where: { id } }); + return true; + } catch { + return false; + } + }, }; diff --git a/apps/SeleniumService/seleniumDownloads/MassDHP_Member_Eligibility_1497211726.pdf b/apps/SeleniumService/seleniumDownloads/MassDHP_Member_Eligibility_1497211726.pdf index 398d463..5e94f4c 100644 --- a/apps/SeleniumService/seleniumDownloads/MassDHP_Member_Eligibility_1497211726.pdf +++ b/apps/SeleniumService/seleniumDownloads/MassDHP_Member_Eligibility_1497211726.pdf @@ -23,7 +23,7 @@ endobj 4 0 obj <> stream xWY0 )tfD-O ܠP`Pt~zJ4)K ٴD@^$3lĵ~U=MTwe\׏h CY5˓̔5sype=?h%y(:8YG}vӓ]I ϐ, pwQ(fKcH$t2xA"0 >`BPY5u>oji'Rۯ32gQ+RQhiMhl砽thNb\DC{ɼ>SJyAwBYv}Qg -eKe{2 <2C3C044FB8B3147B433FB683CF1264AB>] +/ID [<1C3064F1E315560AD7C9C0CCCE64F486> <1C3064F1E315560AD7C9C0CCCE64F486>] >> startxref 64180 diff --git a/dele.txt b/dele.txt new file mode 100644 index 0000000..203819c --- /dev/null +++ b/dele.txt @@ -0,0 +1,5 @@ +{ + "status": "success", + "eligibility": "Y", + "pdf_path": "C:\\Users\\Potenz\\Desktop\\DentalManager\\apps\\SeleniumService\\seleniumDownloads\\MassDHP_Member_Eligibility_1497211726.pdf" +} \ No newline at end of file diff --git a/packages/db/prisma/schema.prisma b/packages/db/prisma/schema.prisma index 218e833..61767a4 100644 --- a/packages/db/prisma/schema.prisma +++ b/packages/db/prisma/schema.prisma @@ -52,7 +52,10 @@ model Patient { user User @relation(fields: [userId], references: [id]) appointments Appointment[] claims Claim[] - pdfs ClaimPdf[] + groups PdfGroup[] + + @@index([insuranceId]) + @@index([createdAt]) } model Appointment { @@ -73,6 +76,9 @@ model Appointment { user User @relation(fields: [userId], references: [id]) staff Staff? @relation(fields: [staffId], references: [id]) claims Claim[] + + @@index([patientId]) + @@index([date]) } model Staff { @@ -108,7 +114,6 @@ model Claim { staff Staff? @relation("ClaimStaff", fields: [staffId], references: [id]) serviceLines ServiceLine[] - pdf ClaimPdf[] } model ServiceLine { @@ -136,17 +141,33 @@ model InsuranceCredential { @@index([userId]) } -model ClaimPdf { +model PdfGroup { + id Int @id @default(autoincrement()) + title String // e.g., "June Claim Docs", "Eligibility Set A" + category PdfCategory + createdAt DateTime @default(now()) + patientId Int + patient Patient @relation(fields: [patientId], references: [id], onDelete: Cascade) + pdfs PdfFile[] + + @@index([patientId]) + @@index([category]) +} + +model PdfFile { id Int @id @default(autoincrement()) - patientId Int - claimId Int? filename String pdfData Bytes uploadedAt DateTime @default(now()) + groupId Int + group PdfGroup @relation(fields: [groupId], references: [id], onDelete: Cascade) - patient Patient @relation(fields: [patientId], references: [id], onDelete: Cascade) - claim Claim? @relation(fields: [claimId], references: [id], onDelete: Cascade) + @@index([groupId]) - @@index([patientId]) - @@index([claimId]) +} + +enum PdfCategory { + CLAIM + ELIGIBILITY + OTHER } diff --git a/packages/db/usedSchemas/index.ts b/packages/db/usedSchemas/index.ts index 0eaa6fd..b4efb58 100644 --- a/packages/db/usedSchemas/index.ts +++ b/packages/db/usedSchemas/index.ts @@ -5,4 +5,6 @@ export * from '../shared/schemas/objects/UserUncheckedCreateInput.schema'; export * from '../shared/schemas/objects/StaffUncheckedCreateInput.schema' export * from '../shared/schemas/objects/ClaimUncheckedCreateInput.schema' export * from '../shared/schemas/objects/InsuranceCredentialUncheckedCreateInput.schema' -export * from '../shared/schemas/objects/ClaimPdfUncheckedCreateInput.schema' \ No newline at end of file +export * from '../shared/schemas/objects/PdfFileUncheckedCreateInput.schema' +export * from '../shared/schemas/objects/PdfGroupUncheckedCreateInput.schema' +export * from '../shared/schemas/enums/PdfCategory.schema' \ No newline at end of file