car_mms/app/lib/vehicle-management.server.ts
2025-09-11 14:22:27 +03:00

368 lines
12 KiB
TypeScript

import { prisma } from "./db.server";
import type { Vehicle } from "@prisma/client";
import type { CreateVehicleData, UpdateVehicleData, VehicleWithOwner, VehicleWithRelations } from "~/types/database";
// Get all vehicles with search and pagination
export async function getVehicles(
searchQuery?: string,
page: number = 1,
limit: number = 10,
ownerId?: number,
plateNumber?: string
): Promise<{
vehicles: VehicleWithOwner[];
total: number;
totalPages: number;
}> {
const offset = (page - 1) * limit;
// Build where clause for search
const whereClause: any = {};
if (ownerId) {
whereClause.ownerId = ownerId;
}
if (plateNumber) {
whereClause.plateNumber = { contains: plateNumber.toLowerCase() };
}
if (searchQuery) {
const searchLower = searchQuery.toLowerCase();
whereClause.OR = [
{ plateNumber: { contains: searchLower } },
{ manufacturer: { contains: searchLower } },
{ model: { contains: searchLower } },
{ bodyType: { contains: searchLower } },
{ owner: { name: { contains: searchLower } } },
];
}
const [vehicles, total] = await Promise.all([
prisma.vehicle.findMany({
where: whereClause,
include: {
owner: {
select: {
id: true,
name: true,
phone: true,
email: true,
},
},
},
orderBy: { createdDate: 'desc' },
skip: offset,
take: limit,
}),
prisma.vehicle.count({ where: whereClause }),
]);
return {
vehicles,
total,
totalPages: Math.ceil(total / limit),
};
}
// Get vehicle by ID with full relationships
export async function getVehicleById(id: number): Promise<VehicleWithRelations | null> {
return await prisma.vehicle.findUnique({
where: { id },
include: {
owner: true,
maintenanceVisits: {
orderBy: { visitDate: 'desc' },
take: 10,
},
},
});
}
// Create new vehicle
export async function createVehicle(
vehicleData: CreateVehicleData
): Promise<{ success: boolean; vehicle?: Vehicle; error?: string }> {
try {
// Check if vehicle with same plate number already exists
const existingVehicle = await prisma.vehicle.findUnique({
where: { plateNumber: vehicleData.plateNumber },
});
if (existingVehicle) {
return { success: false, error: "رقم اللوحة موجود بالفعل" };
}
// Check if owner exists
const owner = await prisma.customer.findUnique({
where: { id: vehicleData.ownerId },
});
if (!owner) {
return { success: false, error: "المالك غير موجود" };
}
// Create vehicle
const vehicle = await prisma.vehicle.create({
data: {
plateNumber: vehicleData.plateNumber.trim(),
bodyType: vehicleData.bodyType.trim(),
manufacturer: vehicleData.manufacturer.trim(),
model: vehicleData.model.trim(),
trim: vehicleData.trim?.trim() || null,
year: vehicleData.year,
transmission: vehicleData.transmission,
fuel: vehicleData.fuel,
cylinders: vehicleData.cylinders || null,
engineDisplacement: vehicleData.engineDisplacement || null,
useType: vehicleData.useType,
ownerId: vehicleData.ownerId,
},
});
return { success: true, vehicle };
} catch (error) {
console.error("Error creating vehicle:", error);
return { success: false, error: "حدث خطأ أثناء إنشاء المركبة" };
}
}
// Update vehicle
export async function updateVehicle(
id: number,
vehicleData: UpdateVehicleData
): Promise<{ success: boolean; vehicle?: Vehicle; error?: string }> {
try {
// Check if vehicle exists
const existingVehicle = await prisma.vehicle.findUnique({
where: { id },
});
if (!existingVehicle) {
return { success: false, error: "المركبة غير موجودة" };
}
// Check for plate number conflicts with other vehicles
if (vehicleData.plateNumber) {
const conflictVehicle = await prisma.vehicle.findFirst({
where: {
AND: [
{ id: { not: id } },
{ plateNumber: vehicleData.plateNumber },
],
},
});
if (conflictVehicle) {
return { success: false, error: "رقم اللوحة موجود بالفعل" };
}
}
// Check if new owner exists (if changing owner)
if (vehicleData.ownerId && vehicleData.ownerId !== existingVehicle.ownerId) {
const owner = await prisma.customer.findUnique({
where: { id: vehicleData.ownerId },
});
if (!owner) {
return { success: false, error: "المالك الجديد غير موجود" };
}
}
// Prepare update data
const updateData: any = {};
if (vehicleData.plateNumber !== undefined) updateData.plateNumber = vehicleData.plateNumber.trim();
if (vehicleData.bodyType !== undefined) updateData.bodyType = vehicleData.bodyType.trim();
if (vehicleData.manufacturer !== undefined) updateData.manufacturer = vehicleData.manufacturer.trim();
if (vehicleData.model !== undefined) updateData.model = vehicleData.model.trim();
if (vehicleData.trim !== undefined) updateData.trim = vehicleData.trim?.trim() || null;
if (vehicleData.year !== undefined) updateData.year = vehicleData.year;
if (vehicleData.transmission !== undefined) updateData.transmission = vehicleData.transmission;
if (vehicleData.fuel !== undefined) updateData.fuel = vehicleData.fuel;
if (vehicleData.cylinders !== undefined) updateData.cylinders = vehicleData.cylinders || null;
if (vehicleData.engineDisplacement !== undefined) updateData.engineDisplacement = vehicleData.engineDisplacement || null;
if (vehicleData.useType !== undefined) updateData.useType = vehicleData.useType;
if (vehicleData.ownerId !== undefined) updateData.ownerId = vehicleData.ownerId;
if (vehicleData.lastVisitDate !== undefined) updateData.lastVisitDate = vehicleData.lastVisitDate;
if (vehicleData.suggestedNextVisitDate !== undefined) updateData.suggestedNextVisitDate = vehicleData.suggestedNextVisitDate;
// Update vehicle
const vehicle = await prisma.vehicle.update({
where: { id },
data: updateData,
});
return { success: true, vehicle };
} catch (error) {
console.error("Error updating vehicle:", error);
return { success: false, error: "حدث خطأ أثناء تحديث المركبة" };
}
}
// Delete vehicle with relationship handling
export async function deleteVehicle(
id: number
): Promise<{ success: boolean; error?: string }> {
try {
// Check if vehicle exists
const existingVehicle = await prisma.vehicle.findUnique({
where: { id },
include: {
maintenanceVisits: true,
},
});
if (!existingVehicle) {
return { success: false, error: "المركبة غير موجودة" };
}
// Check if vehicle has maintenance visits
if (existingVehicle.maintenanceVisits.length > 0) {
return {
success: false,
error: `لا يمكن حذف المركبة لأنها تحتوي على ${existingVehicle.maintenanceVisits.length} زيارة صيانة. يرجى حذف الزيارات أولاً`
};
}
// Delete vehicle
await prisma.vehicle.delete({
where: { id },
});
return { success: true };
} catch (error) {
console.error("Error deleting vehicle:", error);
return { success: false, error: "حدث خطأ أثناء حذف المركبة" };
}
}
// Get vehicles for dropdown/select options
export async function getVehiclesForSelect(ownerId?: number): Promise<{
id: number;
plateNumber: string;
manufacturer: string;
model: string;
year: number;
}[]> {
const whereClause = ownerId ? { ownerId } : {};
return await prisma.vehicle.findMany({
where: whereClause,
select: {
id: true,
plateNumber: true,
manufacturer: true,
model: true,
year: true,
},
orderBy: { plateNumber: 'asc' },
});
}
// Get vehicle statistics
export async function getVehicleStats(vehicleId: number): Promise<{
totalVisits: number;
totalSpent: number;
lastVisitDate?: Date;
nextSuggestedVisitDate?: Date;
averageVisitCost: number;
} | null> {
const vehicle = await prisma.vehicle.findUnique({
where: { id: vehicleId },
include: {
maintenanceVisits: {
select: {
cost: true,
visitDate: true,
},
orderBy: { visitDate: 'desc' },
},
},
});
if (!vehicle) return null;
const totalSpent = vehicle.maintenanceVisits.reduce((sum, visit) => sum + visit.cost, 0);
const averageVisitCost = vehicle.maintenanceVisits.length > 0
? totalSpent / vehicle.maintenanceVisits.length
: 0;
const lastVisitDate = vehicle.maintenanceVisits.length > 0
? vehicle.maintenanceVisits[0].visitDate
: undefined;
return {
totalVisits: vehicle.maintenanceVisits.length,
totalSpent,
lastVisitDate,
nextSuggestedVisitDate: vehicle.suggestedNextVisitDate || undefined,
averageVisitCost,
};
}
// Search vehicles by plate number or manufacturer (for autocomplete)
export async function searchVehicles(query: string, limit: number = 10): Promise<{
id: number;
plateNumber: string;
manufacturer: string;
model: string;
year: number;
owner: { id: number; name: string; };
}[]> {
if (!query || query.trim().length < 2) {
return [];
}
const searchLower = query.toLowerCase();
return await prisma.vehicle.findMany({
where: {
OR: [
{ plateNumber: { contains: searchLower } },
{ manufacturer: { contains: searchLower } },
{ model: { contains: searchLower } },
],
},
select: {
id: true,
plateNumber: true,
manufacturer: true,
model: true,
year: true,
owner: {
select: {
id: true,
name: true,
},
},
},
orderBy: { plateNumber: 'asc' },
take: limit,
});
}
// Get vehicles by owner ID
export async function getVehiclesByOwner(ownerId: number): Promise<VehicleWithOwner[]> {
return await prisma.vehicle.findMany({
where: { ownerId },
include: {
owner: {
select: {
id: true,
name: true,
phone: true,
email: true,
},
},
},
orderBy: { createdDate: 'desc' },
});
}
export async function getMaintenanceVisitsByVehicle(vehicleId: number) {
const visits = await prisma.maintenanceVisit.findMany({
where: { vehicleId: vehicleId },
orderBy: { createdDate: 'desc' },
});
return visits
}