initial commit
This commit is contained in:
85
apps/Backend/src/services/databaseBackupService.ts
Executable file
85
apps/Backend/src/services/databaseBackupService.ts
Executable file
@@ -0,0 +1,85 @@
|
||||
import { spawn } from "child_process";
|
||||
import fs from "fs";
|
||||
import os from "os";
|
||||
import path from "path";
|
||||
import archiver from "archiver";
|
||||
|
||||
function safeRmDir(dir: string) {
|
||||
try {
|
||||
fs.rmSync(dir, { recursive: true, force: true });
|
||||
} catch {}
|
||||
}
|
||||
|
||||
interface BackupToPathParams {
|
||||
destinationPath: string;
|
||||
filename: string;
|
||||
}
|
||||
|
||||
export async function backupDatabaseToPath({
|
||||
destinationPath,
|
||||
filename,
|
||||
}: BackupToPathParams): Promise<void> {
|
||||
const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "dental_backup_"));
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
const pgDump = spawn(
|
||||
"pg_dump",
|
||||
[
|
||||
"-Fd",
|
||||
"-j",
|
||||
"4",
|
||||
"--no-acl",
|
||||
"--no-owner",
|
||||
"-h",
|
||||
process.env.DB_HOST || "localhost",
|
||||
"-U",
|
||||
process.env.DB_USER || "postgres",
|
||||
process.env.DB_NAME || "dental_db",
|
||||
"-f",
|
||||
tmpDir,
|
||||
],
|
||||
{
|
||||
env: {
|
||||
...process.env,
|
||||
PGPASSWORD: process.env.DB_PASSWORD,
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
let pgError = "";
|
||||
|
||||
pgDump.stderr.on("data", (d) => (pgError += d.toString()));
|
||||
|
||||
pgDump.on("close", async (code) => {
|
||||
if (code !== 0) {
|
||||
safeRmDir(tmpDir);
|
||||
return reject(new Error(pgError || "pg_dump failed"));
|
||||
}
|
||||
|
||||
const outputFile = path.join(destinationPath, filename);
|
||||
const outputStream = fs.createWriteStream(outputFile);
|
||||
|
||||
const archive = archiver("zip");
|
||||
|
||||
outputStream.on("error", (err) => {
|
||||
safeRmDir(tmpDir);
|
||||
reject(err);
|
||||
});
|
||||
|
||||
archive.on("error", (err) => {
|
||||
safeRmDir(tmpDir);
|
||||
reject(err);
|
||||
});
|
||||
|
||||
archive.pipe(outputStream);
|
||||
archive.directory(tmpDir + path.sep, false);
|
||||
|
||||
archive.finalize();
|
||||
|
||||
archive.on("end", () => {
|
||||
safeRmDir(tmpDir);
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
29
apps/Backend/src/services/patientDataExtractorService.ts
Executable file
29
apps/Backend/src/services/patientDataExtractorService.ts
Executable file
@@ -0,0 +1,29 @@
|
||||
import axios from "axios";
|
||||
import FormData from "form-data";
|
||||
|
||||
export interface ExtractedData {
|
||||
name?: string;
|
||||
memberId?: string;
|
||||
dob?: string;
|
||||
[key: string]: any;
|
||||
}
|
||||
|
||||
export default async function forwardToPatientDataExtractorService(
|
||||
file: Express.Multer.File
|
||||
): Promise<ExtractedData> {
|
||||
const form = new FormData();
|
||||
form.append("pdf", file.buffer, {
|
||||
filename: file.originalname,
|
||||
contentType: file.mimetype,
|
||||
});
|
||||
|
||||
const response = await axios.post<ExtractedData>(
|
||||
"http://localhost:5001/extract",
|
||||
form,
|
||||
{
|
||||
headers: form.getHeaders(),
|
||||
}
|
||||
);
|
||||
|
||||
return response.data;
|
||||
}
|
||||
34
apps/Backend/src/services/paymentOCRService.ts
Executable file
34
apps/Backend/src/services/paymentOCRService.ts
Executable file
@@ -0,0 +1,34 @@
|
||||
import axios from "axios";
|
||||
import FormData from "form-data";
|
||||
|
||||
export async function forwardToPaymentOCRService(
|
||||
files: Express.Multer.File | Express.Multer.File[]
|
||||
): Promise<any> {
|
||||
const arr = Array.isArray(files) ? files : [files];
|
||||
|
||||
const form = new FormData();
|
||||
for (const f of arr) {
|
||||
form.append("files", f.buffer, {
|
||||
filename: f.originalname,
|
||||
contentType: f.mimetype, // image/jpeg, image/png, image/tiff, etc.
|
||||
knownLength: f.size,
|
||||
});
|
||||
}
|
||||
|
||||
const url = `http://localhost:5003/extract/json`;
|
||||
|
||||
try {
|
||||
const resp = await axios.post<{ rows: any }>(url, form, {
|
||||
headers: form.getHeaders(),
|
||||
maxBodyLength: Infinity,
|
||||
maxContentLength: Infinity,
|
||||
timeout: 120000, // OCR can be heavy; adjust as needed
|
||||
});
|
||||
return resp.data?.rows ?? [];
|
||||
} catch (err: any) {
|
||||
// Bubble up a useful error message
|
||||
const status = err?.response?.status;
|
||||
const detail = err?.response?.data?.detail || err?.message || "Unknown error";
|
||||
throw new Error(`Payment OCR request failed${status ? ` (${status})` : ""}: ${detail}`);
|
||||
}
|
||||
}
|
||||
284
apps/Backend/src/services/paymentService.ts
Executable file
284
apps/Backend/src/services/paymentService.ts
Executable file
@@ -0,0 +1,284 @@
|
||||
import Decimal from "decimal.js";
|
||||
import {
|
||||
NewTransactionPayload,
|
||||
OcrRow,
|
||||
Payment,
|
||||
PaymentMethod,
|
||||
PaymentStatus,
|
||||
ClaimStatus,
|
||||
} from "@repo/db/types";
|
||||
import { storage } from "../storage";
|
||||
import { prisma } from "@repo/db/client";
|
||||
import { convertOCRDate } from "../utils/dateUtils";
|
||||
|
||||
/**
|
||||
* Validate transactions against a payment record
|
||||
*/
|
||||
export async function validateTransactions(
|
||||
paymentId: number,
|
||||
serviceLineTransactions: NewTransactionPayload["serviceLineTransactions"],
|
||||
options?: { isReversal?: boolean }
|
||||
) {
|
||||
const paymentRecord = await storage.getPaymentById(paymentId);
|
||||
if (!paymentRecord) {
|
||||
throw new Error("Payment not found");
|
||||
}
|
||||
|
||||
// Choose service lines from claim if present, otherwise direct payment service lines(OCR Based datas)
|
||||
const serviceLines = paymentRecord.claim
|
||||
? paymentRecord.claim.serviceLines
|
||||
: paymentRecord.serviceLines;
|
||||
|
||||
if (!serviceLines || serviceLines.length === 0) {
|
||||
throw new Error("No service lines available for this payment");
|
||||
}
|
||||
|
||||
for (const txn of serviceLineTransactions) {
|
||||
const line = serviceLines.find((sl) => sl.id === txn.serviceLineId);
|
||||
|
||||
if (!line) {
|
||||
throw new Error(`Invalid service line: ${txn.serviceLineId}`);
|
||||
}
|
||||
|
||||
const paidAmount = new Decimal(txn.paidAmount ?? 0);
|
||||
const adjustedAmount = new Decimal(txn.adjustedAmount ?? 0);
|
||||
|
||||
if (!options?.isReversal && (paidAmount.lt(0) || adjustedAmount.lt(0))) {
|
||||
throw new Error("Amounts cannot be negative");
|
||||
}
|
||||
|
||||
if (paidAmount.eq(0) && adjustedAmount.eq(0)) {
|
||||
throw new Error("Must provide a payment or adjustment");
|
||||
}
|
||||
if (!options?.isReversal && paidAmount.gt(line.totalDue)) {
|
||||
throw new Error(
|
||||
`Paid amount exceeds due for service line ${txn.serviceLineId}`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return paymentRecord;
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply transactions to a payment & recalc totals
|
||||
*/
|
||||
export async function applyTransactions(
|
||||
paymentId: number,
|
||||
serviceLineTransactions: NewTransactionPayload["serviceLineTransactions"],
|
||||
userId: number
|
||||
): Promise<Payment> {
|
||||
return prisma.$transaction(async (tx) => {
|
||||
// 1. Insert service line transactions + recalculate each serviceLines
|
||||
for (const txn of serviceLineTransactions) {
|
||||
await tx.serviceLineTransaction.create({
|
||||
data: {
|
||||
paymentId,
|
||||
serviceLineId: txn.serviceLineId,
|
||||
transactionId: txn.transactionId,
|
||||
paidAmount: new Decimal(txn.paidAmount),
|
||||
adjustedAmount: new Decimal(txn.adjustedAmount || 0),
|
||||
method: txn.method,
|
||||
receivedDate: txn.receivedDate,
|
||||
payerName: txn.payerName,
|
||||
notes: txn.notes,
|
||||
},
|
||||
});
|
||||
|
||||
// Recalculate Claim - serviceLines model totals and updates along with Claim-serviceLine status
|
||||
const aggLine = await tx.serviceLineTransaction.aggregate({
|
||||
_sum: { paidAmount: true, adjustedAmount: true },
|
||||
where: { serviceLineId: txn.serviceLineId },
|
||||
});
|
||||
|
||||
const serviceLine = await tx.serviceLine.findUniqueOrThrow({
|
||||
where: { id: txn.serviceLineId },
|
||||
select: { totalBilled: true },
|
||||
});
|
||||
|
||||
const totalPaid = aggLine._sum.paidAmount || new Decimal(0);
|
||||
const totalAdjusted = aggLine._sum.adjustedAmount || new Decimal(0);
|
||||
const totalDue = serviceLine.totalBilled
|
||||
.minus(totalPaid)
|
||||
.minus(totalAdjusted);
|
||||
|
||||
await tx.serviceLine.update({
|
||||
where: { id: txn.serviceLineId },
|
||||
data: {
|
||||
totalPaid,
|
||||
totalAdjusted,
|
||||
totalDue,
|
||||
status:
|
||||
totalDue.lte(0) && totalPaid.gt(0)
|
||||
? "PAID"
|
||||
: totalPaid.gt(0)
|
||||
? "PARTIALLY_PAID"
|
||||
: "UNPAID",
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
// 2. Recalc payment model totals based on serviceLineTransactions, and update PaymentStatus.
|
||||
const aggPayment = await tx.serviceLineTransaction.aggregate({
|
||||
_sum: { paidAmount: true, adjustedAmount: true },
|
||||
where: { paymentId },
|
||||
});
|
||||
|
||||
const payment = await tx.payment.findUniqueOrThrow({
|
||||
where: { id: paymentId },
|
||||
select: { totalBilled: true },
|
||||
});
|
||||
|
||||
const totalPaid = aggPayment._sum.paidAmount || new Decimal(0);
|
||||
const totalAdjusted = aggPayment._sum.adjustedAmount || new Decimal(0);
|
||||
const totalDue = payment.totalBilled.minus(totalPaid).minus(totalAdjusted);
|
||||
|
||||
let status: PaymentStatus;
|
||||
if (totalDue.lte(0) && totalPaid.gt(0)) status = "PAID";
|
||||
else if (totalPaid.gt(0)) status = "PARTIALLY_PAID";
|
||||
else status = "PENDING";
|
||||
|
||||
const updatedPayment = await tx.payment.update({
|
||||
where: { id: paymentId },
|
||||
data: { totalPaid, totalAdjusted, totalDue, status, updatedById: userId },
|
||||
});
|
||||
|
||||
// 3. Update Claim Model Status based on serviceLineTransaction and Payment values.(as they hold the same values
|
||||
// as per, ServiceLine.totalPaid and totalAdjusted and Claim.totalBilled) Hence not fetching unneccessary.
|
||||
const claimId = updatedPayment.claimId ?? null;
|
||||
if (claimId) {
|
||||
let newClaimStatus: ClaimStatus;
|
||||
if (totalDue.lte(0) && totalPaid.gt(0)) newClaimStatus = "APPROVED";
|
||||
else newClaimStatus = "PENDING";
|
||||
|
||||
await tx.claim.update({
|
||||
where: { id: claimId },
|
||||
data: { status: newClaimStatus },
|
||||
});
|
||||
}
|
||||
|
||||
return updatedPayment;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Main entry point for updating payments
|
||||
*/
|
||||
export async function updatePayment(
|
||||
paymentId: number,
|
||||
serviceLineTransactions: NewTransactionPayload["serviceLineTransactions"],
|
||||
userId: number,
|
||||
options?: { isReversal?: boolean }
|
||||
): Promise<Payment> {
|
||||
await validateTransactions(paymentId, serviceLineTransactions, options);
|
||||
return applyTransactions(paymentId, serviceLineTransactions, userId);
|
||||
}
|
||||
|
||||
// handling full-ocr-payments-import
|
||||
|
||||
export const fullOcrPaymentService = {
|
||||
async importRows(rows: OcrRow[], userId: number) {
|
||||
const results: number[] = [];
|
||||
|
||||
for (const [index, row] of rows.entries()) {
|
||||
try {
|
||||
if (!row.patientName || !row.insuranceId) {
|
||||
throw new Error(
|
||||
`Row ${index + 1}: missing patientName or insuranceId`
|
||||
);
|
||||
}
|
||||
if (!row.procedureCode) {
|
||||
throw new Error(`Row ${index + 1}: missing procedureCode`);
|
||||
}
|
||||
|
||||
const billed = new Decimal(row.totalBilled ?? 0);
|
||||
const allowed = new Decimal(row.totalAllowed ?? row.totalBilled ?? 0);
|
||||
const paid = new Decimal(row.totalPaid ?? 0);
|
||||
|
||||
const adjusted = billed.minus(allowed); // write-off
|
||||
|
||||
// Step 1–3 in a transaction
|
||||
const { paymentId, serviceLineId } = await prisma.$transaction(
|
||||
async (tx) => {
|
||||
// 1. Find or create patient
|
||||
let patient = await tx.patient.findFirst({
|
||||
where: { insuranceId: row.insuranceId.toString() },
|
||||
});
|
||||
|
||||
if (!patient) {
|
||||
const [firstNameRaw, ...rest] = (row.patientName ?? "")
|
||||
.trim()
|
||||
.split(" ");
|
||||
const firstName = firstNameRaw || "Unknown";
|
||||
const lastName = rest.length > 0 ? rest.join(" ") : "Unknown";
|
||||
|
||||
patient = await tx.patient.create({
|
||||
data: {
|
||||
firstName,
|
||||
lastName,
|
||||
insuranceId: row.insuranceId.toString(),
|
||||
dateOfBirth: new Date(Date.UTC(1900, 0, 1)), // fallback (1900, jan, 1)
|
||||
gender: "",
|
||||
phone: "",
|
||||
userId,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
// 2. Create payment (claimId null) — IMPORTANT: start with zeros, due = billed
|
||||
const payment = await tx.payment.create({
|
||||
data: {
|
||||
patientId: patient.id,
|
||||
userId,
|
||||
totalBilled: billed,
|
||||
totalPaid: new Decimal(0),
|
||||
totalAdjusted: new Decimal(0),
|
||||
totalDue: billed,
|
||||
status: "PENDING", // updatePayment will fix it
|
||||
notes: `OCR import from ${row.sourceFile ?? "Unknown file"}`,
|
||||
icn: row.icn ?? "",
|
||||
},
|
||||
});
|
||||
|
||||
// 3. Create service line — IMPORTANT: start with zeros, due = billed
|
||||
const serviceLine = await tx.serviceLine.create({
|
||||
data: {
|
||||
paymentId: payment.id,
|
||||
procedureCode: row.procedureCode,
|
||||
toothNumber: row.toothNumber ?? null,
|
||||
toothSurface: row.toothSurface ?? null,
|
||||
procedureDate: convertOCRDate(row.procedureDate),
|
||||
totalBilled: billed,
|
||||
totalPaid: new Decimal(0),
|
||||
totalAdjusted: new Decimal(0),
|
||||
totalDue: billed,
|
||||
},
|
||||
});
|
||||
|
||||
return { paymentId: payment.id, serviceLineId: serviceLine.id };
|
||||
}
|
||||
);
|
||||
|
||||
// Step 4: AFTER commit, recalc using updatePayment (global prisma can see it now)
|
||||
// Build transaction & let updatePayment handle recalculation
|
||||
const txn = {
|
||||
serviceLineId,
|
||||
paidAmount: paid.toNumber(),
|
||||
adjustedAmount: adjusted.toNumber(),
|
||||
method: "OTHER" as PaymentMethod,
|
||||
receivedDate: new Date(),
|
||||
notes: "OCR import",
|
||||
};
|
||||
|
||||
await updatePayment(paymentId, [txn], userId);
|
||||
|
||||
results.push(paymentId);
|
||||
} catch (err) {
|
||||
console.error(`❌ Failed to import OCR row ${index + 1}:`, err);
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
return results;
|
||||
},
|
||||
};
|
||||
52
apps/Backend/src/services/seleniumClaimClient.ts
Executable file
52
apps/Backend/src/services/seleniumClaimClient.ts
Executable file
@@ -0,0 +1,52 @@
|
||||
import axios from "axios";
|
||||
|
||||
export interface SeleniumPayload {
|
||||
claim: any;
|
||||
pdfs: {
|
||||
originalname: string;
|
||||
bufferBase64: string;
|
||||
}[];
|
||||
images: {
|
||||
originalname: string;
|
||||
bufferBase64: string;
|
||||
}[];
|
||||
}
|
||||
|
||||
export async function forwardToSeleniumClaimAgent(
|
||||
claimData: any,
|
||||
files: Express.Multer.File[]
|
||||
): Promise<any> {
|
||||
const pdfs = files
|
||||
.filter((file) => file.mimetype === "application/pdf")
|
||||
.map((file) => ({
|
||||
originalname: file.originalname,
|
||||
bufferBase64: file.buffer.toString("base64"),
|
||||
}));
|
||||
|
||||
const images = files
|
||||
.filter((file) => file.mimetype.startsWith("image/"))
|
||||
.map((file) => ({
|
||||
originalname: file.originalname,
|
||||
bufferBase64: file.buffer.toString("base64"),
|
||||
}));
|
||||
|
||||
const payload: SeleniumPayload = {
|
||||
claim: claimData,
|
||||
pdfs,
|
||||
images,
|
||||
};
|
||||
|
||||
const result = await axios.post(
|
||||
"http://localhost:5002/claimsubmit",
|
||||
payload
|
||||
);
|
||||
if (result.data.status === "error") {
|
||||
const errorMsg =
|
||||
typeof result.data.message === "string"
|
||||
? result.data.message
|
||||
: result.data.message?.msg || "Selenium agent error";
|
||||
throw new Error(errorMsg);
|
||||
}
|
||||
|
||||
return result.data;
|
||||
}
|
||||
122
apps/Backend/src/services/seleniumDdmaInsuranceEligibilityClient.ts
Executable file
122
apps/Backend/src/services/seleniumDdmaInsuranceEligibilityClient.ts
Executable file
@@ -0,0 +1,122 @@
|
||||
import axios from "axios";
|
||||
import http from "http";
|
||||
import https from "https";
|
||||
import dotenv from "dotenv";
|
||||
dotenv.config();
|
||||
|
||||
export interface SeleniumPayload {
|
||||
data: any;
|
||||
url?: string;
|
||||
}
|
||||
|
||||
const SELENIUM_AGENT_BASE = process.env.SELENIUM_AGENT_BASE_URL;
|
||||
|
||||
const httpAgent = new http.Agent({ keepAlive: true, keepAliveMsecs: 60_000 });
|
||||
const httpsAgent = new https.Agent({ keepAlive: true, keepAliveMsecs: 60_000 });
|
||||
|
||||
const client = axios.create({
|
||||
baseURL: SELENIUM_AGENT_BASE,
|
||||
timeout: 5 * 60 * 1000,
|
||||
httpAgent,
|
||||
httpsAgent,
|
||||
validateStatus: (s) => s >= 200 && s < 600,
|
||||
});
|
||||
|
||||
async function requestWithRetries(
|
||||
config: any,
|
||||
retries = 4,
|
||||
baseBackoffMs = 300
|
||||
) {
|
||||
for (let attempt = 1; attempt <= retries; attempt++) {
|
||||
try {
|
||||
const r = await client.request(config);
|
||||
if (![502, 503, 504].includes(r.status)) return r;
|
||||
console.warn(
|
||||
`[selenium-client] retryable HTTP status ${r.status} (attempt ${attempt})`
|
||||
);
|
||||
} catch (err: any) {
|
||||
const code = err?.code;
|
||||
const isTransient =
|
||||
code === "ECONNRESET" ||
|
||||
code === "ECONNREFUSED" ||
|
||||
code === "EPIPE" ||
|
||||
code === "ETIMEDOUT";
|
||||
if (!isTransient) throw err;
|
||||
console.warn(
|
||||
`[selenium-client] transient network error ${code} (attempt ${attempt})`
|
||||
);
|
||||
}
|
||||
await new Promise((r) => setTimeout(r, baseBackoffMs * attempt));
|
||||
}
|
||||
// final attempt (let exception bubble if it fails)
|
||||
return client.request(config);
|
||||
}
|
||||
|
||||
function now() {
|
||||
return new Date().toISOString();
|
||||
}
|
||||
function log(tag: string, msg: string, ctx?: any) {
|
||||
console.log(`${now()} [${tag}] ${msg}`, ctx ?? "");
|
||||
}
|
||||
|
||||
export async function forwardToSeleniumDdmaEligibilityAgent(
|
||||
insuranceEligibilityData: any
|
||||
): Promise<any> {
|
||||
const payload = { data: insuranceEligibilityData };
|
||||
const url = `/ddma-eligibility`;
|
||||
log("selenium-client", "POST ddma-eligibility", {
|
||||
url: SELENIUM_AGENT_BASE + url,
|
||||
keys: Object.keys(payload),
|
||||
});
|
||||
const r = await requestWithRetries({ url, method: "POST", data: payload }, 4);
|
||||
log("selenium-client", "agent response", {
|
||||
status: r.status,
|
||||
dataKeys: r.data ? Object.keys(r.data) : null,
|
||||
});
|
||||
if (r.status >= 500)
|
||||
throw new Error(`Selenium agent server error: ${r.status}`);
|
||||
return r.data;
|
||||
}
|
||||
|
||||
export async function forwardOtpToSeleniumDdmaAgent(
|
||||
sessionId: string,
|
||||
otp: string
|
||||
): Promise<any> {
|
||||
const url = `/submit-otp`;
|
||||
log("selenium-client", "POST submit-otp", {
|
||||
url: SELENIUM_AGENT_BASE + url,
|
||||
sessionId,
|
||||
});
|
||||
const r = await requestWithRetries(
|
||||
{ url, method: "POST", data: { session_id: sessionId, otp } },
|
||||
4
|
||||
);
|
||||
log("selenium-client", "submit-otp response", {
|
||||
status: r.status,
|
||||
data: r.data,
|
||||
});
|
||||
if (r.status >= 500)
|
||||
throw new Error(`Selenium agent server error on submit-otp: ${r.status}`);
|
||||
return r.data;
|
||||
}
|
||||
|
||||
export async function getSeleniumDdmaSessionStatus(
|
||||
sessionId: string
|
||||
): Promise<any> {
|
||||
const url = `/session/${sessionId}/status`;
|
||||
log("selenium-client", "GET session status", {
|
||||
url: SELENIUM_AGENT_BASE + url,
|
||||
sessionId,
|
||||
});
|
||||
const r = await requestWithRetries({ url, method: "GET" }, 4);
|
||||
log("selenium-client", "session status response", {
|
||||
status: r.status,
|
||||
dataKeys: r.data ? Object.keys(r.data) : null,
|
||||
});
|
||||
if (r.status === 404) {
|
||||
const e: any = new Error("not_found");
|
||||
e.response = { status: 404, data: r.data };
|
||||
throw e;
|
||||
}
|
||||
return r.data;
|
||||
}
|
||||
52
apps/Backend/src/services/seleniumInsuranceClaimPreAuthClient.ts
Executable file
52
apps/Backend/src/services/seleniumInsuranceClaimPreAuthClient.ts
Executable file
@@ -0,0 +1,52 @@
|
||||
import axios from "axios";
|
||||
|
||||
export interface SeleniumPayload {
|
||||
claim: any;
|
||||
pdfs: {
|
||||
originalname: string;
|
||||
bufferBase64: string;
|
||||
}[];
|
||||
images: {
|
||||
originalname: string;
|
||||
bufferBase64: string;
|
||||
}[];
|
||||
}
|
||||
|
||||
export async function forwardToSeleniumClaimPreAuthAgent(
|
||||
claimData: any,
|
||||
files: Express.Multer.File[]
|
||||
): Promise<any> {
|
||||
const pdfs = files
|
||||
.filter((file) => file.mimetype === "application/pdf")
|
||||
.map((file) => ({
|
||||
originalname: file.originalname,
|
||||
bufferBase64: file.buffer.toString("base64"),
|
||||
}));
|
||||
|
||||
const images = files
|
||||
.filter((file) => file.mimetype.startsWith("image/"))
|
||||
.map((file) => ({
|
||||
originalname: file.originalname,
|
||||
bufferBase64: file.buffer.toString("base64"),
|
||||
}));
|
||||
|
||||
const payload: SeleniumPayload = {
|
||||
claim: claimData,
|
||||
pdfs,
|
||||
images,
|
||||
};
|
||||
|
||||
const result = await axios.post(
|
||||
"http://localhost:5002/claim-pre-auth",
|
||||
payload
|
||||
);
|
||||
if (result.data.status === "error") {
|
||||
const errorMsg =
|
||||
typeof result.data.message === "string"
|
||||
? result.data.message
|
||||
: result.data.message?.msg || "Selenium agent error";
|
||||
throw new Error(errorMsg);
|
||||
}
|
||||
|
||||
return result.data;
|
||||
}
|
||||
27
apps/Backend/src/services/seleniumInsuranceClaimStatusClient.ts
Executable file
27
apps/Backend/src/services/seleniumInsuranceClaimStatusClient.ts
Executable file
@@ -0,0 +1,27 @@
|
||||
import axios from "axios";
|
||||
|
||||
export interface SeleniumPayload {
|
||||
data: any;
|
||||
}
|
||||
|
||||
export async function forwardToSeleniumInsuranceClaimStatusAgent(
|
||||
insuranceClaimStatusData: any
|
||||
): Promise<any> {
|
||||
const payload: SeleniumPayload = {
|
||||
data: insuranceClaimStatusData,
|
||||
};
|
||||
|
||||
const result = await axios.post(
|
||||
"http://localhost:5002/claim-status-check",
|
||||
payload
|
||||
);
|
||||
if (result.data.status === "error") {
|
||||
const errorMsg =
|
||||
typeof result.data.message === "string"
|
||||
? result.data.message
|
||||
: result.data.message?.msg || "Selenium agent error";
|
||||
throw new Error(errorMsg);
|
||||
}
|
||||
|
||||
return result.data;
|
||||
}
|
||||
27
apps/Backend/src/services/seleniumInsuranceEligibilityClient.ts
Executable file
27
apps/Backend/src/services/seleniumInsuranceEligibilityClient.ts
Executable file
@@ -0,0 +1,27 @@
|
||||
import axios from "axios";
|
||||
|
||||
export interface SeleniumPayload {
|
||||
data: any;
|
||||
}
|
||||
|
||||
export async function forwardToSeleniumInsuranceEligibilityAgent(
|
||||
insuranceEligibilityData: any
|
||||
): Promise<any> {
|
||||
const payload: SeleniumPayload = {
|
||||
data: insuranceEligibilityData,
|
||||
};
|
||||
|
||||
const result = await axios.post(
|
||||
"http://localhost:5002/eligibility-check",
|
||||
payload
|
||||
);
|
||||
if (result.data.status === "error") {
|
||||
const errorMsg =
|
||||
typeof result.data.message === "string"
|
||||
? result.data.message
|
||||
: result.data.message?.msg || "Selenium agent error";
|
||||
throw new Error(errorMsg);
|
||||
}
|
||||
|
||||
return result.data;
|
||||
}
|
||||
Reference in New Issue
Block a user