uup
This commit is contained in:
365
app/lib/maintenance-visit-management.server.ts
Normal file
365
app/lib/maintenance-visit-management.server.ts
Normal file
@@ -0,0 +1,365 @@
|
||||
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<MaintenanceVisitWithRelations | null> {
|
||||
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<MaintenanceVisit> {
|
||||
// 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<MaintenanceVisit> {
|
||||
// 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<void> {
|
||||
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<MaintenanceVisitWithRelations[]> {
|
||||
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<MaintenanceVisitWithRelations[]> {
|
||||
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' },
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user