payment checkpoint 2
This commit is contained in:
@@ -1,25 +1,26 @@
|
||||
import { Router } from 'express';
|
||||
import patientRoutes from './patients';
|
||||
import appointmentRoutes from './appointments'
|
||||
import userRoutes from './users'
|
||||
import staffRoutes from './staffs'
|
||||
import patientsRoutes from './patients';
|
||||
import appointmentsRoutes from './appointments'
|
||||
import usersRoutes from './users'
|
||||
import staffsRoutes from './staffs'
|
||||
import pdfExtractionRoutes from './pdfExtraction';
|
||||
import claimsRoutes from './claims';
|
||||
import insuranceCredsRoutes from './insuranceCreds';
|
||||
import documentRoutes from './documents';
|
||||
import documentsRoutes from './documents';
|
||||
import insuranceEligibilityRoutes from './insuranceEligibility'
|
||||
import paymentsRoutes from './payments'
|
||||
|
||||
const router = Router();
|
||||
|
||||
router.use('/patients', patientRoutes);
|
||||
router.use('/appointments', appointmentRoutes);
|
||||
router.use('/users', userRoutes);
|
||||
router.use('/staffs', staffRoutes);
|
||||
router.use('/patients', patientsRoutes);
|
||||
router.use('/appointments', appointmentsRoutes);
|
||||
router.use('/users', usersRoutes);
|
||||
router.use('/staffs', staffsRoutes);
|
||||
router.use('/pdfExtraction', pdfExtractionRoutes);
|
||||
router.use('/claims', claimsRoutes);
|
||||
router.use('/insuranceCreds', insuranceCredsRoutes);
|
||||
router.use('/documents', documentRoutes);
|
||||
router.use('/documents', documentsRoutes);
|
||||
router.use('/insuranceEligibility', insuranceEligibilityRoutes);
|
||||
|
||||
router.use('/payments', paymentsRoutes);
|
||||
|
||||
export default router;
|
||||
@@ -2,11 +2,52 @@ import { Router } from "express";
|
||||
import { Request, Response } from "express";
|
||||
import { storage } from "../storage";
|
||||
import { z } from "zod";
|
||||
import { ClaimUncheckedCreateInputObjectSchema } from "@repo/db/usedSchemas";
|
||||
import {
|
||||
ClaimUncheckedCreateInputObjectSchema,
|
||||
PaymentUncheckedCreateInputObjectSchema,
|
||||
PaymentTransactionCreateInputObjectSchema,
|
||||
ServiceLinePaymentCreateInputObjectSchema,
|
||||
} from "@repo/db/usedSchemas";
|
||||
import { Prisma } from "@repo/db/generated/prisma";
|
||||
import { ZodError } from "zod";
|
||||
|
||||
const router = Router();
|
||||
// Base Payment type
|
||||
type Payment = z.infer<typeof PaymentUncheckedCreateInputObjectSchema>;
|
||||
type PaymentTransaction = z.infer<
|
||||
typeof PaymentTransactionCreateInputObjectSchema
|
||||
>;
|
||||
type ServiceLinePayment = z.infer<
|
||||
typeof ServiceLinePaymentCreateInputObjectSchema
|
||||
>;
|
||||
|
||||
// Define Zod schemas
|
||||
const insertPaymentSchema = (
|
||||
PaymentUncheckedCreateInputObjectSchema as unknown as z.ZodObject<any>
|
||||
).omit({
|
||||
id: true,
|
||||
createdAt: true,
|
||||
updatedAt: true,
|
||||
});
|
||||
type InsertPayment = z.infer<typeof insertPaymentSchema>;
|
||||
|
||||
const updatePaymentSchema = (
|
||||
PaymentUncheckedCreateInputObjectSchema as unknown as z.ZodObject<any>
|
||||
)
|
||||
.omit({
|
||||
id: true,
|
||||
createdAt: true,
|
||||
})
|
||||
.partial();
|
||||
type UpdatePayment = z.infer<typeof updatePaymentSchema>;
|
||||
|
||||
type PaymentWithExtras = Prisma.PaymentGetPayload<{
|
||||
include: {
|
||||
transactions: true;
|
||||
servicePayments: true;
|
||||
claim: true;
|
||||
};
|
||||
}>;
|
||||
|
||||
// Claim schema
|
||||
const ClaimSchema = (
|
||||
ClaimUncheckedCreateInputObjectSchema as unknown as z.ZodObject<any>
|
||||
).omit({
|
||||
@@ -29,11 +70,42 @@ const updateClaimSchema = (
|
||||
|
||||
type UpdateClaim = z.infer<typeof updateClaimSchema>;
|
||||
|
||||
const paymentFilterSchema = z.object({
|
||||
from: z.string().datetime(),
|
||||
to: z.string().datetime(),
|
||||
});
|
||||
|
||||
function parseIntOrError(input: string | undefined, name: string) {
|
||||
if (!input) throw new Error(`${name} is required`);
|
||||
const value = parseInt(input, 10);
|
||||
if (isNaN(value)) throw new Error(`${name} must be a valid number`);
|
||||
return value;
|
||||
}
|
||||
|
||||
export function handleRouteError(
|
||||
res: Response,
|
||||
error: unknown,
|
||||
defaultMsg: string
|
||||
) {
|
||||
if (error instanceof ZodError) {
|
||||
return res.status(400).json({
|
||||
message: "Validation error",
|
||||
errors: error.format(),
|
||||
});
|
||||
}
|
||||
|
||||
const msg = error instanceof Error ? error.message : defaultMsg;
|
||||
return res.status(500).json({ message: msg });
|
||||
}
|
||||
|
||||
const router = Router();
|
||||
|
||||
// GET /api/payments/recent
|
||||
router.get('/recent', async (req, res) => {
|
||||
router.get("/recent", async (req: Request, res: Response): Promise<any> => {
|
||||
try {
|
||||
const userId = req.user.id;
|
||||
const userId = req.user?.id;
|
||||
if (!userId) return res.status(401).json({ message: "Unauthorized" });
|
||||
|
||||
const limit = parseInt(req.query.limit as string) || 10;
|
||||
const offset = parseInt(req.query.offset as string) || 0;
|
||||
|
||||
@@ -42,102 +114,164 @@ router.get('/recent', async (req, res) => {
|
||||
storage.getTotalPaymentCountByUser(userId),
|
||||
]);
|
||||
|
||||
res.json({ payments, totalCount });
|
||||
res.status(200).json({ payments, totalCount });
|
||||
} catch (err) {
|
||||
console.error('Failed to fetch payments:', err);
|
||||
res.status(500).json({ message: 'Failed to fetch recent payments' });
|
||||
console.error("Failed to fetch payments:", err);
|
||||
res.status(500).json({ message: "Failed to fetch recent payments" });
|
||||
}
|
||||
});
|
||||
|
||||
// GET /api/payments/claim/:claimId
|
||||
router.get('/claim/:claimId', async (req: Request, res: Response): Promise<any> => {
|
||||
try {
|
||||
const userId = req.user.id;
|
||||
const claimId = parseInt(req.params.claimId);
|
||||
router.get(
|
||||
"/claim/:claimId",
|
||||
async (req: Request, res: Response): Promise<any> => {
|
||||
try {
|
||||
const userId = req.user?.id;
|
||||
if (!userId) return res.status(401).json({ message: "Unauthorized" });
|
||||
|
||||
const payment = await storage.getPaymentByClaimId(userId, claimId);
|
||||
if (!payment) return res.status(404).json({ message: 'Payment not found' });
|
||||
const parsedClaimId = parseIntOrError(req.params.claimId, "Claim ID");
|
||||
|
||||
res.json(payment);
|
||||
} catch (err) {
|
||||
console.error('Failed to fetch payment by claim:', err);
|
||||
res.status(500).json({ message: 'Failed to fetch payment' });
|
||||
const payments = await storage.getPaymentsByClaimId(
|
||||
userId,
|
||||
parsedClaimId
|
||||
);
|
||||
if (!payments)
|
||||
return res.status(404).json({ message: "No payments found for claim" });
|
||||
|
||||
res.status(200).json(payments);
|
||||
} catch (error) {
|
||||
console.error("Error fetching payments:", error);
|
||||
res.status(500).json({ message: "Failed to retrieve payments" });
|
||||
}
|
||||
}
|
||||
});
|
||||
);
|
||||
|
||||
// GET /api/payments/patient/:patientId
|
||||
router.get('/patient/:patientId', async (req, res) => {
|
||||
try {
|
||||
const userId = req.user.id;
|
||||
const patientId = parseInt(req.params.patientId);
|
||||
router.get(
|
||||
"/patient/:patientId",
|
||||
async (req: Request, res: Response): Promise<any> => {
|
||||
try {
|
||||
const userId = req.user?.id;
|
||||
if (!userId) return res.status(401).json({ message: "Unauthorized" });
|
||||
|
||||
const payments = await storage.getPaymentsByPatientId(userId, patientId);
|
||||
res.json(payments);
|
||||
} catch (err) {
|
||||
console.error('Failed to fetch patient payments:', err);
|
||||
res.status(500).json({ message: 'Failed to fetch patient payments' });
|
||||
const parsedPatientId = parseIntOrError(
|
||||
req.params.patientId,
|
||||
"Patient ID"
|
||||
);
|
||||
|
||||
const payments = await storage.getPaymentsByPatientId(
|
||||
userId,
|
||||
parsedPatientId
|
||||
);
|
||||
|
||||
if (!payments)
|
||||
return res.status(404).json({ message: "No payments found for claim" });
|
||||
|
||||
res.status(200).json(payments);
|
||||
} catch (err) {
|
||||
console.error("Failed to fetch patient payments:", err);
|
||||
res.status(500).json({ message: "Failed to fetch patient payments" });
|
||||
}
|
||||
}
|
||||
});
|
||||
);
|
||||
|
||||
// GET /api/payments/filter
|
||||
router.get('/filter', async (req, res) => {
|
||||
router.get("/filter", async (req: Request, res: Response): Promise<any> => {
|
||||
try {
|
||||
const userId = req.user.id;
|
||||
const { from, to } = req.query;
|
||||
const fromDate = new Date(from as string);
|
||||
const toDate = new Date(to as string);
|
||||
const userId = req.user?.id;
|
||||
if (!userId) return res.status(401).json({ message: "Unauthorized" });
|
||||
|
||||
const payments = await storage.getPaymentsByDateRange(userId, fromDate, toDate);
|
||||
res.json(payments);
|
||||
const validated = paymentFilterSchema.safeParse(req.query);
|
||||
if (!validated.success) {
|
||||
return res.status(400).json({
|
||||
message: "Invalid date format",
|
||||
errors: validated.error.errors,
|
||||
});
|
||||
}
|
||||
|
||||
const { from, to } = validated.data;
|
||||
const payments = await storage.getPaymentsByDateRange(
|
||||
userId,
|
||||
new Date(from),
|
||||
new Date(to)
|
||||
);
|
||||
res.status(200).json(payments);
|
||||
} catch (err) {
|
||||
console.error('Failed to filter payments:', err);
|
||||
res.status(500).json({ message: 'Failed to filter payments' });
|
||||
console.error("Failed to filter payments:", err);
|
||||
res.status(500).json({ message: "Server error" });
|
||||
}
|
||||
});
|
||||
|
||||
// POST /api/payments/:claimId
|
||||
router.post('/:claimId', body('totalBilled').isDecimal(),(req: Request, res: Response): Promise<any> => {
|
||||
const errors = validationResult(req);
|
||||
if (!errors.isEmpty()) return res.status(400).json({ errors: errors.array() });
|
||||
|
||||
router.post("/:claimId", async (req: Request, res: Response): Promise<any> => {
|
||||
try {
|
||||
const userId = req.user.id;
|
||||
const claimId = parseInt(req.params.claimId);
|
||||
const { totalBilled } = req.body;
|
||||
const userId = req.user?.id;
|
||||
if (!userId) return res.status(401).json({ message: "Unauthorized" });
|
||||
|
||||
const payment = await storage.createPayment({ userId, claimId, totalBilled });
|
||||
const claimId = parseIntOrError(req.params.claimId, "Claim ID");
|
||||
|
||||
const validated = insertPaymentSchema.safeParse({
|
||||
...req.body,
|
||||
claimId,
|
||||
userId,
|
||||
});
|
||||
|
||||
if (!validated.success) {
|
||||
return res.status(400).json({
|
||||
message: "Validation failed",
|
||||
errors: validated.error.flatten(),
|
||||
});
|
||||
}
|
||||
|
||||
const payment = await storage.createPayment(validated.data);
|
||||
res.status(201).json(payment);
|
||||
} catch (err) {
|
||||
console.error('Failed to create payment:', err);
|
||||
res.status(500).json({ message: 'Failed to create payment' });
|
||||
} catch (err: unknown) {
|
||||
const message =
|
||||
err instanceof Error ? err.message : "Failed to create payment";
|
||||
res.status(500).json({ message });
|
||||
}
|
||||
});
|
||||
|
||||
// PUT /api/payments/:id
|
||||
router.put('/:id', async (req, res) => {
|
||||
router.put("/:id", async (req: Request, res: Response): Promise<any> => {
|
||||
try {
|
||||
const userId = req.user.id;
|
||||
const id = parseInt(req.params.id);
|
||||
const updates = req.body;
|
||||
const userId = req.user?.id;
|
||||
if (!userId) return res.status(401).json({ message: "Unauthorized" });
|
||||
|
||||
const updated = await storage.updatePayment(userId, id, updates);
|
||||
res.json(updated);
|
||||
} catch (err) {
|
||||
console.error('Failed to update payment:', err);
|
||||
res.status(500).json({ message: 'Failed to update payment' });
|
||||
const id = parseIntOrError(req.params.id, "Payment ID");
|
||||
|
||||
const validated = updatePaymentSchema.safeParse(req.body);
|
||||
if (!validated.success) {
|
||||
return res.status(400).json({
|
||||
message: "Validation failed",
|
||||
errors: validated.error.flatten(),
|
||||
});
|
||||
}
|
||||
|
||||
const updated = await storage.updatePayment(id, validated.data, userId);
|
||||
|
||||
res.status(200).json(updated);
|
||||
} catch (err: unknown) {
|
||||
const message =
|
||||
err instanceof Error ? err.message : "Failed to update payment";
|
||||
res.status(500).json({ message });
|
||||
}
|
||||
});
|
||||
|
||||
// DELETE /api/payments/:id
|
||||
router.delete('/:id', async (req, res) => {
|
||||
router.delete("/:id", async (req: Request, res: Response): Promise<any> => {
|
||||
try {
|
||||
const userId = req.user.id;
|
||||
const id = parseInt(req.params.id);
|
||||
const userId = req.user?.id;
|
||||
if (!userId) return res.status(401).json({ message: "Unauthorized" });
|
||||
|
||||
const id = parseIntOrError(req.params.id, "Payment ID");
|
||||
await storage.deletePayment(userId, id);
|
||||
res.json({ message: 'Payment deleted' });
|
||||
} catch (err) {
|
||||
console.error('Failed to delete payment:', err);
|
||||
res.status(500).json({ message: 'Failed to delete payment' });
|
||||
|
||||
res.status(200).json({ message: "Payment deleted successfully" });
|
||||
} catch (err: unknown) {
|
||||
const message =
|
||||
err instanceof Error ? err.message : "Failed to delete payment";
|
||||
res.status(500).json({ message });
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user