import { prisma } from "./db.server"; import type { MaintenanceVisit } from "@prisma/client"; import type { CreateMaintenanceVisitData, UpdateMaintenanceVisitData, MaintenanceVisitWithRelations } from "~/types/database"; // Get all maintenance visits with search and pagination export async function getMaintenanceVisits( searchQuery?: string, page: number = 1, limit: number = 10, vehicleId?: number, customerId?: number ): Promise<{ visits: MaintenanceVisitWithRelations[]; total: number; totalPages: number; }> { const offset = (page - 1) * limit; // Build where clause for search and filters const whereClause: any = {}; if (vehicleId) { whereClause.vehicleId = vehicleId; } if (customerId) { whereClause.customerId = customerId; } if (searchQuery) { const searchLower = searchQuery.toLowerCase(); whereClause.OR = [ { maintenanceJobs: { contains: searchLower } }, { description: { contains: searchLower } }, { paymentStatus: { contains: searchLower } }, { vehicle: { plateNumber: { contains: searchLower } } }, { customer: { name: { contains: searchLower } } }, ]; } const [visits, total] = await Promise.all([ prisma.maintenanceVisit.findMany({ where: whereClause, include: { vehicle: { select: { id: true, plateNumber: true, manufacturer: true, model: true, year: true, }, }, customer: { select: { id: true, name: true, phone: true, email: true, }, }, income: { select: { id: true, amount: true, incomeDate: true, }, }, }, orderBy: { visitDate: 'desc' }, skip: offset, take: limit, }), prisma.maintenanceVisit.count({ where: whereClause }), ]); const totalPages = Math.ceil(total / limit); return { visits, total, totalPages, }; } // Get a single maintenance visit by ID export async function getMaintenanceVisitById(id: number): Promise { return prisma.maintenanceVisit.findUnique({ where: { id }, include: { vehicle: { select: { id: true, plateNumber: true, manufacturer: true, model: true, year: true, ownerId: true, }, }, customer: { select: { id: true, name: true, phone: true, email: true, }, }, income: { select: { id: true, amount: true, incomeDate: true, }, }, }, }); } // Create a new maintenance visit export async function createMaintenanceVisit(data: CreateMaintenanceVisitData): Promise { // Calculate next visit date based on delay const nextVisitDate = new Date(); nextVisitDate.setMonth(nextVisitDate.getMonth() + data.nextVisitDelay); // Start a transaction to create visit, update vehicle, and create income const result = await prisma.$transaction(async (tx) => { // Create the maintenance visit const visit = await tx.maintenanceVisit.create({ data: { vehicleId: data.vehicleId, customerId: data.customerId, maintenanceJobs: JSON.stringify(data.maintenanceJobs), description: data.description, cost: data.cost, paymentStatus: data.paymentStatus || "pending", kilometers: data.kilometers, visitDate: data.visitDate || new Date(), nextVisitDelay: data.nextVisitDelay, }, }); // Update vehicle's last visit date and suggested next visit date await tx.vehicle.update({ where: { id: data.vehicleId }, data: { lastVisitDate: visit.visitDate, suggestedNextVisitDate: nextVisitDate, }, }); // Create income record await tx.income.create({ data: { maintenanceVisitId: visit.id, amount: data.cost, incomeDate: visit.visitDate, }, }); return visit; }); return result; } // Update an existing maintenance visit export async function updateMaintenanceVisit( id: number, data: UpdateMaintenanceVisitData ): Promise { // Calculate next visit date if delay is updated let nextVisitDate: Date | undefined; if (data.nextVisitDelay) { nextVisitDate = new Date(data.visitDate || new Date()); nextVisitDate.setMonth(nextVisitDate.getMonth() + data.nextVisitDelay); } // Start a transaction to update visit, vehicle, and income const result = await prisma.$transaction(async (tx) => { // Get the current visit to check for changes const currentVisit = await tx.maintenanceVisit.findUnique({ where: { id }, select: { vehicleId: true, cost: true, visitDate: true }, }); if (!currentVisit) { throw new Error("Maintenance visit not found"); } // Update the maintenance visit const visit = await tx.maintenanceVisit.update({ where: { id }, data: { maintenanceJobs: data.maintenanceJobs ? JSON.stringify(data.maintenanceJobs) : undefined, description: data.description, cost: data.cost, paymentStatus: data.paymentStatus, kilometers: data.kilometers, visitDate: data.visitDate, nextVisitDelay: data.nextVisitDelay, }, }); // Update vehicle if visit date or delay changed if (data.visitDate || data.nextVisitDelay) { const updateData: any = {}; if (data.visitDate) { updateData.lastVisitDate = data.visitDate; } if (nextVisitDate) { updateData.suggestedNextVisitDate = nextVisitDate; } if (Object.keys(updateData).length > 0) { await tx.vehicle.update({ where: { id: currentVisit.vehicleId }, data: updateData, }); } } // Update income record if cost or date changed if (data.cost !== undefined || data.visitDate) { await tx.income.updateMany({ where: { maintenanceVisitId: id }, data: { amount: data.cost || currentVisit.cost, incomeDate: data.visitDate || currentVisit.visitDate, }, }); } return visit; }); return result; } // Delete a maintenance visit export async function deleteMaintenanceVisit(id: number): Promise { await prisma.$transaction(async (tx) => { // Get visit details before deletion const visit = await tx.maintenanceVisit.findUnique({ where: { id }, select: { vehicleId: true }, }); if (!visit) { throw new Error("Maintenance visit not found"); } // Delete the maintenance visit (income will be deleted by cascade) await tx.maintenanceVisit.delete({ where: { id }, }); // Update vehicle's last visit date to the most recent remaining visit const lastVisit = await tx.maintenanceVisit.findFirst({ where: { vehicleId: visit.vehicleId }, orderBy: { visitDate: 'desc' }, select: { visitDate: true, nextVisitDelay: true }, }); let updateData: any = {}; if (lastVisit) { updateData.lastVisitDate = lastVisit.visitDate; // Recalculate next visit date const nextDate = new Date(lastVisit.visitDate); nextDate.setMonth(nextDate.getMonth() + lastVisit.nextVisitDelay); updateData.suggestedNextVisitDate = nextDate; } else { // No visits left, clear the dates updateData.lastVisitDate = null; updateData.suggestedNextVisitDate = null; } await tx.vehicle.update({ where: { id: visit.vehicleId }, data: updateData, }); }); } // Get maintenance visits for a specific vehicle export async function getVehicleMaintenanceHistory(vehicleId: number): Promise { return prisma.maintenanceVisit.findMany({ where: { vehicleId }, include: { vehicle: { select: { id: true, plateNumber: true, manufacturer: true, model: true, year: true, }, }, customer: { select: { id: true, name: true, phone: true, email: true, }, }, income: { select: { id: true, amount: true, incomeDate: true, }, }, }, orderBy: { visitDate: 'desc' }, }); } // Get maintenance visits for a specific customer export async function getCustomerMaintenanceHistory(customerId: number): Promise { return prisma.maintenanceVisit.findMany({ where: { customerId }, include: { vehicle: { select: { id: true, plateNumber: true, manufacturer: true, model: true, year: true, }, }, customer: { select: { id: true, name: true, phone: true, email: true, }, }, maintenanceType: { select: { id: true, name: true, description: true, }, }, income: { select: { id: true, amount: true, incomeDate: true, }, }, }, orderBy: { visitDate: 'desc' }, }); }