1087 lines
27 KiB
TypeScript
1087 lines
27 KiB
TypeScript
import { prisma as db } from "@repo/db/client";
|
|
import { PdfCategory, PdfTitle } from "@repo/db/generated/prisma";
|
|
import {
|
|
Appointment,
|
|
Claim,
|
|
ClaimWithServiceLines,
|
|
DatabaseBackup,
|
|
InsertAppointment,
|
|
InsertClaim,
|
|
InsertInsuranceCredential,
|
|
InsertPatient,
|
|
InsertPayment,
|
|
InsertUser,
|
|
InsuranceCredential,
|
|
Notification,
|
|
NotificationTypes,
|
|
Patient,
|
|
Payment,
|
|
PaymentWithExtras,
|
|
PdfFile,
|
|
PdfGroup,
|
|
Staff,
|
|
UpdateAppointment,
|
|
UpdateClaim,
|
|
UpdatePatient,
|
|
UpdatePayment,
|
|
User,
|
|
} from "@repo/db/types";
|
|
|
|
export interface IStorage {
|
|
// User methods
|
|
getUser(id: number): Promise<User | undefined>;
|
|
getUsers(limit: number, offset: number): Promise<User[]>;
|
|
getUserByUsername(username: string): Promise<User | undefined>;
|
|
createUser(user: InsertUser): Promise<User>;
|
|
updateUser(id: number, updates: Partial<User>): Promise<User | undefined>;
|
|
deleteUser(id: number): Promise<boolean>;
|
|
|
|
// Patient methods
|
|
getPatient(id: number): Promise<Patient | undefined>;
|
|
getPatientByInsuranceId(insuranceId: string): Promise<Patient | null>;
|
|
getPatientsByUserId(userId: number): Promise<Patient[]>;
|
|
getRecentPatients(limit: number, offset: number): Promise<Patient[]>;
|
|
getPatientsByIds(ids: number[]): Promise<Patient[]>;
|
|
createPatient(patient: InsertPatient): Promise<Patient>;
|
|
updatePatient(id: number, patient: UpdatePatient): Promise<Patient>;
|
|
deletePatient(id: number): Promise<void>;
|
|
searchPatients(args: {
|
|
filters: any;
|
|
limit: number;
|
|
offset: number;
|
|
}): Promise<
|
|
{
|
|
id: number;
|
|
firstName: string | null;
|
|
lastName: string | null;
|
|
phone: string | null;
|
|
gender: string | null;
|
|
dateOfBirth: Date;
|
|
insuranceId: string | null;
|
|
insuranceProvider: string | null;
|
|
status: string;
|
|
}[]
|
|
>;
|
|
getTotalPatientCount(): Promise<number>;
|
|
countPatients(filters: any): Promise<number>; // optional but useful
|
|
|
|
// Appointment methods
|
|
getAppointment(id: number): Promise<Appointment | undefined>;
|
|
getAllAppointments(): Promise<Appointment[]>;
|
|
getAppointmentsByUserId(userId: number): Promise<Appointment[]>;
|
|
getAppointmentsByPatientId(patientId: number): Promise<Appointment[]>;
|
|
getRecentAppointments(limit: number, offset: number): Promise<Appointment[]>;
|
|
getAppointmentsOnRange(start: Date, end: Date): Promise<Appointment[]>;
|
|
createAppointment(appointment: InsertAppointment): Promise<Appointment>;
|
|
updateAppointment(
|
|
id: number,
|
|
appointment: UpdateAppointment
|
|
): Promise<Appointment>;
|
|
deleteAppointment(id: number): Promise<void>;
|
|
getPatientAppointmentByDateTime(
|
|
patientId: number,
|
|
date: Date,
|
|
startTime: string
|
|
): Promise<Appointment | undefined>;
|
|
getStaffAppointmentByDateTime(
|
|
staffId: number,
|
|
date: Date,
|
|
startTime: string,
|
|
excludeId?: number
|
|
): Promise<Appointment | undefined>;
|
|
getPatientConflictAppointment(
|
|
patientId: number,
|
|
date: Date,
|
|
startTime: string,
|
|
excludeId: number
|
|
): Promise<Appointment | undefined>;
|
|
getStaffConflictAppointment(
|
|
staffId: number,
|
|
date: Date,
|
|
startTime: string,
|
|
excludeId: number
|
|
): Promise<Appointment | undefined>;
|
|
|
|
// Staff methods
|
|
getStaff(id: number): Promise<Staff | undefined>;
|
|
getAllStaff(): Promise<Staff[]>;
|
|
createStaff(staff: Staff): Promise<Staff>;
|
|
updateStaff(id: number, updates: Partial<Staff>): Promise<Staff | undefined>;
|
|
deleteStaff(id: number): Promise<boolean>;
|
|
countAppointmentsByStaffId(staffId: number): Promise<number>;
|
|
countClaimsByStaffId(staffId: number): Promise<number>;
|
|
|
|
// Claim methods
|
|
getClaim(id: number): Promise<Claim | undefined>;
|
|
getRecentClaimsByPatientId(
|
|
patientId: number,
|
|
limit: number,
|
|
offset: number
|
|
): Promise<ClaimWithServiceLines[]>;
|
|
|
|
getTotalClaimCountByPatient(patientId: number): Promise<number>;
|
|
getClaimsByAppointmentId(appointmentId: number): Promise<Claim[]>;
|
|
getRecentClaims(limit: number, offset: number): Promise<Claim[]>;
|
|
getTotalClaimCount(): Promise<number>;
|
|
createClaim(claim: InsertClaim): Promise<Claim>;
|
|
updateClaim(id: number, updates: UpdateClaim): Promise<Claim>;
|
|
deleteClaim(id: number): Promise<void>;
|
|
|
|
// InsuranceCredential methods
|
|
getInsuranceCredential(id: number): Promise<InsuranceCredential | null>;
|
|
getInsuranceCredentialsByUser(userId: number): Promise<InsuranceCredential[]>;
|
|
createInsuranceCredential(
|
|
data: InsertInsuranceCredential
|
|
): Promise<InsuranceCredential>;
|
|
updateInsuranceCredential(
|
|
id: number,
|
|
updates: Partial<InsuranceCredential>
|
|
): Promise<InsuranceCredential | null>;
|
|
deleteInsuranceCredential(userId: number, id: number): Promise<boolean>;
|
|
getInsuranceCredentialByUserAndSiteKey(
|
|
userId: number,
|
|
siteKey: string
|
|
): Promise<InsuranceCredential | null>;
|
|
|
|
// General PDF Methods
|
|
createPdfFile(
|
|
groupId: number,
|
|
filename: string,
|
|
pdfData: Buffer
|
|
): Promise<PdfFile>;
|
|
getPdfFileById(id: number): Promise<PdfFile | undefined>;
|
|
getPdfFilesByGroupId(groupId: number): Promise<PdfFile[]>;
|
|
getRecentPdfFiles(limit: number, offset: number): Promise<PdfFile[]>;
|
|
deletePdfFile(id: number): Promise<boolean>;
|
|
updatePdfFile(
|
|
id: number,
|
|
updates: Partial<Pick<PdfFile, "filename" | "pdfData">>
|
|
): Promise<PdfFile | undefined>;
|
|
|
|
// PDF Group management
|
|
createPdfGroup(
|
|
patientId: number,
|
|
title: string,
|
|
titleKey: PdfTitle,
|
|
category: PdfCategory
|
|
): Promise<PdfGroup>;
|
|
findPdfGroupByPatientTitleKeyAndCategory(
|
|
patientId: number,
|
|
titleKey: PdfTitle,
|
|
category: PdfCategory
|
|
): Promise<PdfGroup | undefined>;
|
|
getAllPdfGroups(): Promise<PdfGroup[]>;
|
|
getPdfGroupById(id: number): Promise<PdfGroup | undefined>;
|
|
getPdfGroupsByPatientId(patientId: number): Promise<PdfGroup[]>;
|
|
updatePdfGroup(
|
|
id: number,
|
|
updates: Partial<Pick<PdfGroup, "title" | "category">>
|
|
): Promise<PdfGroup | undefined>;
|
|
deletePdfGroup(id: number): Promise<boolean>;
|
|
|
|
// Payment methods:
|
|
getPayment(id: number): Promise<Payment | undefined>;
|
|
createPayment(data: InsertPayment): Promise<Payment>;
|
|
updatePayment(id: number, updates: UpdatePayment): Promise<Payment>;
|
|
deletePayment(id: number, userId: number): Promise<void>;
|
|
getPaymentById(id: number): Promise<PaymentWithExtras | null>;
|
|
getRecentPaymentsByPatientId(
|
|
patientId: number,
|
|
limit: number,
|
|
offset: number
|
|
): Promise<PaymentWithExtras[] | null>;
|
|
getTotalPaymentCountByPatient(patientId: number): Promise<number>;
|
|
getPaymentsByClaimId(claimId: number): Promise<PaymentWithExtras | null>;
|
|
getRecentPayments(
|
|
limit: number,
|
|
offset: number
|
|
): Promise<PaymentWithExtras[]>;
|
|
getPaymentsByDateRange(from: Date, to: Date): Promise<PaymentWithExtras[]>;
|
|
getTotalPaymentCount(): Promise<number>;
|
|
|
|
// Database Backup methods
|
|
createBackup(userId: number): Promise<DatabaseBackup>;
|
|
getLastBackup(userId: number): Promise<DatabaseBackup | null>;
|
|
getBackups(userId: number, limit?: number): Promise<DatabaseBackup[]>;
|
|
deleteBackups(userId: number): Promise<number>; // clears all for user
|
|
|
|
// Notification methods
|
|
createNotification(
|
|
userId: number,
|
|
type: NotificationTypes,
|
|
message: string
|
|
): Promise<Notification>;
|
|
getNotifications(
|
|
userId: number,
|
|
limit?: number,
|
|
offset?: number
|
|
): Promise<Notification[]>;
|
|
markNotificationRead(
|
|
userId: number,
|
|
notificationId: number
|
|
): Promise<boolean>;
|
|
markAllNotificationsRead(userId: number): Promise<number>;
|
|
deleteNotificationsByType(
|
|
userId: number,
|
|
type: NotificationTypes
|
|
): Promise<number>;
|
|
deleteAllNotifications(userId: number): Promise<number>;
|
|
}
|
|
|
|
export const storage: IStorage = {
|
|
// User methods
|
|
async getUser(id: number): Promise<User | undefined> {
|
|
const user = await db.user.findUnique({ where: { id } });
|
|
return user ?? undefined;
|
|
},
|
|
|
|
async getUsers(limit: number, offset: number): Promise<User[]> {
|
|
return await db.user.findMany({ skip: offset, take: limit });
|
|
},
|
|
|
|
async getUserByUsername(username: string): Promise<User | undefined> {
|
|
const user = await db.user.findUnique({ where: { username } });
|
|
return user ?? undefined;
|
|
},
|
|
|
|
async createUser(user: InsertUser): Promise<User> {
|
|
return await db.user.create({ data: user as User });
|
|
},
|
|
|
|
async updateUser(
|
|
id: number,
|
|
updates: Partial<User>
|
|
): Promise<User | undefined> {
|
|
try {
|
|
return await db.user.update({ where: { id }, data: updates });
|
|
} catch {
|
|
return undefined;
|
|
}
|
|
},
|
|
|
|
async deleteUser(id: number): Promise<boolean> {
|
|
try {
|
|
await db.user.delete({ where: { id } });
|
|
return true;
|
|
} catch {
|
|
return false;
|
|
}
|
|
},
|
|
|
|
// Patient methods
|
|
async getPatient(id: number): Promise<Patient | undefined> {
|
|
const patient = await db.patient.findUnique({ where: { id } });
|
|
return patient ?? undefined;
|
|
},
|
|
|
|
async getPatientsByUserId(userId: number): Promise<Patient[]> {
|
|
return await db.patient.findMany({ where: { userId } });
|
|
},
|
|
|
|
async getPatientByInsuranceId(insuranceId: string): Promise<Patient | null> {
|
|
return db.patient.findFirst({
|
|
where: { insuranceId },
|
|
});
|
|
},
|
|
|
|
async getRecentPatients(limit: number, offset: number): Promise<Patient[]> {
|
|
return db.patient.findMany({
|
|
skip: offset,
|
|
take: limit,
|
|
orderBy: { createdAt: "desc" },
|
|
});
|
|
},
|
|
|
|
async getPatientsByIds(ids: number[]): Promise<Patient[]> {
|
|
if (!ids || ids.length === 0) return [];
|
|
const uniqueIds = Array.from(new Set(ids));
|
|
return db.patient.findMany({
|
|
where: { id: { in: uniqueIds } },
|
|
select: {
|
|
id: true,
|
|
firstName: true,
|
|
lastName: true,
|
|
phone: true,
|
|
email: true,
|
|
dateOfBirth: true,
|
|
gender: true,
|
|
insuranceId: true,
|
|
insuranceProvider: true,
|
|
status: true,
|
|
userId: true,
|
|
createdAt: true,
|
|
},
|
|
});
|
|
},
|
|
|
|
async createPatient(patient: InsertPatient): Promise<Patient> {
|
|
return await db.patient.create({ data: patient as Patient });
|
|
},
|
|
|
|
async updatePatient(id: number, updateData: UpdatePatient): Promise<Patient> {
|
|
try {
|
|
return await db.patient.update({
|
|
where: { id },
|
|
data: updateData,
|
|
});
|
|
} catch (err) {
|
|
throw new Error(`Patient with ID ${id} not found`);
|
|
}
|
|
},
|
|
|
|
async deletePatient(id: number): Promise<void> {
|
|
try {
|
|
await db.patient.delete({ where: { id } });
|
|
} catch (err) {
|
|
console.error("Error deleting patient:", err);
|
|
throw new Error(`Failed to delete patient: ${err}`);
|
|
}
|
|
},
|
|
|
|
async searchPatients({
|
|
filters,
|
|
limit,
|
|
offset,
|
|
}: {
|
|
filters: any;
|
|
limit: number;
|
|
offset: number;
|
|
}) {
|
|
return db.patient.findMany({
|
|
where: filters,
|
|
orderBy: { createdAt: "desc" },
|
|
take: limit,
|
|
skip: offset,
|
|
select: {
|
|
id: true,
|
|
firstName: true,
|
|
lastName: true,
|
|
phone: true,
|
|
gender: true,
|
|
dateOfBirth: true,
|
|
insuranceId: true,
|
|
insuranceProvider: true,
|
|
status: true,
|
|
},
|
|
});
|
|
},
|
|
|
|
async getTotalPatientCount(): Promise<number> {
|
|
return db.patient.count();
|
|
},
|
|
|
|
async countPatients(filters: any) {
|
|
return db.patient.count({ where: filters });
|
|
},
|
|
|
|
// Appointment methods
|
|
async getAppointment(id: number): Promise<Appointment | undefined> {
|
|
const appointment = await db.appointment.findUnique({ where: { id } });
|
|
return appointment ?? undefined;
|
|
},
|
|
|
|
async getAllAppointments(): Promise<Appointment[]> {
|
|
return await db.appointment.findMany();
|
|
},
|
|
|
|
async getAppointmentsByUserId(userId: number): Promise<Appointment[]> {
|
|
return await db.appointment.findMany({ where: { userId } });
|
|
},
|
|
|
|
async getAppointmentsByPatientId(patientId: number): Promise<Appointment[]> {
|
|
return await db.appointment.findMany({ where: { patientId } });
|
|
},
|
|
|
|
async getAppointmentsOnRange(start: Date, end: Date): Promise<Appointment[]> {
|
|
return db.appointment.findMany({
|
|
where: {
|
|
date: {
|
|
gte: start,
|
|
lte: end,
|
|
},
|
|
},
|
|
orderBy: { date: "asc" },
|
|
});
|
|
},
|
|
|
|
async getRecentAppointments(
|
|
limit: number,
|
|
offset: number
|
|
): Promise<Appointment[]> {
|
|
return db.appointment.findMany({
|
|
skip: offset,
|
|
take: limit,
|
|
orderBy: { date: "desc" },
|
|
});
|
|
},
|
|
|
|
async createAppointment(
|
|
appointment: InsertAppointment
|
|
): Promise<Appointment> {
|
|
return await db.appointment.create({ data: appointment as Appointment });
|
|
},
|
|
|
|
async updateAppointment(
|
|
id: number,
|
|
updateData: UpdateAppointment
|
|
): Promise<Appointment> {
|
|
try {
|
|
return await db.appointment.update({
|
|
where: { id },
|
|
data: updateData,
|
|
});
|
|
} catch (err) {
|
|
throw new Error(`Appointment with ID ${id} not found`);
|
|
}
|
|
},
|
|
|
|
async deleteAppointment(id: number): Promise<void> {
|
|
try {
|
|
await db.appointment.delete({ where: { id } });
|
|
} catch (err) {
|
|
throw new Error(`Appointment with ID ${id} not found`);
|
|
}
|
|
},
|
|
|
|
async getPatientAppointmentByDateTime(
|
|
patientId: number,
|
|
date: Date,
|
|
startTime: string
|
|
): Promise<Appointment | undefined> {
|
|
return (
|
|
(await db.appointment.findFirst({
|
|
where: {
|
|
patientId,
|
|
date,
|
|
startTime,
|
|
},
|
|
})) ?? undefined
|
|
);
|
|
},
|
|
|
|
async getStaffAppointmentByDateTime(
|
|
staffId: number,
|
|
date: Date,
|
|
startTime: string,
|
|
excludeId?: number
|
|
): Promise<Appointment | undefined> {
|
|
return (
|
|
(await db.appointment.findFirst({
|
|
where: {
|
|
staffId,
|
|
date,
|
|
startTime,
|
|
NOT: excludeId ? { id: excludeId } : undefined,
|
|
},
|
|
})) ?? undefined
|
|
);
|
|
},
|
|
|
|
async getPatientConflictAppointment(
|
|
patientId: number,
|
|
date: Date,
|
|
startTime: string,
|
|
excludeId: number
|
|
): Promise<Appointment | undefined> {
|
|
return (
|
|
(await db.appointment.findFirst({
|
|
where: {
|
|
patientId,
|
|
date,
|
|
startTime,
|
|
NOT: { id: excludeId },
|
|
},
|
|
})) ?? undefined
|
|
);
|
|
},
|
|
|
|
async getStaffConflictAppointment(
|
|
staffId: number,
|
|
date: Date,
|
|
startTime: string,
|
|
excludeId: number
|
|
): Promise<Appointment | undefined> {
|
|
return (
|
|
(await db.appointment.findFirst({
|
|
where: {
|
|
staffId,
|
|
date,
|
|
startTime,
|
|
NOT: { id: excludeId },
|
|
},
|
|
})) ?? undefined
|
|
);
|
|
},
|
|
|
|
// Staff methods
|
|
async getStaff(id: number): Promise<Staff | undefined> {
|
|
const staff = await db.staff.findUnique({ where: { id } });
|
|
return staff ?? undefined;
|
|
},
|
|
|
|
async getAllStaff(): Promise<Staff[]> {
|
|
const staff = await db.staff.findMany();
|
|
return staff;
|
|
},
|
|
|
|
async createStaff(staff: Staff): Promise<Staff> {
|
|
const createdStaff = await db.staff.create({
|
|
data: staff,
|
|
});
|
|
return createdStaff;
|
|
},
|
|
|
|
async updateStaff(
|
|
id: number,
|
|
updates: Partial<Staff>
|
|
): Promise<Staff | undefined> {
|
|
const updatedStaff = await db.staff.update({
|
|
where: { id },
|
|
data: updates,
|
|
});
|
|
return updatedStaff ?? undefined;
|
|
},
|
|
|
|
async deleteStaff(id: number): Promise<boolean> {
|
|
try {
|
|
await db.staff.delete({ where: { id } });
|
|
return true;
|
|
} catch (error) {
|
|
console.error("Error deleting staff:", error);
|
|
return false;
|
|
}
|
|
},
|
|
|
|
async countAppointmentsByStaffId(staffId: number): Promise<number> {
|
|
return await db.appointment.count({ where: { staffId } });
|
|
},
|
|
|
|
async countClaimsByStaffId(staffId: number): Promise<number> {
|
|
return await db.claim.count({ where: { staffId } });
|
|
},
|
|
|
|
// Claim methods implementation
|
|
async getClaim(id: number): Promise<Claim | undefined> {
|
|
const claim = await db.claim.findUnique({ where: { id } });
|
|
return claim ?? undefined;
|
|
},
|
|
|
|
async getRecentClaimsByPatientId(
|
|
patientId: number,
|
|
limit: number,
|
|
offset: number
|
|
): Promise<ClaimWithServiceLines[]> {
|
|
return db.claim.findMany({
|
|
where: { patientId },
|
|
orderBy: { createdAt: "desc" },
|
|
skip: offset,
|
|
take: limit,
|
|
include: {
|
|
serviceLines: true,
|
|
staff: true,
|
|
claimFiles: true,
|
|
},
|
|
});
|
|
},
|
|
|
|
async getTotalClaimCountByPatient(patientId: number): Promise<number> {
|
|
return db.claim.count({
|
|
where: { patientId },
|
|
});
|
|
},
|
|
|
|
async getClaimsByAppointmentId(appointmentId: number): Promise<Claim[]> {
|
|
return await db.claim.findMany({ where: { appointmentId } });
|
|
},
|
|
|
|
async getRecentClaims(
|
|
limit: number,
|
|
offset: number
|
|
): Promise<ClaimWithServiceLines[]> {
|
|
return db.claim.findMany({
|
|
orderBy: { createdAt: "desc" },
|
|
skip: offset,
|
|
take: limit,
|
|
include: { serviceLines: true, staff: true, claimFiles: true },
|
|
});
|
|
},
|
|
|
|
async getTotalClaimCount(): Promise<number> {
|
|
return db.claim.count();
|
|
},
|
|
|
|
async createClaim(claim: InsertClaim): Promise<Claim> {
|
|
return await db.claim.create({ data: claim as Claim });
|
|
},
|
|
|
|
async updateClaim(id: number, updates: UpdateClaim): Promise<Claim> {
|
|
try {
|
|
return await db.claim.update({
|
|
where: { id },
|
|
data: updates,
|
|
});
|
|
} catch (err) {
|
|
throw new Error(`Claim with ID ${id} not found`);
|
|
}
|
|
},
|
|
|
|
async deleteClaim(id: number): Promise<void> {
|
|
try {
|
|
await db.claim.delete({ where: { id } });
|
|
} catch (err) {
|
|
throw new Error(`Claim with ID ${id} not found`);
|
|
}
|
|
},
|
|
|
|
// Insurance Creds
|
|
async getInsuranceCredential(id: number) {
|
|
return await db.insuranceCredential.findUnique({ where: { id } });
|
|
},
|
|
|
|
async getInsuranceCredentialsByUser(userId: number) {
|
|
return await db.insuranceCredential.findMany({ where: { userId } });
|
|
},
|
|
|
|
async createInsuranceCredential(data: InsertInsuranceCredential) {
|
|
return await db.insuranceCredential.create({
|
|
data: data as InsuranceCredential,
|
|
});
|
|
},
|
|
|
|
async updateInsuranceCredential(
|
|
id: number,
|
|
updates: Partial<InsuranceCredential>
|
|
) {
|
|
return await db.insuranceCredential.update({
|
|
where: { id },
|
|
data: updates,
|
|
});
|
|
},
|
|
|
|
async deleteInsuranceCredential(userId: number, id: number) {
|
|
try {
|
|
await db.insuranceCredential.delete({ where: { userId, id } });
|
|
return true;
|
|
} catch {
|
|
return false;
|
|
}
|
|
},
|
|
|
|
async getInsuranceCredentialByUserAndSiteKey(
|
|
userId: number,
|
|
siteKey: string
|
|
): Promise<InsuranceCredential | null> {
|
|
return await db.insuranceCredential.findFirst({
|
|
where: { userId, siteKey },
|
|
});
|
|
},
|
|
|
|
// PDF Files
|
|
async createPdfFile(groupId, filename, pdfData) {
|
|
return db.pdfFile.create({
|
|
data: {
|
|
groupId,
|
|
filename,
|
|
pdfData,
|
|
},
|
|
});
|
|
},
|
|
|
|
async getAllPdfGroups(): Promise<PdfGroup[]> {
|
|
return db.pdfGroup.findMany({
|
|
orderBy: {
|
|
createdAt: "desc",
|
|
},
|
|
});
|
|
},
|
|
|
|
async getPdfFileById(id) {
|
|
return (await db.pdfFile.findUnique({ where: { id } })) ?? undefined;
|
|
},
|
|
|
|
async getPdfFilesByGroupId(groupId) {
|
|
return db.pdfFile.findMany({
|
|
where: { groupId },
|
|
orderBy: { uploadedAt: "desc" },
|
|
});
|
|
},
|
|
|
|
async getRecentPdfFiles(limit: number, offset: number): Promise<PdfFile[]> {
|
|
return db.pdfFile.findMany({
|
|
skip: offset,
|
|
take: limit,
|
|
orderBy: { uploadedAt: "desc" },
|
|
include: { group: true },
|
|
});
|
|
},
|
|
|
|
async updatePdfFile(id, updates) {
|
|
try {
|
|
return await db.pdfFile.update({
|
|
where: { id },
|
|
data: updates,
|
|
});
|
|
} catch {
|
|
return undefined;
|
|
}
|
|
},
|
|
|
|
async deletePdfFile(id) {
|
|
try {
|
|
await db.pdfFile.delete({ where: { id } });
|
|
return true;
|
|
} catch {
|
|
return false;
|
|
}
|
|
},
|
|
|
|
// ----------------------
|
|
// PdfGroup CRUD
|
|
// ----------------------
|
|
|
|
async createPdfGroup(patientId, title, titleKey, category) {
|
|
return db.pdfGroup.create({
|
|
data: {
|
|
patientId,
|
|
title,
|
|
titleKey,
|
|
category,
|
|
},
|
|
});
|
|
},
|
|
|
|
async findPdfGroupByPatientTitleKeyAndCategory(
|
|
patientId,
|
|
titleKey,
|
|
category
|
|
) {
|
|
return (
|
|
(await db.pdfGroup.findFirst({
|
|
where: {
|
|
patientId,
|
|
titleKey,
|
|
category,
|
|
},
|
|
})) ?? undefined
|
|
);
|
|
},
|
|
|
|
async getPdfGroupById(id) {
|
|
return (await db.pdfGroup.findUnique({ where: { id } })) ?? undefined;
|
|
},
|
|
|
|
async getPdfGroupsByPatientId(patientId) {
|
|
return db.pdfGroup.findMany({
|
|
where: { patientId },
|
|
orderBy: { createdAt: "desc" },
|
|
});
|
|
},
|
|
|
|
async updatePdfGroup(id, updates) {
|
|
try {
|
|
return await db.pdfGroup.update({
|
|
where: { id },
|
|
data: updates,
|
|
});
|
|
} catch {
|
|
return undefined;
|
|
}
|
|
},
|
|
|
|
async deletePdfGroup(id) {
|
|
try {
|
|
await db.pdfGroup.delete({ where: { id } });
|
|
return true;
|
|
} catch {
|
|
return false;
|
|
}
|
|
},
|
|
|
|
// Payment Methods
|
|
async getPayment(id: number): Promise<Payment | undefined> {
|
|
const payment = await db.payment.findUnique({ where: { id } });
|
|
return payment ?? undefined;
|
|
},
|
|
|
|
async createPayment(payment: InsertPayment): Promise<Payment> {
|
|
return db.payment.create({ data: payment as Payment });
|
|
},
|
|
|
|
async updatePayment(id: number, updates: UpdatePayment): Promise<Payment> {
|
|
const existing = await db.payment.findFirst({ where: { id } });
|
|
if (!existing) {
|
|
throw new Error("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 getRecentPaymentsByPatientId(
|
|
patientId: number,
|
|
limit: number,
|
|
offset: number
|
|
): Promise<PaymentWithExtras[]> {
|
|
const payments = await db.payment.findMany({
|
|
where: { patientId },
|
|
orderBy: { createdAt: "desc" },
|
|
skip: offset,
|
|
take: limit,
|
|
include: {
|
|
claim: {
|
|
include: {
|
|
serviceLines: true,
|
|
},
|
|
},
|
|
serviceLines: true,
|
|
serviceLineTransactions: {
|
|
include: {
|
|
serviceLine: true,
|
|
},
|
|
},
|
|
updatedBy: true,
|
|
patient: true,
|
|
},
|
|
});
|
|
|
|
return payments.map((payment) => ({
|
|
...payment,
|
|
patientName: payment.claim?.patientName ?? "",
|
|
paymentDate: payment.createdAt,
|
|
paymentMethod: payment.serviceLineTransactions[0]?.method ?? "OTHER",
|
|
}));
|
|
},
|
|
|
|
async getTotalPaymentCountByPatient(patientId: number): Promise<number> {
|
|
return db.payment.count({
|
|
where: { patientId },
|
|
});
|
|
},
|
|
|
|
async getPaymentById(id: number): Promise<PaymentWithExtras | null> {
|
|
const payment = await db.payment.findFirst({
|
|
where: { id },
|
|
include: {
|
|
claim: {
|
|
include: {
|
|
serviceLines: true,
|
|
},
|
|
},
|
|
serviceLines: true,
|
|
serviceLineTransactions: {
|
|
include: {
|
|
serviceLine: true,
|
|
},
|
|
},
|
|
updatedBy: true,
|
|
patient: true,
|
|
},
|
|
});
|
|
|
|
if (!payment) return null;
|
|
|
|
return {
|
|
...payment,
|
|
patientName: payment.claim?.patientName ?? "",
|
|
paymentDate: payment.createdAt,
|
|
paymentMethod: payment.serviceLineTransactions[0]?.method ?? "OTHER",
|
|
};
|
|
},
|
|
|
|
async getPaymentsByClaimId(
|
|
claimId: number
|
|
): Promise<PaymentWithExtras | null> {
|
|
const payment = await db.payment.findFirst({
|
|
where: { claimId },
|
|
include: {
|
|
claim: {
|
|
include: {
|
|
serviceLines: true,
|
|
},
|
|
},
|
|
serviceLines: true,
|
|
serviceLineTransactions: {
|
|
include: {
|
|
serviceLine: true,
|
|
},
|
|
},
|
|
updatedBy: true,
|
|
patient: true,
|
|
},
|
|
});
|
|
|
|
if (!payment) return null;
|
|
|
|
return {
|
|
...payment,
|
|
patientName: payment.claim?.patientName ?? "",
|
|
paymentDate: payment.createdAt,
|
|
paymentMethod: payment.serviceLineTransactions[0]?.method ?? "OTHER",
|
|
};
|
|
},
|
|
|
|
async getRecentPayments(
|
|
limit: number,
|
|
offset: number
|
|
): Promise<PaymentWithExtras[]> {
|
|
const payments = await db.payment.findMany({
|
|
orderBy: { createdAt: "desc" },
|
|
skip: offset,
|
|
take: limit,
|
|
include: {
|
|
claim: {
|
|
include: {
|
|
serviceLines: true,
|
|
},
|
|
},
|
|
serviceLines: true,
|
|
serviceLineTransactions: {
|
|
include: {
|
|
serviceLine: true,
|
|
},
|
|
},
|
|
updatedBy: true,
|
|
patient: true,
|
|
},
|
|
});
|
|
|
|
return payments.map((payment) => ({
|
|
...payment,
|
|
patientName: payment.claim?.patientName ?? "",
|
|
paymentDate: payment.createdAt,
|
|
paymentMethod: payment.serviceLineTransactions[0]?.method ?? "OTHER",
|
|
}));
|
|
},
|
|
|
|
async getPaymentsByDateRange(
|
|
from: Date,
|
|
to: Date
|
|
): Promise<PaymentWithExtras[]> {
|
|
const payments = await db.payment.findMany({
|
|
where: {
|
|
createdAt: {
|
|
gte: from,
|
|
lte: to,
|
|
},
|
|
},
|
|
orderBy: { createdAt: "desc" },
|
|
include: {
|
|
claim: {
|
|
include: {
|
|
serviceLines: true,
|
|
},
|
|
},
|
|
serviceLines: true,
|
|
serviceLineTransactions: {
|
|
include: {
|
|
serviceLine: true,
|
|
},
|
|
},
|
|
updatedBy: true,
|
|
patient: true,
|
|
},
|
|
});
|
|
|
|
return payments.map((payment) => ({
|
|
...payment,
|
|
patientName: payment.claim?.patientName ?? "",
|
|
paymentDate: payment.createdAt,
|
|
paymentMethod: payment.serviceLineTransactions[0]?.method ?? "OTHER",
|
|
}));
|
|
},
|
|
|
|
async getTotalPaymentCount(): Promise<number> {
|
|
return db.payment.count();
|
|
},
|
|
|
|
// ==============================
|
|
// Database Backup methods
|
|
// ==============================
|
|
async createBackup(userId) {
|
|
return await db.databaseBackup.create({ data: { userId } });
|
|
},
|
|
|
|
async getLastBackup(userId) {
|
|
return await db.databaseBackup.findFirst({
|
|
where: { userId },
|
|
orderBy: { createdAt: "desc" },
|
|
});
|
|
},
|
|
|
|
async getBackups(userId, limit = 10) {
|
|
return await db.databaseBackup.findMany({
|
|
where: { userId },
|
|
orderBy: { createdAt: "desc" },
|
|
take: limit,
|
|
});
|
|
},
|
|
|
|
async deleteBackups(userId) {
|
|
const result = await db.databaseBackup.deleteMany({ where: { userId } });
|
|
return result.count;
|
|
},
|
|
|
|
// ==============================
|
|
// Notification methods
|
|
// ==============================
|
|
async createNotification(userId, type, message) {
|
|
return await db.notification.create({
|
|
data: { userId, type, message },
|
|
});
|
|
},
|
|
|
|
async getNotifications(
|
|
userId: number,
|
|
limit = 50,
|
|
offset = 0
|
|
): Promise<Notification[]> {
|
|
return await db.notification.findMany({
|
|
where: { userId },
|
|
orderBy: { createdAt: "desc" },
|
|
take: limit,
|
|
skip: offset,
|
|
});
|
|
},
|
|
|
|
async markNotificationRead(userId, notificationId) {
|
|
const result = await db.notification.updateMany({
|
|
where: { id: notificationId, userId },
|
|
data: { read: true },
|
|
});
|
|
return result.count > 0;
|
|
},
|
|
|
|
async markAllNotificationsRead(userId) {
|
|
const result = await db.notification.updateMany({
|
|
where: { userId },
|
|
data: { read: true },
|
|
});
|
|
return result.count;
|
|
},
|
|
|
|
async deleteNotificationsByType(userId, type) {
|
|
const result = await db.notification.deleteMany({
|
|
where: { userId, type },
|
|
});
|
|
return result.count;
|
|
},
|
|
|
|
async deleteAllNotifications(userId: number): Promise<number> {
|
|
const result = await db.notification.deleteMany({
|
|
where: { userId },
|
|
});
|
|
return result.count;
|
|
},
|
|
};
|