init files

This commit is contained in:
2025-07-30 23:20:15 +05:30
parent c5b27fbe7b
commit 23e4eb6b7c
6 changed files with 410 additions and 14 deletions

View File

@@ -0,0 +1,144 @@
import { Router } from "express";
import { Request, Response } from "express";
import { storage } from "../storage";
import { z } from "zod";
import { ClaimUncheckedCreateInputObjectSchema } from "@repo/db/usedSchemas";
const router = Router();
// Define Zod schemas
const ClaimSchema = (
ClaimUncheckedCreateInputObjectSchema as unknown as z.ZodObject<any>
).omit({
id: true,
createdAt: true,
updatedAt: true,
});
type InsertClaim = z.infer<typeof ClaimSchema>;
const updateClaimSchema = (
ClaimUncheckedCreateInputObjectSchema as unknown as z.ZodObject<any>
)
.omit({
id: true,
createdAt: true,
updatedAt: true,
})
.partial();
type UpdateClaim = z.infer<typeof updateClaimSchema>;
// GET /api/payments/recent
router.get('/recent', async (req, res) => {
try {
const userId = req.user.id;
const limit = parseInt(req.query.limit as string) || 10;
const offset = parseInt(req.query.offset as string) || 0;
const [payments, totalCount] = await Promise.all([
storage.getRecentPaymentsByUser(userId, limit, offset),
storage.getTotalPaymentCountByUser(userId),
]);
res.json({ payments, totalCount });
} catch (err) {
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);
const payment = await storage.getPaymentByClaimId(userId, claimId);
if (!payment) return res.status(404).json({ message: 'Payment not found' });
res.json(payment);
} catch (err) {
console.error('Failed to fetch payment by claim:', err);
res.status(500).json({ message: 'Failed to fetch payment' });
}
});
// GET /api/payments/patient/:patientId
router.get('/patient/:patientId', async (req, res) => {
try {
const userId = req.user.id;
const patientId = parseInt(req.params.patientId);
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' });
}
});
// GET /api/payments/filter
router.get('/filter', async (req, res) => {
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 payments = await storage.getPaymentsByDateRange(userId, fromDate, toDate);
res.json(payments);
} catch (err) {
console.error('Failed to filter payments:', err);
res.status(500).json({ message: 'Failed to filter payments' });
}
});
// 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() });
try {
const userId = req.user.id;
const claimId = parseInt(req.params.claimId);
const { totalBilled } = req.body;
const payment = await storage.createPayment({ userId, claimId, totalBilled });
res.status(201).json(payment);
} catch (err) {
console.error('Failed to create payment:', err);
res.status(500).json({ message: 'Failed to create payment' });
}
});
// PUT /api/payments/:id
router.put('/:id', async (req, res) => {
try {
const userId = req.user.id;
const id = parseInt(req.params.id);
const updates = req.body;
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' });
}
});
// DELETE /api/payments/:id
router.delete('/:id', async (req, res) => {
try {
const userId = req.user.id;
const id = parseInt(req.params.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' });
}
});
export default router;

View File

@@ -9,8 +9,13 @@ import {
PdfFileUncheckedCreateInputObjectSchema, PdfFileUncheckedCreateInputObjectSchema,
PdfGroupUncheckedCreateInputObjectSchema, PdfGroupUncheckedCreateInputObjectSchema,
PdfCategorySchema, PdfCategorySchema,
PaymentUncheckedCreateInputObjectSchema,
PaymentTransactionCreateInputObjectSchema,
ServiceLinePaymentCreateInputObjectSchema,
} from "@repo/db/usedSchemas"; } from "@repo/db/usedSchemas";
import { z } from "zod"; import { z } from "zod";
import { Prisma } from "@repo/db/generated/prisma";
//creating types out of schema auto generated. //creating types out of schema auto generated.
type Appointment = z.infer<typeof AppointmentUncheckedCreateInputObjectSchema>; type Appointment = z.infer<typeof AppointmentUncheckedCreateInputObjectSchema>;
@@ -160,6 +165,41 @@ export interface ClaimPdfMetadata {
uploadedAt: Date; uploadedAt: Date;
} }
// Base Payment type
type Payment = z.infer<typeof PaymentUncheckedCreateInputObjectSchema>;
type PaymentTransaction = z.infer<typeof PaymentTransactionCreateInputObjectSchema>;
type ServiceLinePayment = z.infer<typeof ServiceLinePaymentCreateInputObjectSchema>
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;
};
}>;
export interface IStorage { export interface IStorage {
// User methods // User methods
getUser(id: number): Promise<User | undefined>; getUser(id: number): Promise<User | undefined>;
@@ -290,7 +330,7 @@ export interface IStorage {
updates: Partial<Pick<PdfFile, "filename" | "pdfData">> updates: Partial<Pick<PdfFile, "filename" | "pdfData">>
): Promise<PdfFile | undefined>; ): Promise<PdfFile | undefined>;
// Group management // PDF Group management
createPdfGroup( createPdfGroup(
patientId: number, patientId: number,
title: string, title: string,
@@ -309,6 +349,17 @@ export interface IStorage {
updates: Partial<Pick<PdfGroup, "title" | "category">> updates: Partial<Pick<PdfGroup, "title" | "category">>
): Promise<PdfGroup | undefined>; ): Promise<PdfGroup | undefined>;
deletePdfGroup(id: number): Promise<boolean>; deletePdfGroup(id: number): Promise<boolean>;
// Payment methods:
createPayment(data: InsertPayment): Promise<Payment>;
updatePayment(id: number, updates: UpdatePayment): Promise<Payment>;
deletePayment(id: number): Promise<void>;
getPaymentById(id: number): Promise<PaymentWithExtras | null>;
getPaymentByClaimId(claimId: number): Promise<PaymentWithExtras | null>;
getPaymentsByPatientId(patientId: number, userId: number): Promise<PaymentWithExtras[]>;
getRecentPaymentsByUser(userId: number, limit: number, offset: number): Promise<PaymentWithExtras[]>;
getPaymentsByDateRange(userId: number, from: Date, to: Date): Promise<PaymentWithExtras[]>;
getTotalPaymentCountByUser(userId: number): Promise<number>;
} }
export const storage: IStorage = { export const storage: IStorage = {
@@ -831,4 +882,91 @@ export const storage: IStorage = {
return false; return false;
} }
}, },
// Payment Methods
async createPayment(payment: InsertPayment): Promise<Payment> {
return db.payment.create({ data: payment as Payment });
},
async updatePayment(id: number, updates: UpdatePayment): Promise<Payment> {
return db.payment.update({ where: { id }, data: updates });
},
async deletePayment(id: number): Promise<void> {
await db.payment.delete({ where: { id } });
},
async getPaymentById(id: number): Promise<PaymentWithExtras | null> {
return db.payment.findUnique({
where: { id },
include: {
claim: true,
transactions: true,
servicePayments: true,
},
});
},
async getPaymentByClaimId(claimId: number): Promise<PaymentWithExtras | null> {
return db.payment.findFirst({
where: { claimId },
include: {
claim: true,
transactions: true,
servicePayments: true,
},
});
},
async getPaymentsByPatientId(patientId: number, userId: number): Promise<PaymentWithExtras[]> {
return db.payment.findMany({
where: {
patientId,
userId,
},
include: {
claim: true,
transactions: true,
servicePayments: true,
},
});
},
async getRecentPaymentsByUser(userId: number, limit: number, offset: number): Promise<PaymentWithExtras[]> {
return db.payment.findMany({
where: { userId },
orderBy: { createdAt: "desc" },
skip: offset,
take: limit,
include: {
claim: true,
transactions: true,
servicePayments: true,
},
});
},
async getPaymentsByDateRange(userId: number, from: Date, to: Date): Promise<PaymentWithExtras[]> {
return db.payment.findMany({
where: {
userId,
createdAt: {
gte: from,
lte: to,
},
},
orderBy: { createdAt: "desc" },
include: {
claim: true,
transactions: true,
servicePayments: true,
},
});
},
async getTotalPaymentCountByUser(userId: number): Promise<number> {
return db.payment.count({ where: { userId } });
},
}; };

View File

@@ -6,8 +6,6 @@ import { Card, CardHeader, CardTitle, CardContent, CardFooter } from "@/componen
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";
import { useToast } from "@/hooks/use-toast"; import { useToast } from "@/hooks/use-toast";
import { useAuth } from "@/hooks/use-auth"; import { useAuth } from "@/hooks/use-auth";
// import { Patient, Appointment } from "@repo/db/shared/schemas";
import { Patient, Appointment } from "@repo/db/shared/schemas";
import { import {
CreditCard, CreditCard,
Clock, Clock,
@@ -35,6 +33,47 @@ import {
SelectTrigger, SelectTrigger,
SelectValue, SelectValue,
} from "@/components/ui/select"; } from "@/components/ui/select";
import { PatientUncheckedCreateInputObjectSchema, AppointmentUncheckedCreateInputObjectSchema } from "@repo/db/usedSchemas";
import { z } from "zod";
const PatientSchema = (
PatientUncheckedCreateInputObjectSchema as unknown as z.ZodObject<any>
).omit({
appointments: true,
});
type Patient = z.infer<typeof PatientSchema>;
const insertPatientSchema = (
PatientUncheckedCreateInputObjectSchema as unknown as z.ZodObject<any>
).omit({
id: true,
createdAt: true,
userId: true,
});
type InsertPatient = z.infer<typeof insertPatientSchema>;
//creating types out of schema auto generated.
type Appointment = z.infer<typeof AppointmentUncheckedCreateInputObjectSchema>;
const insertAppointmentSchema = (
AppointmentUncheckedCreateInputObjectSchema as unknown as z.ZodObject<any>
).omit({
id: true,
createdAt: true,
});
type InsertAppointment = z.infer<typeof insertAppointmentSchema>;
const updateAppointmentSchema = (
AppointmentUncheckedCreateInputObjectSchema as unknown as z.ZodObject<any>
)
.omit({
id: true,
createdAt: true,
userId: true,
})
.partial();
type UpdateAppointment = z.infer<typeof updateAppointmentSchema>;
export default function PaymentsPage() { export default function PaymentsPage() {
const [isMobileMenuOpen, setIsMobileMenuOpen] = useState(false); const [isMobileMenuOpen, setIsMobileMenuOpen] = useState(false);

View File

@@ -13,7 +13,8 @@
"exports": { "exports": {
"./client": "./src/index.ts", "./client": "./src/index.ts",
"./shared/schemas" : "./shared/schemas/index.ts", "./shared/schemas" : "./shared/schemas/index.ts",
"./usedSchemas" : "./usedSchemas/index.ts" "./usedSchemas" : "./usedSchemas/index.ts",
"./generated/prisma" : "./generated/prisma/index.d.ts"
}, },
"dependencies": { "dependencies": {
"@prisma/client": "^6.10.0", "@prisma/client": "^6.10.0",

View File

@@ -114,6 +114,7 @@ model Claim {
staff Staff? @relation("ClaimStaff", fields: [staffId], references: [id]) staff Staff? @relation("ClaimStaff", fields: [staffId], references: [id])
serviceLines ServiceLine[] serviceLines ServiceLine[]
payment Payment?
} }
enum ClaimStatus { enum ClaimStatus {
@@ -124,15 +125,16 @@ enum ClaimStatus {
} }
model ServiceLine { model ServiceLine {
id Int @id @default(autoincrement()) id Int @id @default(autoincrement())
claimId Int claimId Int
procedureCode String procedureCode String
procedureDate DateTime @db.Date procedureDate DateTime @db.Date
oralCavityArea String? oralCavityArea String?
toothNumber String? toothNumber String?
toothSurface String? toothSurface String?
billedAmount Float billedAmount Float
claim Claim @relation(fields: [claimId], references: [id], onDelete: Cascade) claim Claim @relation(fields: [claimId], references: [id], onDelete: Cascade)
servicePayments ServiceLinePayment[]
} }
model InsuranceCredential { model InsuranceCredential {
@@ -177,3 +179,72 @@ enum PdfCategory {
ELIGIBILITY ELIGIBILITY
OTHER OTHER
} }
model Payment {
id Int @id @default(autoincrement())
patientId Int
userId Int
claimId Int @unique
totalBilled Decimal @db.Decimal(10, 2)
totalPaid Decimal @default(0.00) @db.Decimal(10, 2)
totalDue Decimal @db.Decimal(10, 2)
status PaymentStatus @default(PENDING)
receivedDate DateTime?
paymentMethod PaymentMethod?
notes String?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
claim Claim @relation(fields: [claimId], references: [id], onDelete: Cascade)
transactions PaymentTransaction[]
servicePayments ServiceLinePayment[]
@@index([claimId])
}
model PaymentTransaction {
id Int @id @default(autoincrement())
paymentId Int
amount Decimal @db.Decimal(10, 2)
method PaymentMethod
transactionId String?
receivedDate DateTime
payerName String?
notes String?
createdAt DateTime @default(now())
payment Payment @relation(fields: [paymentId], references: [id], onDelete: Cascade)
@@index([paymentId])
}
model ServiceLinePayment {
id Int @id @default(autoincrement())
paymentId Int
serviceLineId Int
paidAmount Decimal @db.Decimal(10, 2)
adjustedAmount Decimal @default(0.00) @db.Decimal(10, 2)
notes String?
payment Payment @relation(fields: [paymentId], references: [id], onDelete: Cascade)
serviceLine ServiceLine @relation(fields: [serviceLineId], references: [id], onDelete: Cascade)
@@index([paymentId])
@@index([serviceLineId])
}
enum PaymentStatus {
PENDING
PARTIALLY_PAID
PAID
OVERPAID
DENIED
}
enum PaymentMethod {
EFT
CHECK
CASH
CARD
OTHER
}

View File

@@ -9,3 +9,6 @@ export * from '../shared/schemas/objects/PdfFileUncheckedCreateInput.schema'
export * from '../shared/schemas/objects/PdfGroupUncheckedCreateInput.schema' export * from '../shared/schemas/objects/PdfGroupUncheckedCreateInput.schema'
export * from '../shared/schemas/enums/PdfCategory.schema' export * from '../shared/schemas/enums/PdfCategory.schema'
export * from '../shared/schemas/enums/ClaimStatus.schema' export * from '../shared/schemas/enums/ClaimStatus.schema'
export * from '../shared/schemas/objects/PaymentUncheckedCreateInput.schema'
export * from '../shared/schemas/objects/PaymentTransactionCreateInput.schema'
export * from '../shared/schemas/objects/ServiceLinePaymentCreateInput.schema'