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> => {
|
||||
router.get(
|
||||
"/claim/:claimId",
|
||||
async (req: Request, res: Response): Promise<any> => {
|
||||
try {
|
||||
const userId = req.user.id;
|
||||
const claimId = parseInt(req.params.claimId);
|
||||
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) => {
|
||||
router.get(
|
||||
"/patient/:patientId",
|
||||
async (req: Request, res: Response): Promise<any> => {
|
||||
try {
|
||||
const userId = req.user.id;
|
||||
const patientId = parseInt(req.params.patientId);
|
||||
const userId = req.user?.id;
|
||||
if (!userId) return res.status(401).json({ message: "Unauthorized" });
|
||||
|
||||
const payments = await storage.getPaymentsByPatientId(userId, patientId);
|
||||
res.json(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' });
|
||||
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 });
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -16,7 +16,6 @@ import {
|
||||
import { z } from "zod";
|
||||
import { Prisma } from "@repo/db/generated/prisma";
|
||||
|
||||
|
||||
//creating types out of schema auto generated.
|
||||
type Appointment = z.infer<typeof AppointmentUncheckedCreateInputObjectSchema>;
|
||||
|
||||
@@ -167,9 +166,12 @@ export interface ClaimPdfMetadata {
|
||||
|
||||
// Base Payment type
|
||||
type Payment = z.infer<typeof PaymentUncheckedCreateInputObjectSchema>;
|
||||
type PaymentTransaction = z.infer<typeof PaymentTransactionCreateInputObjectSchema>;
|
||||
type ServiceLinePayment = z.infer<typeof ServiceLinePaymentCreateInputObjectSchema>
|
||||
|
||||
type PaymentTransaction = z.infer<
|
||||
typeof PaymentTransactionCreateInputObjectSchema
|
||||
>;
|
||||
type ServiceLinePayment = z.infer<
|
||||
typeof ServiceLinePaymentCreateInputObjectSchema
|
||||
>;
|
||||
|
||||
const insertPaymentSchema = (
|
||||
PaymentUncheckedCreateInputObjectSchema as unknown as z.ZodObject<any>
|
||||
@@ -180,7 +182,6 @@ const insertPaymentSchema = (
|
||||
});
|
||||
type InsertPayment = z.infer<typeof insertPaymentSchema>;
|
||||
|
||||
|
||||
const updatePaymentSchema = (
|
||||
PaymentUncheckedCreateInputObjectSchema as unknown as z.ZodObject<any>
|
||||
)
|
||||
@@ -191,7 +192,6 @@ const updatePaymentSchema = (
|
||||
.partial();
|
||||
type UpdatePayment = z.infer<typeof updatePaymentSchema>;
|
||||
|
||||
|
||||
type PaymentWithExtras = Prisma.PaymentGetPayload<{
|
||||
include: {
|
||||
transactions: true;
|
||||
@@ -352,13 +352,31 @@ export interface IStorage {
|
||||
|
||||
// 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[]>;
|
||||
updatePayment(
|
||||
id: number,
|
||||
updates: UpdatePayment,
|
||||
userId: number
|
||||
): Promise<Payment>;
|
||||
deletePayment(id: number, userId: number): Promise<void>;
|
||||
getPaymentById(id: number, userId: number): Promise<PaymentWithExtras | null>;
|
||||
getPaymentsByClaimId(
|
||||
claimId: number,
|
||||
userId: 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>;
|
||||
}
|
||||
|
||||
@@ -562,13 +580,15 @@ export const storage: IStorage = {
|
||||
date: Date,
|
||||
startTime: string
|
||||
): Promise<Appointment | undefined> {
|
||||
return await db.appointment.findFirst({
|
||||
return (
|
||||
(await db.appointment.findFirst({
|
||||
where: {
|
||||
patientId,
|
||||
date,
|
||||
startTime,
|
||||
},
|
||||
}) ?? undefined;
|
||||
})) ?? undefined
|
||||
);
|
||||
},
|
||||
|
||||
async getStaffAppointmentByDateTime(
|
||||
@@ -577,14 +597,16 @@ export const storage: IStorage = {
|
||||
startTime: string,
|
||||
excludeId?: number
|
||||
): Promise<Appointment | undefined> {
|
||||
return await db.appointment.findFirst({
|
||||
return (
|
||||
(await db.appointment.findFirst({
|
||||
where: {
|
||||
staffId,
|
||||
date,
|
||||
startTime,
|
||||
NOT: excludeId ? { id: excludeId } : undefined,
|
||||
},
|
||||
}) ?? undefined;
|
||||
})) ?? undefined
|
||||
);
|
||||
},
|
||||
|
||||
async getPatientConflictAppointment(
|
||||
@@ -593,14 +615,16 @@ export const storage: IStorage = {
|
||||
startTime: string,
|
||||
excludeId: number
|
||||
): Promise<Appointment | undefined> {
|
||||
return await db.appointment.findFirst({
|
||||
return (
|
||||
(await db.appointment.findFirst({
|
||||
where: {
|
||||
patientId,
|
||||
date,
|
||||
startTime,
|
||||
NOT: { id: excludeId },
|
||||
},
|
||||
}) ?? undefined;
|
||||
})) ?? undefined
|
||||
);
|
||||
},
|
||||
|
||||
async getStaffConflictAppointment(
|
||||
@@ -609,17 +633,18 @@ export const storage: IStorage = {
|
||||
startTime: string,
|
||||
excludeId: number
|
||||
): Promise<Appointment | undefined> {
|
||||
return await db.appointment.findFirst({
|
||||
return (
|
||||
(await db.appointment.findFirst({
|
||||
where: {
|
||||
staffId,
|
||||
date,
|
||||
startTime,
|
||||
NOT: { id: excludeId },
|
||||
},
|
||||
}) ?? undefined;
|
||||
})) ?? undefined
|
||||
);
|
||||
},
|
||||
|
||||
|
||||
// Staff methods
|
||||
async getStaff(id: number): Promise<Staff | undefined> {
|
||||
const staff = await db.staff.findUnique({ where: { id } });
|
||||
@@ -884,33 +909,41 @@ export const storage: IStorage = {
|
||||
},
|
||||
|
||||
// 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> {
|
||||
async updatePayment(
|
||||
id: number,
|
||||
updates: UpdatePayment,
|
||||
userId: number
|
||||
): Promise<Payment> {
|
||||
const existing = await db.payment.findFirst({ where: { id, userId } });
|
||||
if (!existing) {
|
||||
throw new Error("Not authorized or payment not found");
|
||||
}
|
||||
|
||||
return db.payment.update({
|
||||
where: { id },
|
||||
data: updates,
|
||||
});
|
||||
},
|
||||
|
||||
async deletePayment(id: number, userId: number): Promise<void> {
|
||||
const existing = await db.payment.findFirst({ where: { id, userId } });
|
||||
if (!existing) {
|
||||
throw new Error("Not authorized or payment not found");
|
||||
}
|
||||
|
||||
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> {
|
||||
async getPaymentById(
|
||||
id: number,
|
||||
userId: number
|
||||
): Promise<PaymentWithExtras | null> {
|
||||
return db.payment.findFirst({
|
||||
where: { claimId },
|
||||
where: { id, userId },
|
||||
include: {
|
||||
claim: true,
|
||||
transactions: true,
|
||||
@@ -919,8 +952,24 @@ export const storage: IStorage = {
|
||||
});
|
||||
},
|
||||
|
||||
async getPaymentsByClaimId(
|
||||
claimId: number,
|
||||
userId: number
|
||||
): Promise<PaymentWithExtras | null> {
|
||||
return db.payment.findFirst({
|
||||
where: { claimId, userId },
|
||||
include: {
|
||||
claim: true,
|
||||
transactions: true,
|
||||
servicePayments: true,
|
||||
},
|
||||
});
|
||||
},
|
||||
|
||||
async getPaymentsByPatientId(patientId: number, userId: number): Promise<PaymentWithExtras[]> {
|
||||
async getPaymentsByPatientId(
|
||||
patientId: number,
|
||||
userId: number
|
||||
): Promise<PaymentWithExtras[]> {
|
||||
return db.payment.findMany({
|
||||
where: {
|
||||
patientId,
|
||||
@@ -934,7 +983,11 @@ export const storage: IStorage = {
|
||||
});
|
||||
},
|
||||
|
||||
async getRecentPaymentsByUser(userId: number, limit: number, offset: number): Promise<PaymentWithExtras[]> {
|
||||
async getRecentPaymentsByUser(
|
||||
userId: number,
|
||||
limit: number,
|
||||
offset: number
|
||||
): Promise<PaymentWithExtras[]> {
|
||||
return db.payment.findMany({
|
||||
where: { userId },
|
||||
orderBy: { createdAt: "desc" },
|
||||
@@ -948,7 +1001,11 @@ export const storage: IStorage = {
|
||||
});
|
||||
},
|
||||
|
||||
async getPaymentsByDateRange(userId: number, from: Date, to: Date): Promise<PaymentWithExtras[]> {
|
||||
async getPaymentsByDateRange(
|
||||
userId: number,
|
||||
from: Date,
|
||||
to: Date
|
||||
): Promise<PaymentWithExtras[]> {
|
||||
return db.payment.findMany({
|
||||
where: {
|
||||
userId,
|
||||
|
||||
141
apps/Frontend/src/components/payments/payment-edit-modal.tsx
Normal file
141
apps/Frontend/src/components/payments/payment-edit-modal.tsx
Normal file
@@ -0,0 +1,141 @@
|
||||
import { useState } from "react";
|
||||
import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogFooter } from "@/components/ui/dialog";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Label } from "@/components/ui/label";
|
||||
import { Textarea } from "@/components/ui/textarea";
|
||||
import { apiRequest, queryClient } from "@/lib/queryClient";
|
||||
import { useToast } from "@/hooks/use-toast";
|
||||
import { z } from "zod";
|
||||
import { format } from "date-fns";
|
||||
import { PaymentUncheckedCreateInputObjectSchema } from "@repo/db/usedSchemas";
|
||||
|
||||
type Payment = z.infer<typeof PaymentUncheckedCreateInputObjectSchema>;
|
||||
|
||||
interface PaymentEditModalProps {
|
||||
isOpen: boolean;
|
||||
onClose: () => void;
|
||||
payment: Payment;
|
||||
onSave: () => void;
|
||||
}
|
||||
|
||||
export default function PaymentEditModal({
|
||||
isOpen,
|
||||
onClose,
|
||||
payment,
|
||||
onSave,
|
||||
}: PaymentEditModalProps) {
|
||||
const { toast } = useToast();
|
||||
const [form, setForm] = useState({
|
||||
payerName: payment.payerName,
|
||||
amountPaid: payment.amountPaid.toString(),
|
||||
paymentDate: format(new Date(payment.paymentDate), "yyyy-MM-dd"),
|
||||
paymentMethod: payment.paymentMethod,
|
||||
note: payment.note || "",
|
||||
});
|
||||
|
||||
const [loading, setLoading] = useState(false);
|
||||
|
||||
const handleChange = (e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>) => {
|
||||
const { name, value } = e.target;
|
||||
setForm({ ...form, [name]: value });
|
||||
};
|
||||
|
||||
const handleSubmit = async () => {
|
||||
setLoading(true);
|
||||
try {
|
||||
const res = await apiRequest("PUT", `/api/payments/${payment.id}`, {
|
||||
...form,
|
||||
amountPaid: parseFloat(form.amountPaid),
|
||||
paymentDate: new Date(form.paymentDate),
|
||||
});
|
||||
|
||||
if (!res.ok) throw new Error("Failed to update payment");
|
||||
|
||||
toast({ title: "Success", description: "Payment updated successfully" });
|
||||
queryClient.invalidateQueries();
|
||||
onClose();
|
||||
onSave();
|
||||
} catch (error) {
|
||||
toast({
|
||||
title: "Error",
|
||||
description: "Failed to update payment",
|
||||
variant: "destructive",
|
||||
});
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Dialog open={isOpen} onOpenChange={onClose}>
|
||||
<DialogContent>
|
||||
<DialogHeader>
|
||||
<DialogTitle>Edit Payment</DialogTitle>
|
||||
</DialogHeader>
|
||||
|
||||
<div className="space-y-4">
|
||||
<div className="grid gap-2">
|
||||
<Label htmlFor="payerName">Payer Name</Label>
|
||||
<Input
|
||||
id="payerName"
|
||||
name="payerName"
|
||||
value={form.payerName}
|
||||
onChange={handleChange}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="grid gap-2">
|
||||
<Label htmlFor="amountPaid">Amount Paid</Label>
|
||||
<Input
|
||||
id="amountPaid"
|
||||
name="amountPaid"
|
||||
type="number"
|
||||
step="0.01"
|
||||
value={form.amountPaid}
|
||||
onChange={handleChange}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="grid gap-2">
|
||||
<Label htmlFor="paymentDate">Payment Date</Label>
|
||||
<Input
|
||||
id="paymentDate"
|
||||
name="paymentDate"
|
||||
type="date"
|
||||
value={form.paymentDate}
|
||||
onChange={handleChange}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="grid gap-2">
|
||||
<Label htmlFor="paymentMethod">Payment Method</Label>
|
||||
<Input
|
||||
id="paymentMethod"
|
||||
name="paymentMethod"
|
||||
value={form.paymentMethod}
|
||||
onChange={handleChange}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="grid gap-2">
|
||||
<Label htmlFor="note">Note</Label>
|
||||
<Textarea
|
||||
id="note"
|
||||
name="note"
|
||||
value={form.note}
|
||||
onChange={handleChange}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<DialogFooter>
|
||||
<Button variant="ghost" onClick={onClose} disabled={loading}>Cancel</Button>
|
||||
<Button onClick={handleSubmit} disabled={loading}>
|
||||
{loading ? "Saving..." : "Save Changes"}
|
||||
</Button>
|
||||
</DialogFooter>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
);
|
||||
}
|
||||
35
apps/Frontend/src/components/payments/payment-view-modal.tsx
Normal file
35
apps/Frontend/src/components/payments/payment-view-modal.tsx
Normal file
@@ -0,0 +1,35 @@
|
||||
import { Dialog, DialogContent, DialogHeader, DialogTitle } from "@/components/ui/dialog";
|
||||
import { formatDateToHumanReadable } from "@/utils/dateUtils";
|
||||
import { z } from "zod";
|
||||
import { PaymentUncheckedCreateInputObjectSchema } from "@repo/db/usedSchemas";
|
||||
|
||||
type Payment = z.infer<typeof PaymentUncheckedCreateInputObjectSchema>;
|
||||
|
||||
interface PaymentViewModalProps {
|
||||
isOpen: boolean;
|
||||
onClose: () => void;
|
||||
payment: Payment;
|
||||
}
|
||||
|
||||
export default function PaymentViewModal({ isOpen, onClose, payment }: PaymentViewModalProps) {
|
||||
return (
|
||||
<Dialog open={isOpen} onOpenChange={onClose}>
|
||||
<DialogContent>
|
||||
<DialogHeader>
|
||||
<DialogTitle>Payment Details</DialogTitle>
|
||||
</DialogHeader>
|
||||
|
||||
<div className="space-y-4 text-sm">
|
||||
<div><strong>Payment ID:</strong> PAY-{payment.id.toString().padStart(4, "0")}</div>
|
||||
<div><strong>Claim ID:</strong> {payment.claimId}</div>
|
||||
<div><strong>Payer Name:</strong> {payment.payerName}</div>
|
||||
<div><strong>Amount Paid:</strong> ${payment.amountPaid.toFixed(2)}</div>
|
||||
<div><strong>Payment Date:</strong> {formatDateToHumanReadable(payment.paymentDate)}</div>
|
||||
<div><strong>Payment Method:</strong> {payment.paymentMethod}</div>
|
||||
<div><strong>Note:</strong> {payment.note || "—"}</div>
|
||||
<div><strong>Created At:</strong> {formatDateToHumanReadable(payment.createdAt)}</div>
|
||||
</div>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
);
|
||||
}
|
||||
295
apps/Frontend/src/components/payments/payments-recent-table.tsx
Normal file
295
apps/Frontend/src/components/payments/payments-recent-table.tsx
Normal file
@@ -0,0 +1,295 @@
|
||||
import { useState, useEffect, useMemo } from "react";
|
||||
import { useQuery, useMutation } from "@tanstack/react-query";
|
||||
import {
|
||||
Table, TableBody, TableCell, TableHead, TableHeader, TableRow,
|
||||
} from "@/components/ui/table";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Edit, Eye, Delete } from "lucide-react";
|
||||
import { apiRequest, queryClient } from "@/lib/queryClient";
|
||||
import { useToast } from "@/hooks/use-toast";
|
||||
import { formatDateToHumanReadable } from "@/utils/dateUtils";
|
||||
import { Pagination, PaginationContent, PaginationItem, PaginationLink, PaginationNext, PaginationPrevious } from "@/components/ui/pagination";
|
||||
import { Checkbox } from "@/components/ui/checkbox";
|
||||
import { PaymentUncheckedCreateInputObjectSchema, PaymentTransactionCreateInputObjectSchema, ServiceLinePaymentCreateInputObjectSchema } from "@repo/db/usedSchemas";
|
||||
import { z } from "zod";
|
||||
import { DeleteConfirmationDialog } from "../ui/deleteDialog";
|
||||
import PaymentViewModal from "./payment-view-modal";
|
||||
import PaymentEditModal from "./payment-edit-modal";
|
||||
|
||||
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;
|
||||
};
|
||||
}>;
|
||||
|
||||
interface PaymentApiResponse {
|
||||
payments: Payment[];
|
||||
totalCount: number;
|
||||
}
|
||||
|
||||
|
||||
interface PaymentsRecentTableProps {
|
||||
allowEdit?: boolean;
|
||||
allowView?: boolean;
|
||||
allowDelete?: boolean;
|
||||
allowCheckbox?: boolean;
|
||||
onSelectPayment?: (payment: Payment | null) => void;
|
||||
onPageChange?: (page: number) => void;
|
||||
claimId?: number;
|
||||
}
|
||||
|
||||
export default function PaymentsRecentTable({
|
||||
allowEdit,
|
||||
allowView,
|
||||
allowDelete,
|
||||
allowCheckbox,
|
||||
onSelectPayment,
|
||||
onPageChange,
|
||||
claimId,
|
||||
}: PaymentsRecentTableProps) {
|
||||
const { toast } = useToast();
|
||||
const [currentPage, setCurrentPage] = useState(1);
|
||||
const paymentsPerPage = 5;
|
||||
const offset = (currentPage - 1) * paymentsPerPage;
|
||||
|
||||
const [selectedPaymentId, setSelectedPaymentId] = useState<number | null>(null);
|
||||
const [currentPayment, setCurrentPayment] = useState<Payment | null>(null);
|
||||
const [isViewOpen, setIsViewOpen] = useState(false);
|
||||
const [isEditOpen, setIsEditOpen] = useState(false);
|
||||
const [isDeleteOpen, setIsDeleteOpen] = useState(false);
|
||||
|
||||
const getQueryKey = () =>
|
||||
claimId
|
||||
? ["payments", "claim", claimId, currentPage]
|
||||
: ["payments", "recent", currentPage];
|
||||
|
||||
const {
|
||||
data: paymentsData,
|
||||
isLoading,
|
||||
isError,
|
||||
} = useQuery<PaymentApiResponse>({
|
||||
queryKey: getQueryKey(),
|
||||
queryFn: async () => {
|
||||
const endpoint = claimId
|
||||
? `/api/payments/claim/${claimId}?limit=${paymentsPerPage}&offset=${offset}`
|
||||
: `/api/payments/recent?limit=${paymentsPerPage}&offset=${offset}`;
|
||||
|
||||
const res = await apiRequest("GET", endpoint);
|
||||
if (!res.ok) throw new Error("Failed to fetch payments");
|
||||
return res.json();
|
||||
},
|
||||
placeholderData: { payments: [], totalCount: 0 },
|
||||
});
|
||||
|
||||
const deleteMutation = useMutation({
|
||||
mutationFn: async (id: number) => {
|
||||
const res = await apiRequest("DELETE", `/api/payments/${id}`);
|
||||
if (!res.ok) throw new Error("Failed to delete");
|
||||
},
|
||||
onSuccess: () => {
|
||||
toast({ title: "Deleted", description: "Payment deleted successfully" });
|
||||
setIsDeleteOpen(false);
|
||||
queryClient.invalidateQueries({ queryKey: getQueryKey() });
|
||||
},
|
||||
onError: () => {
|
||||
toast({ title: "Error", description: "Delete failed", variant: "destructive" });
|
||||
},
|
||||
});
|
||||
|
||||
const handleSelectPayment = (payment: Payment) => {
|
||||
const isSelected = selectedPaymentId === payment.id;
|
||||
const newSelectedId = isSelected ? null : payment.id;
|
||||
setSelectedPaymentId(newSelectedId);
|
||||
onSelectPayment?.(isSelected ? null : payment);
|
||||
};
|
||||
|
||||
const handleDelete = () => {
|
||||
if (currentPayment?.id) {
|
||||
deleteMutation.mutate(currentPayment.id);
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
onPageChange?.(currentPage);
|
||||
}, [currentPage]);
|
||||
|
||||
const totalPages = useMemo(
|
||||
() => Math.ceil((paymentsData?.totalCount || 0) / paymentsPerPage),
|
||||
[paymentsData]
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="bg-white rounded shadow">
|
||||
<div className="overflow-x-auto">
|
||||
<Table>
|
||||
<TableHeader>
|
||||
<TableRow>
|
||||
{allowCheckbox && <TableHead>Select</TableHead>}
|
||||
<TableHead>Payment ID</TableHead>
|
||||
<TableHead>Payer</TableHead>
|
||||
<TableHead>Amount</TableHead>
|
||||
<TableHead>Date</TableHead>
|
||||
<TableHead>Method</TableHead>
|
||||
<TableHead>Note</TableHead>
|
||||
<TableHead className="text-right">Actions</TableHead>
|
||||
</TableRow>
|
||||
</TableHeader>
|
||||
<TableBody>
|
||||
{isLoading ? (
|
||||
<TableRow>
|
||||
<TableCell colSpan={8} className="text-center py-6">Loading...</TableCell>
|
||||
</TableRow>
|
||||
) : isError ? (
|
||||
<TableRow>
|
||||
<TableCell colSpan={8} className="text-center text-red-500 py-6">Error loading payments</TableCell>
|
||||
</TableRow>
|
||||
) : paymentsData?.payments.length === 0 ? (
|
||||
<TableRow>
|
||||
<TableCell colSpan={8} className="text-center py-6 text-muted-foreground">No payments found</TableCell>
|
||||
</TableRow>
|
||||
) : (
|
||||
paymentsData?.payments.map((payment) => (
|
||||
<TableRow key={payment.id}>
|
||||
{allowCheckbox && (
|
||||
<TableCell>
|
||||
<Checkbox
|
||||
checked={selectedPaymentId === payment.id}
|
||||
onCheckedChange={() => handleSelectPayment(payment)}
|
||||
/>
|
||||
</TableCell>
|
||||
)}
|
||||
<TableCell>{`PAY-${payment.id.toString().padStart(4, "0")}`}</TableCell>
|
||||
<TableCell>{payment.payerName}</TableCell>
|
||||
<TableCell>${payment.amountPaid.toFixed(2)}</TableCell>
|
||||
<TableCell>{formatDateToHumanReadable(payment.paymentDate)}</TableCell>
|
||||
<TableCell>{payment.paymentMethod}</TableCell>
|
||||
<TableCell>{payment.note || "—"}</TableCell>
|
||||
<TableCell className="text-right space-x-2">
|
||||
{allowDelete && (
|
||||
<Button variant="ghost" size="icon" onClick={() => { setCurrentPayment(payment); setIsDeleteOpen(true); }}>
|
||||
<Delete className="text-red-600" />
|
||||
</Button>
|
||||
)}
|
||||
{allowEdit && (
|
||||
<Button variant="ghost" size="icon" onClick={() => { setCurrentPayment(payment); setIsEditOpen(true); }}>
|
||||
<Edit />
|
||||
</Button>
|
||||
)}
|
||||
{allowView && (
|
||||
<Button variant="ghost" size="icon" onClick={() => { setCurrentPayment(payment); setIsViewOpen(true); }}>
|
||||
<Eye />
|
||||
</Button>
|
||||
)}
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
))
|
||||
)}
|
||||
</TableBody>
|
||||
</Table>
|
||||
</div>
|
||||
|
||||
{/* Pagination */}
|
||||
{totalPages > 1 && (
|
||||
<div className="px-4 py-2 border-t flex items-center justify-between">
|
||||
<div className="text-sm text-muted-foreground">
|
||||
Showing {(offset + 1)}–{Math.min(offset + paymentsPerPage, paymentsData?.totalCount || 0)} of {paymentsData?.totalCount || 0}
|
||||
</div>
|
||||
<Pagination>
|
||||
<PaginationContent>
|
||||
<PaginationItem>
|
||||
<PaginationPrevious
|
||||
href="#"
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
if (currentPage > 1) setCurrentPage(currentPage - 1);
|
||||
}}
|
||||
className={currentPage === 1 ? "pointer-events-none opacity-50" : ""}
|
||||
/>
|
||||
</PaginationItem>
|
||||
{Array.from({ length: totalPages }).map((_, i) => (
|
||||
<PaginationItem key={i}>
|
||||
<PaginationLink
|
||||
href="#"
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
setCurrentPage(i + 1);
|
||||
}}
|
||||
isActive={currentPage === i + 1}
|
||||
>
|
||||
{i + 1}
|
||||
</PaginationLink>
|
||||
</PaginationItem>
|
||||
))}
|
||||
<PaginationItem>
|
||||
<PaginationNext
|
||||
href="#"
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
if (currentPage < totalPages) setCurrentPage(currentPage + 1);
|
||||
}}
|
||||
className={currentPage === totalPages ? "pointer-events-none opacity-50" : ""}
|
||||
/>
|
||||
</PaginationItem>
|
||||
</PaginationContent>
|
||||
</Pagination>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Modals */}
|
||||
{isViewOpen && currentPayment && (
|
||||
<PaymentViewModal
|
||||
isOpen={isViewOpen}
|
||||
onClose={() => setIsViewOpen(false)}
|
||||
payment={currentPayment}
|
||||
/>
|
||||
)}
|
||||
|
||||
{isEditOpen && currentPayment && (
|
||||
<PaymentEditModal
|
||||
isOpen={isEditOpen}
|
||||
onClose={() => setIsEditOpen(false)}
|
||||
payment={currentPayment}
|
||||
onSave={() => {
|
||||
queryClient.invalidateQueries({ queryKey: getQueryKey() });
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
|
||||
<DeleteConfirmationDialog
|
||||
isOpen={isDeleteOpen}
|
||||
onCancel={() => setIsDeleteOpen(false)}
|
||||
onConfirm={handleDelete}
|
||||
entityName={`Payment ${currentPayment?.id}`}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
501
package-lock.json
generated
501
package-lock.json
generated
@@ -13,6 +13,7 @@
|
||||
"packages/*"
|
||||
],
|
||||
"dependencies": {
|
||||
"@prisma/client": "^6.13.0",
|
||||
"@reduxjs/toolkit": "^2.8.2",
|
||||
"dotenv": "^16.5.0",
|
||||
"dotenv-cli": "^8.0.0",
|
||||
@@ -23,6 +24,7 @@
|
||||
"@types/react-redux": "^7.1.34",
|
||||
"prettier": "^3.5.3",
|
||||
"prettier-plugin-tailwindcss": "^0.6.11",
|
||||
"prisma": "^6.13.0",
|
||||
"turbo": "^2.5.3"
|
||||
},
|
||||
"engines": {
|
||||
@@ -1518,9 +1520,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@prisma/client": {
|
||||
"version": "6.10.1",
|
||||
"resolved": "https://registry.npmjs.org/@prisma/client/-/client-6.10.1.tgz",
|
||||
"integrity": "sha512-Re4pMlcUsQsUTAYMK7EJ4Bw2kg3WfZAAlr8GjORJaK4VOP6LxRQUQ1TuLnxcF42XqGkWQ36q5CQF1yVadANQ6w==",
|
||||
"version": "6.13.0",
|
||||
"resolved": "https://registry.npmjs.org/@prisma/client/-/client-6.13.0.tgz",
|
||||
"integrity": "sha512-8m2+I3dQovkV8CkDMluiwEV1TxV9EXdT6xaCz39O6jYw7mkf5gwfmi+cL4LJsEPwz5tG7sreBwkRpEMJedGYUQ==",
|
||||
"hasInstallScript": true,
|
||||
"license": "Apache-2.0",
|
||||
"engines": {
|
||||
@@ -1540,53 +1542,56 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@prisma/config": {
|
||||
"version": "6.10.1",
|
||||
"resolved": "https://registry.npmjs.org/@prisma/config/-/config-6.10.1.tgz",
|
||||
"integrity": "sha512-kz4/bnqrOrzWo8KzYguN0cden4CzLJJ+2VSpKtF8utHS3l1JS0Lhv6BLwpOX6X9yNreTbZQZwewb+/BMPDCIYQ==",
|
||||
"version": "6.13.0",
|
||||
"resolved": "https://registry.npmjs.org/@prisma/config/-/config-6.13.0.tgz",
|
||||
"integrity": "sha512-OYMM+pcrvj/NqNWCGESSxVG3O7kX6oWuGyvufTUNnDw740KIQvNyA4v0eILgkpuwsKIDU36beZCkUtIt0naTog==",
|
||||
"devOptional": true,
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"jiti": "2.4.2"
|
||||
"c12": "3.1.0",
|
||||
"deepmerge-ts": "7.1.5",
|
||||
"effect": "3.16.12",
|
||||
"read-package-up": "11.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@prisma/debug": {
|
||||
"version": "6.10.1",
|
||||
"resolved": "https://registry.npmjs.org/@prisma/debug/-/debug-6.10.1.tgz",
|
||||
"integrity": "sha512-k2YT53cWxv9OLjW4zSYTZ6Z7j0gPfCzcr2Mj99qsuvlxr8WAKSZ2NcSR0zLf/mP4oxnYG842IMj3utTgcd7CaA==",
|
||||
"version": "6.13.0",
|
||||
"resolved": "https://registry.npmjs.org/@prisma/debug/-/debug-6.13.0.tgz",
|
||||
"integrity": "sha512-um+9pfKJW0ihmM83id9FXGi5qEbVJ0Vxi1Gm0xpYsjwUBnw6s2LdPBbrsG9QXRX46K4CLWCTNvskXBup4i9hlw==",
|
||||
"devOptional": true,
|
||||
"license": "Apache-2.0"
|
||||
},
|
||||
"node_modules/@prisma/engines": {
|
||||
"version": "6.10.1",
|
||||
"resolved": "https://registry.npmjs.org/@prisma/engines/-/engines-6.10.1.tgz",
|
||||
"integrity": "sha512-Q07P5rS2iPwk2IQr/rUQJ42tHjpPyFcbiH7PXZlV81Ryr9NYIgdxcUrwgVOWVm5T7ap02C0dNd1dpnNcSWig8A==",
|
||||
"version": "6.13.0",
|
||||
"resolved": "https://registry.npmjs.org/@prisma/engines/-/engines-6.13.0.tgz",
|
||||
"integrity": "sha512-D+1B79LFvtWA0KTt8ALekQ6A/glB9w10ETknH5Y9g1k2NYYQOQy93ffiuqLn3Pl6IPJG3EsK/YMROKEaq8KBrA==",
|
||||
"devOptional": true,
|
||||
"hasInstallScript": true,
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@prisma/debug": "6.10.1",
|
||||
"@prisma/engines-version": "6.10.1-1.9b628578b3b7cae625e8c927178f15a170e74a9c",
|
||||
"@prisma/fetch-engine": "6.10.1",
|
||||
"@prisma/get-platform": "6.10.1"
|
||||
"@prisma/debug": "6.13.0",
|
||||
"@prisma/engines-version": "6.13.0-35.361e86d0ea4987e9f53a565309b3eed797a6bcbd",
|
||||
"@prisma/fetch-engine": "6.13.0",
|
||||
"@prisma/get-platform": "6.13.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@prisma/engines-version": {
|
||||
"version": "6.10.1-1.9b628578b3b7cae625e8c927178f15a170e74a9c",
|
||||
"resolved": "https://registry.npmjs.org/@prisma/engines-version/-/engines-version-6.10.1-1.9b628578b3b7cae625e8c927178f15a170e74a9c.tgz",
|
||||
"integrity": "sha512-ZJFTsEqapiTYVzXya6TUKYDFnSWCNegfUiG5ik9fleQva5Sk3DNyyUi7X1+0ZxWFHwHDr6BZV5Vm+iwP+LlciA==",
|
||||
"version": "6.13.0-35.361e86d0ea4987e9f53a565309b3eed797a6bcbd",
|
||||
"resolved": "https://registry.npmjs.org/@prisma/engines-version/-/engines-version-6.13.0-35.361e86d0ea4987e9f53a565309b3eed797a6bcbd.tgz",
|
||||
"integrity": "sha512-MpPyKSzBX7P/ZY9odp9TSegnS/yH3CSbchQE9f0yBg3l2QyN59I6vGXcoYcqKC9VTniS1s18AMmhyr1OWavjHg==",
|
||||
"devOptional": true,
|
||||
"license": "Apache-2.0"
|
||||
},
|
||||
"node_modules/@prisma/fetch-engine": {
|
||||
"version": "6.10.1",
|
||||
"resolved": "https://registry.npmjs.org/@prisma/fetch-engine/-/fetch-engine-6.10.1.tgz",
|
||||
"integrity": "sha512-clmbG/Jgmrc/n6Y77QcBmAUlq9LrwI9Dbgy4pq5jeEARBpRCWJDJ7PWW1P8p0LfFU0i5fsyO7FqRzRB8mkdS4g==",
|
||||
"version": "6.13.0",
|
||||
"resolved": "https://registry.npmjs.org/@prisma/fetch-engine/-/fetch-engine-6.13.0.tgz",
|
||||
"integrity": "sha512-grmmq+4FeFKmaaytA8Ozc2+Tf3BC8xn/DVJos6LL022mfRlMZYjT3hZM0/xG7+5fO95zFG9CkDUs0m1S2rXs5Q==",
|
||||
"devOptional": true,
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@prisma/debug": "6.10.1",
|
||||
"@prisma/engines-version": "6.10.1-1.9b628578b3b7cae625e8c927178f15a170e74a9c",
|
||||
"@prisma/get-platform": "6.10.1"
|
||||
"@prisma/debug": "6.13.0",
|
||||
"@prisma/engines-version": "6.13.0-35.361e86d0ea4987e9f53a565309b3eed797a6bcbd",
|
||||
"@prisma/get-platform": "6.13.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@prisma/generator-helper": {
|
||||
@@ -1650,13 +1655,13 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@prisma/get-platform": {
|
||||
"version": "6.10.1",
|
||||
"resolved": "https://registry.npmjs.org/@prisma/get-platform/-/get-platform-6.10.1.tgz",
|
||||
"integrity": "sha512-4CY5ndKylcsce9Mv+VWp5obbR2/86SHOLVV053pwIkhVtT9C9A83yqiqI/5kJM9T1v1u1qco/bYjDKycmei9HA==",
|
||||
"version": "6.13.0",
|
||||
"resolved": "https://registry.npmjs.org/@prisma/get-platform/-/get-platform-6.13.0.tgz",
|
||||
"integrity": "sha512-Nii2pX50fY4QKKxQwm7/vvqT6Ku8yYJLZAFX4e2vzHwRdMqjugcOG5hOSLjxqoXb0cvOspV70TOhMzrw8kqAnw==",
|
||||
"devOptional": true,
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@prisma/debug": "6.10.1"
|
||||
"@prisma/debug": "6.13.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@prisma/internals": {
|
||||
@@ -5554,6 +5559,65 @@
|
||||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/c12": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/c12/-/c12-3.1.0.tgz",
|
||||
"integrity": "sha512-uWoS8OU1MEIsOv8p/5a82c3H31LsWVR5qiyXVfBNOzfffjUWtPnhAb4BYI2uG2HfGmZmFjCtui5XNWaps+iFuw==",
|
||||
"devOptional": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"chokidar": "^4.0.3",
|
||||
"confbox": "^0.2.2",
|
||||
"defu": "^6.1.4",
|
||||
"dotenv": "^16.6.1",
|
||||
"exsolve": "^1.0.7",
|
||||
"giget": "^2.0.0",
|
||||
"jiti": "^2.4.2",
|
||||
"ohash": "^2.0.11",
|
||||
"pathe": "^2.0.3",
|
||||
"perfect-debounce": "^1.0.0",
|
||||
"pkg-types": "^2.2.0",
|
||||
"rc9": "^2.1.2"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"magicast": "^0.3.5"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"magicast": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/c12/node_modules/chokidar": {
|
||||
"version": "4.0.3",
|
||||
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz",
|
||||
"integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==",
|
||||
"devOptional": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"readdirp": "^4.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 14.16.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://paulmillr.com/funding/"
|
||||
}
|
||||
},
|
||||
"node_modules/c12/node_modules/readdirp": {
|
||||
"version": "4.1.2",
|
||||
"resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz",
|
||||
"integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==",
|
||||
"devOptional": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 14.18.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "individual",
|
||||
"url": "https://paulmillr.com/funding/"
|
||||
}
|
||||
},
|
||||
"node_modules/call-bind-apply-helpers": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz",
|
||||
@@ -5738,6 +5802,16 @@
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/citty": {
|
||||
"version": "0.1.6",
|
||||
"resolved": "https://registry.npmjs.org/citty/-/citty-0.1.6.tgz",
|
||||
"integrity": "sha512-tskPPKEs8D2KPafUypv2gxwJP8h/OaJmC82QQGGDQcHvXX43xF2VDACcJVmZ0EuSxkpO9Kc4MlrA3q0+FG58AQ==",
|
||||
"devOptional": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"consola": "^3.2.3"
|
||||
}
|
||||
},
|
||||
"node_modules/class-variance-authority": {
|
||||
"version": "0.7.1",
|
||||
"resolved": "https://registry.npmjs.org/class-variance-authority/-/class-variance-authority-0.7.1.tgz",
|
||||
@@ -5896,6 +5970,13 @@
|
||||
"typedarray": "^0.0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/confbox": {
|
||||
"version": "0.2.2",
|
||||
"resolved": "https://registry.npmjs.org/confbox/-/confbox-0.2.2.tgz",
|
||||
"integrity": "sha512-1NB+BKqhtNipMsov4xI/NnhCKp9XG9NamYp5PVm9klAT0fsrNPjaFICsCFhNhwZJKNh7zB/3q8qXz0E9oaMNtQ==",
|
||||
"devOptional": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/connect-pg-simple": {
|
||||
"version": "10.0.0",
|
||||
"resolved": "https://registry.npmjs.org/connect-pg-simple/-/connect-pg-simple-10.0.0.tgz",
|
||||
@@ -5908,6 +5989,16 @@
|
||||
"node": "^18.18.0 || ^20.9.0 || >=22.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/consola": {
|
||||
"version": "3.4.2",
|
||||
"resolved": "https://registry.npmjs.org/consola/-/consola-3.4.2.tgz",
|
||||
"integrity": "sha512-5IKcdX0nnYavi6G7TtOhwkYzyjfJlatbjMjuLSfE2kYT5pMDOilZ4OvMhi637CcDICTmz3wARPoyhqyX1Y+XvA==",
|
||||
"devOptional": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": "^14.18.0 || >=16.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/console-control-strings": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz",
|
||||
@@ -6261,6 +6352,23 @@
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/deepmerge-ts": {
|
||||
"version": "7.1.5",
|
||||
"resolved": "https://registry.npmjs.org/deepmerge-ts/-/deepmerge-ts-7.1.5.tgz",
|
||||
"integrity": "sha512-HOJkrhaYsweh+W+e74Yn7YStZOilkoPb6fycpwNLKzSPtruFs48nYis0zy5yJz1+ktUhHxoRDJ27RQAWLIJVJw==",
|
||||
"devOptional": true,
|
||||
"license": "BSD-3-Clause",
|
||||
"engines": {
|
||||
"node": ">=16.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/defu": {
|
||||
"version": "6.1.4",
|
||||
"resolved": "https://registry.npmjs.org/defu/-/defu-6.1.4.tgz",
|
||||
"integrity": "sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg==",
|
||||
"devOptional": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/del": {
|
||||
"version": "6.1.1",
|
||||
"resolved": "https://registry.npmjs.org/del/-/del-6.1.1.tgz",
|
||||
@@ -6307,6 +6415,13 @@
|
||||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/destr": {
|
||||
"version": "2.0.5",
|
||||
"resolved": "https://registry.npmjs.org/destr/-/destr-2.0.5.tgz",
|
||||
"integrity": "sha512-ugFTXCtDZunbzasqBxrK93Ik/DRYsO6S/fedkWEMKqt04xZ4csmnmwGDBAb07QWNaGMAmnTIemsYZCksjATwsA==",
|
||||
"devOptional": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/destroy": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz",
|
||||
@@ -6397,9 +6512,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/dotenv": {
|
||||
"version": "16.5.0",
|
||||
"resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.5.0.tgz",
|
||||
"integrity": "sha512-m/C+AwOAr9/W1UOIZUo232ejMNnJAJtYQjUbHoNTBNTJSvqzzDh7vnrei3o3r3m9blf6ZoDkvcw0VmozNRFJxg==",
|
||||
"version": "16.6.1",
|
||||
"resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.6.1.tgz",
|
||||
"integrity": "sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==",
|
||||
"license": "BSD-2-Clause",
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
@@ -6477,6 +6592,17 @@
|
||||
"integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/effect": {
|
||||
"version": "3.16.12",
|
||||
"resolved": "https://registry.npmjs.org/effect/-/effect-3.16.12.tgz",
|
||||
"integrity": "sha512-N39iBk0K71F9nb442TLbTkjl24FLUzuvx2i1I2RsEAQsdAdUTuUoW0vlfUXgkMTUOnYqKnWcFfqw4hK4Pw27hg==",
|
||||
"devOptional": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@standard-schema/spec": "^1.0.0",
|
||||
"fast-check": "^3.23.1"
|
||||
}
|
||||
},
|
||||
"node_modules/electron-to-chromium": {
|
||||
"version": "1.5.171",
|
||||
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.171.tgz",
|
||||
@@ -7070,6 +7196,36 @@
|
||||
"integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/exsolve": {
|
||||
"version": "1.0.7",
|
||||
"resolved": "https://registry.npmjs.org/exsolve/-/exsolve-1.0.7.tgz",
|
||||
"integrity": "sha512-VO5fQUzZtI6C+vx4w/4BWJpg3s/5l+6pRQEHzFRM8WFi4XffSP1Z+4qi7GbjWbvRQEbdIco5mIMq+zX4rPuLrw==",
|
||||
"devOptional": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/fast-check": {
|
||||
"version": "3.23.2",
|
||||
"resolved": "https://registry.npmjs.org/fast-check/-/fast-check-3.23.2.tgz",
|
||||
"integrity": "sha512-h5+1OzzfCC3Ef7VbtKdcv7zsstUQwUDlYpUTvjeUsJAssPgLn7QzbboPtL5ro04Mq0rPOsMzl7q5hIbRs2wD1A==",
|
||||
"devOptional": true,
|
||||
"funding": [
|
||||
{
|
||||
"type": "individual",
|
||||
"url": "https://github.com/sponsors/dubzzz"
|
||||
},
|
||||
{
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/fast-check"
|
||||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"pure-rand": "^6.1.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/fast-deep-equal": {
|
||||
"version": "3.1.3",
|
||||
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
|
||||
@@ -7221,6 +7377,19 @@
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/find-up-simple": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/find-up-simple/-/find-up-simple-1.0.1.tgz",
|
||||
"integrity": "sha512-afd4O7zpqHeRyg4PfDQsXmlDe2PfdHtJt6Akt8jOWaApLOZk5JXs6VMR29lz03pRe9mpykrRCYIYxaJYcfpncQ==",
|
||||
"devOptional": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/flat-cache": {
|
||||
"version": "4.0.1",
|
||||
"resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz",
|
||||
@@ -7585,6 +7754,24 @@
|
||||
"url": "https://github.com/privatenumber/get-tsconfig?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/giget": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/giget/-/giget-2.0.0.tgz",
|
||||
"integrity": "sha512-L5bGsVkxJbJgdnwyuheIunkGatUF/zssUoxxjACCseZYAVbaqdh9Tsmmlkl8vYan09H7sbvKt4pS8GqKLBrEzA==",
|
||||
"devOptional": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"citty": "^0.1.6",
|
||||
"consola": "^3.4.0",
|
||||
"defu": "^6.1.4",
|
||||
"node-fetch-native": "^1.6.6",
|
||||
"nypm": "^0.6.0",
|
||||
"pathe": "^2.0.3"
|
||||
},
|
||||
"bin": {
|
||||
"giget": "dist/cli.mjs"
|
||||
}
|
||||
},
|
||||
"node_modules/glob": {
|
||||
"version": "7.2.3",
|
||||
"resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
|
||||
@@ -7968,6 +8155,19 @@
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/index-to-position": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/index-to-position/-/index-to-position-1.1.0.tgz",
|
||||
"integrity": "sha512-XPdx9Dq4t9Qk1mTMbWONJqU7boCoumEH7fRET37HX5+khDUl3J2W6PdALxhILYlIYx2amlwYcRPp28p0tSiojg==",
|
||||
"devOptional": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/inflight": {
|
||||
"version": "1.0.6",
|
||||
"resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
|
||||
@@ -9191,6 +9391,13 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/node-fetch-native": {
|
||||
"version": "1.6.6",
|
||||
"resolved": "https://registry.npmjs.org/node-fetch-native/-/node-fetch-native-1.6.6.tgz",
|
||||
"integrity": "sha512-8Mc2HhqPdlIfedsuZoc3yioPuzp6b+L5jRCRY1QzuWZh2EGJVQrGppC6V6cF0bLdbW0+O2YpqCA25aF/1lvipQ==",
|
||||
"devOptional": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/node-releases": {
|
||||
"version": "2.0.19",
|
||||
"resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.19.tgz",
|
||||
@@ -9356,6 +9563,26 @@
|
||||
"set-blocking": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/nypm": {
|
||||
"version": "0.6.1",
|
||||
"resolved": "https://registry.npmjs.org/nypm/-/nypm-0.6.1.tgz",
|
||||
"integrity": "sha512-hlacBiRiv1k9hZFiphPUkfSQ/ZfQzZDzC+8z0wL3lvDAOUu/2NnChkKuMoMjNur/9OpKuz2QsIeiPVN0xM5Q0w==",
|
||||
"devOptional": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"citty": "^0.1.6",
|
||||
"consola": "^3.4.2",
|
||||
"pathe": "^2.0.3",
|
||||
"pkg-types": "^2.2.0",
|
||||
"tinyexec": "^1.0.1"
|
||||
},
|
||||
"bin": {
|
||||
"nypm": "dist/cli.mjs"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^14.16.0 || >=16.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/object-assign": {
|
||||
"version": "4.1.1",
|
||||
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
|
||||
@@ -9386,6 +9613,13 @@
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/ohash": {
|
||||
"version": "2.0.11",
|
||||
"resolved": "https://registry.npmjs.org/ohash/-/ohash-2.0.11.tgz",
|
||||
"integrity": "sha512-RdR9FQrFwNBNXAr4GixM8YaRZRJ5PUWbKYbE5eOsrwAjJW0q2REGcf79oYPsLyskQCZG1PLN+S/K1V00joZAoQ==",
|
||||
"devOptional": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/on-finished": {
|
||||
"version": "2.4.1",
|
||||
"resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz",
|
||||
@@ -9725,6 +9959,13 @@
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/pathe": {
|
||||
"version": "2.0.3",
|
||||
"resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz",
|
||||
"integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==",
|
||||
"devOptional": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/pause": {
|
||||
"version": "0.0.1",
|
||||
"resolved": "https://registry.npmjs.org/pause/-/pause-0.0.1.tgz",
|
||||
@@ -9747,6 +9988,13 @@
|
||||
"resolved": "apps/PdfService",
|
||||
"link": true
|
||||
},
|
||||
"node_modules/perfect-debounce": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/perfect-debounce/-/perfect-debounce-1.0.0.tgz",
|
||||
"integrity": "sha512-xCy9V055GLEqoFaHoC1SoLIaLmWctgCUaBaWxDZ7/Zx4CTyX7cJQLJOok/orfjZAh9kEYpjJa4d0KcJmCbctZA==",
|
||||
"devOptional": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/pg": {
|
||||
"version": "8.16.2",
|
||||
"resolved": "https://registry.npmjs.org/pg/-/pg-8.16.2.tgz",
|
||||
@@ -9936,6 +10184,18 @@
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/pkg-types": {
|
||||
"version": "2.2.0",
|
||||
"resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-2.2.0.tgz",
|
||||
"integrity": "sha512-2SM/GZGAEkPp3KWORxQZns4M+WSeXbC2HEvmOIJe3Cmiv6ieAJvdVhDldtHqM5J1Y7MrR1XhkBT/rMlhh9FdqQ==",
|
||||
"devOptional": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"confbox": "^0.2.2",
|
||||
"exsolve": "^1.0.7",
|
||||
"pathe": "^2.0.3"
|
||||
}
|
||||
},
|
||||
"node_modules/postcss": {
|
||||
"version": "8.5.6",
|
||||
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz",
|
||||
@@ -10237,15 +10497,15 @@
|
||||
}
|
||||
},
|
||||
"node_modules/prisma": {
|
||||
"version": "6.10.1",
|
||||
"resolved": "https://registry.npmjs.org/prisma/-/prisma-6.10.1.tgz",
|
||||
"integrity": "sha512-khhlC/G49E4+uyA3T3H5PRBut486HD2bDqE2+rvkU0pwk9IAqGFacLFUyIx9Uw+W2eCtf6XGwsp+/strUwMNPw==",
|
||||
"version": "6.13.0",
|
||||
"resolved": "https://registry.npmjs.org/prisma/-/prisma-6.13.0.tgz",
|
||||
"integrity": "sha512-dfzORf0AbcEyyzxuv2lEwG8g+WRGF/qDQTpHf/6JoHsyF5MyzCEZwClVaEmw3WXcobgadosOboKUgQU0kFs9kw==",
|
||||
"devOptional": true,
|
||||
"hasInstallScript": true,
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@prisma/config": "6.10.1",
|
||||
"@prisma/engines": "6.10.1"
|
||||
"@prisma/config": "6.13.0",
|
||||
"@prisma/engines": "6.13.0"
|
||||
},
|
||||
"bin": {
|
||||
"prisma": "build/index.js"
|
||||
@@ -10414,6 +10674,23 @@
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/pure-rand": {
|
||||
"version": "6.1.0",
|
||||
"resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-6.1.0.tgz",
|
||||
"integrity": "sha512-bVWawvoZoBYpp6yIoQtQXHZjmz35RSVHnUOTefl8Vcjr8snTPY1wnpSPMWekcFwbxI6gtmT7rSYPFvz71ldiOA==",
|
||||
"devOptional": true,
|
||||
"funding": [
|
||||
{
|
||||
"type": "individual",
|
||||
"url": "https://github.com/sponsors/dubzzz"
|
||||
},
|
||||
{
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/fast-check"
|
||||
}
|
||||
],
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/qs": {
|
||||
"version": "6.13.0",
|
||||
"resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz",
|
||||
@@ -10482,6 +10759,17 @@
|
||||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/rc9": {
|
||||
"version": "2.1.2",
|
||||
"resolved": "https://registry.npmjs.org/rc9/-/rc9-2.1.2.tgz",
|
||||
"integrity": "sha512-btXCnMmRIBINM2LDZoEmOogIZU7Qe7zn4BpomSKZ/ykbLObuBdvG+mFq11DL6fjH1DRwHhrlgtYWG96bJiC7Cg==",
|
||||
"devOptional": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"defu": "^6.1.4",
|
||||
"destr": "^2.0.3"
|
||||
}
|
||||
},
|
||||
"node_modules/react": {
|
||||
"version": "19.1.0",
|
||||
"resolved": "https://registry.npmjs.org/react/-/react-19.1.0.tgz",
|
||||
@@ -10768,6 +11056,123 @@
|
||||
"pify": "^2.3.0"
|
||||
}
|
||||
},
|
||||
"node_modules/read-package-up": {
|
||||
"version": "11.0.0",
|
||||
"resolved": "https://registry.npmjs.org/read-package-up/-/read-package-up-11.0.0.tgz",
|
||||
"integrity": "sha512-MbgfoNPANMdb4oRBNg5eqLbB2t2r+o5Ua1pNt8BqGp4I0FJZhuVSOj3PaBPni4azWuSzEdNn2evevzVmEk1ohQ==",
|
||||
"devOptional": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"find-up-simple": "^1.0.0",
|
||||
"read-pkg": "^9.0.0",
|
||||
"type-fest": "^4.6.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/read-package-up/node_modules/hosted-git-info": {
|
||||
"version": "7.0.2",
|
||||
"resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-7.0.2.tgz",
|
||||
"integrity": "sha512-puUZAUKT5m8Zzvs72XWy3HtvVbTWljRE66cP60bxJzAqf2DgICo7lYTY2IHUmLnNpjYvw5bvmoHvPc0QO2a62w==",
|
||||
"devOptional": true,
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"lru-cache": "^10.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^16.14.0 || >=18.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/read-package-up/node_modules/lru-cache": {
|
||||
"version": "10.4.3",
|
||||
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz",
|
||||
"integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==",
|
||||
"devOptional": true,
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/read-package-up/node_modules/normalize-package-data": {
|
||||
"version": "6.0.2",
|
||||
"resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-6.0.2.tgz",
|
||||
"integrity": "sha512-V6gygoYb/5EmNI+MEGrWkC+e6+Rr7mTmfHrxDbLzxQogBkgzo76rkok0Am6thgSF7Mv2nLOajAJj5vDJZEFn7g==",
|
||||
"devOptional": true,
|
||||
"license": "BSD-2-Clause",
|
||||
"dependencies": {
|
||||
"hosted-git-info": "^7.0.0",
|
||||
"semver": "^7.3.5",
|
||||
"validate-npm-package-license": "^3.0.4"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^16.14.0 || >=18.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/read-package-up/node_modules/parse-json": {
|
||||
"version": "8.3.0",
|
||||
"resolved": "https://registry.npmjs.org/parse-json/-/parse-json-8.3.0.tgz",
|
||||
"integrity": "sha512-ybiGyvspI+fAoRQbIPRddCcSTV9/LsJbf0e/S85VLowVGzRmokfneg2kwVW/KU5rOXrPSbF1qAKPMgNTqqROQQ==",
|
||||
"devOptional": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/code-frame": "^7.26.2",
|
||||
"index-to-position": "^1.1.0",
|
||||
"type-fest": "^4.39.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/read-package-up/node_modules/read-pkg": {
|
||||
"version": "9.0.1",
|
||||
"resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-9.0.1.tgz",
|
||||
"integrity": "sha512-9viLL4/n1BJUCT1NXVTdS1jtm80yDEgR5T4yCelII49Mbj0v1rZdKqj7zCiYdbB0CuCgdrvHcNogAKTFPBocFA==",
|
||||
"devOptional": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/normalize-package-data": "^2.4.3",
|
||||
"normalize-package-data": "^6.0.0",
|
||||
"parse-json": "^8.0.0",
|
||||
"type-fest": "^4.6.0",
|
||||
"unicorn-magic": "^0.1.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/read-package-up/node_modules/semver": {
|
||||
"version": "7.7.2",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz",
|
||||
"integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==",
|
||||
"devOptional": true,
|
||||
"license": "ISC",
|
||||
"bin": {
|
||||
"semver": "bin/semver.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/read-package-up/node_modules/type-fest": {
|
||||
"version": "4.41.0",
|
||||
"resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.41.0.tgz",
|
||||
"integrity": "sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==",
|
||||
"devOptional": true,
|
||||
"license": "(MIT OR CC0-1.0)",
|
||||
"engines": {
|
||||
"node": ">=16"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/read-pkg": {
|
||||
"version": "5.2.0",
|
||||
"resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz",
|
||||
@@ -12065,6 +12470,13 @@
|
||||
"integrity": "sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/tinyexec": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.0.1.tgz",
|
||||
"integrity": "sha512-5uC6DDlmeqiOwCPmK9jMSdOuZTh8bU39Ys6yidB+UTt5hfZUPGAypSgFRiEp+jbi9qH40BLDvy85jIU88wKSqw==",
|
||||
"devOptional": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/tinyglobby": {
|
||||
"version": "0.2.14",
|
||||
"resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.14.tgz",
|
||||
@@ -12550,6 +12962,19 @@
|
||||
"integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/unicorn-magic": {
|
||||
"version": "0.1.0",
|
||||
"resolved": "https://registry.npmjs.org/unicorn-magic/-/unicorn-magic-0.1.0.tgz",
|
||||
"integrity": "sha512-lRfVq8fE8gz6QMBuDM6a+LO3IAzTi05H6gCVaUpir2E1Rwpo4ZUog45KpNXKC/Mn3Yb9UDuHumeFTo9iV/D9FQ==",
|
||||
"devOptional": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/unique-string": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/unique-string/-/unique-string-2.0.0.tgz",
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
"@types/react-redux": "^7.1.34",
|
||||
"prettier": "^3.5.3",
|
||||
"prettier-plugin-tailwindcss": "^0.6.11",
|
||||
"prisma": "^6.13.0",
|
||||
"turbo": "^2.5.3"
|
||||
},
|
||||
"engines": {
|
||||
@@ -32,6 +33,7 @@
|
||||
"packages/*"
|
||||
],
|
||||
"dependencies": {
|
||||
"@prisma/client": "^6.13.0",
|
||||
"@reduxjs/toolkit": "^2.8.2",
|
||||
"dotenv": "^16.5.0",
|
||||
"dotenv-cli": "^8.0.0",
|
||||
|
||||
@@ -27,6 +27,8 @@ model User {
|
||||
appointments Appointment[]
|
||||
claims Claim[]
|
||||
insuranceCredentials InsuranceCredential[]
|
||||
// reverse relations
|
||||
updatedPayments Payment[] @relation("PaymentUpdatedBy")
|
||||
}
|
||||
|
||||
model Patient {
|
||||
@@ -185,6 +187,7 @@ model Payment {
|
||||
patientId Int
|
||||
userId Int
|
||||
claimId Int @unique
|
||||
updatedById Int?
|
||||
totalBilled Decimal @db.Decimal(10, 2)
|
||||
totalPaid Decimal @default(0.00) @db.Decimal(10, 2)
|
||||
totalDue Decimal @db.Decimal(10, 2)
|
||||
@@ -196,10 +199,13 @@ model Payment {
|
||||
updatedAt DateTime @updatedAt
|
||||
|
||||
claim Claim @relation(fields: [claimId], references: [id], onDelete: Cascade)
|
||||
updatedBy User? @relation("PaymentUpdatedBy", fields: [updatedById], references: [id])
|
||||
transactions PaymentTransaction[]
|
||||
servicePayments ServiceLinePayment[]
|
||||
|
||||
@@index([id])
|
||||
@@index([claimId])
|
||||
@@index([patientId])
|
||||
}
|
||||
|
||||
model PaymentTransaction {
|
||||
@@ -212,9 +218,7 @@ model PaymentTransaction {
|
||||
payerName String?
|
||||
notes String?
|
||||
createdAt DateTime @default(now())
|
||||
|
||||
payment Payment @relation(fields: [paymentId], references: [id], onDelete: Cascade)
|
||||
|
||||
@@index([paymentId])
|
||||
}
|
||||
|
||||
@@ -225,10 +229,8 @@ model ServiceLinePayment {
|
||||
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])
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user