434 lines
14 KiB
TypeScript
434 lines
14 KiB
TypeScript
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
|
|
import {
|
|
createMaintenanceVisit,
|
|
getMaintenanceVisits,
|
|
getMaintenanceVisitById,
|
|
updateMaintenanceVisit,
|
|
deleteMaintenanceVisit,
|
|
getVehicleMaintenanceHistory,
|
|
getCustomerMaintenanceHistory
|
|
} from '../maintenance-visit-management.server';
|
|
import { prisma } from '../db.server';
|
|
import type { Customer, Vehicle } from '@prisma/client';
|
|
|
|
describe('Maintenance Visit Management', () => {
|
|
let testCustomer: Customer;
|
|
let testVehicle: Vehicle;
|
|
|
|
beforeEach(async () => {
|
|
// Create test customer
|
|
testCustomer = await prisma.customer.create({
|
|
data: {
|
|
name: 'أحمد محمد',
|
|
phone: '0501234567',
|
|
email: 'ahmed@example.com',
|
|
},
|
|
});
|
|
|
|
// Create test vehicle
|
|
testVehicle = await prisma.vehicle.create({
|
|
data: {
|
|
plateNumber: 'أ ب ج 123',
|
|
bodyType: 'سيدان',
|
|
manufacturer: 'تويوتا',
|
|
model: 'كامري',
|
|
year: 2020,
|
|
transmission: 'Automatic',
|
|
fuel: 'Gasoline',
|
|
useType: 'personal',
|
|
ownerId: testCustomer.id,
|
|
},
|
|
});
|
|
});
|
|
|
|
afterEach(async () => {
|
|
// Clean up test data
|
|
await prisma.income.deleteMany();
|
|
await prisma.maintenanceVisit.deleteMany();
|
|
await prisma.vehicle.deleteMany();
|
|
await prisma.customer.deleteMany();
|
|
});
|
|
|
|
describe('createMaintenanceVisit', () => {
|
|
it('should create a maintenance visit successfully', async () => {
|
|
const visitData = {
|
|
vehicleId: testVehicle.id,
|
|
customerId: testCustomer.id,
|
|
maintenanceType: 'تغيير زيت',
|
|
description: 'تغيير زيت المحرك والفلتر',
|
|
cost: 150.00,
|
|
paymentStatus: 'paid',
|
|
kilometers: 50000,
|
|
nextVisitDelay: 3,
|
|
};
|
|
|
|
const visit = await createMaintenanceVisit(visitData);
|
|
|
|
expect(visit).toBeDefined();
|
|
expect(visit.vehicleId).toBe(testVehicle.id);
|
|
expect(visit.customerId).toBe(testCustomer.id);
|
|
expect(visit.maintenanceType).toBe('تغيير زيت');
|
|
expect(visit.cost).toBe(150.00);
|
|
expect(visit.nextVisitDelay).toBe(3);
|
|
|
|
// Check that vehicle was updated
|
|
const updatedVehicle = await prisma.vehicle.findUnique({
|
|
where: { id: testVehicle.id },
|
|
});
|
|
expect(updatedVehicle?.lastVisitDate).toBeDefined();
|
|
expect(updatedVehicle?.suggestedNextVisitDate).toBeDefined();
|
|
|
|
// Check that income was created
|
|
const income = await prisma.income.findFirst({
|
|
where: { maintenanceVisitId: visit.id },
|
|
});
|
|
expect(income).toBeDefined();
|
|
expect(income?.amount).toBe(150.00);
|
|
});
|
|
|
|
it('should calculate next visit date correctly', async () => {
|
|
const visitData = {
|
|
vehicleId: testVehicle.id,
|
|
customerId: testCustomer.id,
|
|
maintenanceType: 'فحص دوري',
|
|
description: 'فحص دوري شامل',
|
|
cost: 200.00,
|
|
kilometers: 45000,
|
|
nextVisitDelay: 2, // 2 months
|
|
};
|
|
|
|
await createMaintenanceVisit(visitData);
|
|
|
|
const updatedVehicle = await prisma.vehicle.findUnique({
|
|
where: { id: testVehicle.id },
|
|
});
|
|
|
|
expect(updatedVehicle?.suggestedNextVisitDate).toBeDefined();
|
|
|
|
// Check that the suggested date is approximately 2 months from now
|
|
const now = new Date();
|
|
const expectedDate = new Date();
|
|
expectedDate.setMonth(expectedDate.getMonth() + 2);
|
|
|
|
const actualDate = updatedVehicle!.suggestedNextVisitDate!;
|
|
const timeDiff = Math.abs(actualDate.getTime() - expectedDate.getTime());
|
|
const daysDiff = timeDiff / (1000 * 60 * 60 * 24);
|
|
|
|
expect(daysDiff).toBeLessThan(2); // Allow 2 days difference
|
|
});
|
|
});
|
|
|
|
describe('getMaintenanceVisits', () => {
|
|
beforeEach(async () => {
|
|
// Create test visits
|
|
await createMaintenanceVisit({
|
|
vehicleId: testVehicle.id,
|
|
customerId: testCustomer.id,
|
|
maintenanceType: 'تغيير زيت',
|
|
description: 'تغيير زيت المحرك',
|
|
cost: 150.00,
|
|
kilometers: 50000,
|
|
nextVisitDelay: 3,
|
|
});
|
|
|
|
await createMaintenanceVisit({
|
|
vehicleId: testVehicle.id,
|
|
customerId: testCustomer.id,
|
|
maintenanceType: 'فحص فرامل',
|
|
description: 'فحص وتنظيف الفرامل',
|
|
cost: 100.00,
|
|
kilometers: 52000,
|
|
nextVisitDelay: 2,
|
|
});
|
|
});
|
|
|
|
it('should get all maintenance visits', async () => {
|
|
const result = await getMaintenanceVisits();
|
|
|
|
expect(result.visits).toHaveLength(2);
|
|
expect(result.total).toBe(2);
|
|
expect(result.totalPages).toBe(1);
|
|
});
|
|
|
|
it('should search maintenance visits by maintenance type', async () => {
|
|
const result = await getMaintenanceVisits('تغيير زيت');
|
|
|
|
expect(result.visits).toHaveLength(1);
|
|
expect(result.visits[0].maintenanceType).toBe('تغيير زيت');
|
|
});
|
|
|
|
it('should filter by vehicle ID', async () => {
|
|
const result = await getMaintenanceVisits('', 1, 10, testVehicle.id);
|
|
|
|
expect(result.visits).toHaveLength(2);
|
|
result.visits.forEach(visit => {
|
|
expect(visit.vehicleId).toBe(testVehicle.id);
|
|
});
|
|
});
|
|
|
|
it('should filter by customer ID', async () => {
|
|
const result = await getMaintenanceVisits('', 1, 10, undefined, testCustomer.id);
|
|
|
|
expect(result.visits).toHaveLength(2);
|
|
result.visits.forEach(visit => {
|
|
expect(visit.customerId).toBe(testCustomer.id);
|
|
});
|
|
});
|
|
|
|
it('should paginate results', async () => {
|
|
const result = await getMaintenanceVisits('', 1, 1);
|
|
|
|
expect(result.visits).toHaveLength(1);
|
|
expect(result.total).toBe(2);
|
|
expect(result.totalPages).toBe(2);
|
|
});
|
|
});
|
|
|
|
describe('getMaintenanceVisitById', () => {
|
|
it('should get a maintenance visit by ID', async () => {
|
|
const createdVisit = await createMaintenanceVisit({
|
|
vehicleId: testVehicle.id,
|
|
customerId: testCustomer.id,
|
|
maintenanceType: 'تغيير إطارات',
|
|
description: 'تغيير الإطارات الأمامية',
|
|
cost: 800.00,
|
|
kilometers: 55000,
|
|
nextVisitDelay: 4,
|
|
});
|
|
|
|
const visit = await getMaintenanceVisitById(createdVisit.id);
|
|
|
|
expect(visit).toBeDefined();
|
|
expect(visit?.id).toBe(createdVisit.id);
|
|
expect(visit?.vehicle).toBeDefined();
|
|
expect(visit?.customer).toBeDefined();
|
|
expect(visit?.income).toBeDefined();
|
|
});
|
|
|
|
it('should return null for non-existent visit', async () => {
|
|
const visit = await getMaintenanceVisitById(99999);
|
|
expect(visit).toBeNull();
|
|
});
|
|
});
|
|
|
|
describe('updateMaintenanceVisit', () => {
|
|
it('should update a maintenance visit successfully', async () => {
|
|
const createdVisit = await createMaintenanceVisit({
|
|
vehicleId: testVehicle.id,
|
|
customerId: testCustomer.id,
|
|
maintenanceType: 'فحص دوري',
|
|
description: 'فحص دوري أولي',
|
|
cost: 200.00,
|
|
kilometers: 48000,
|
|
nextVisitDelay: 3,
|
|
});
|
|
|
|
const updateData = {
|
|
maintenanceType: 'فحص شامل',
|
|
description: 'فحص شامل محدث',
|
|
cost: 250.00,
|
|
paymentStatus: 'paid',
|
|
kilometers: 48500,
|
|
nextVisitDelay: 2,
|
|
};
|
|
|
|
const updatedVisit = await updateMaintenanceVisit(createdVisit.id, updateData);
|
|
|
|
expect(updatedVisit.maintenanceType).toBe('فحص شامل');
|
|
expect(updatedVisit.description).toBe('فحص شامل محدث');
|
|
expect(updatedVisit.cost).toBe(250.00);
|
|
expect(updatedVisit.paymentStatus).toBe('paid');
|
|
expect(updatedVisit.kilometers).toBe(48500);
|
|
expect(updatedVisit.nextVisitDelay).toBe(2);
|
|
|
|
// Check that income was updated
|
|
const income = await prisma.income.findFirst({
|
|
where: { maintenanceVisitId: createdVisit.id },
|
|
});
|
|
expect(income?.amount).toBe(250.00);
|
|
});
|
|
|
|
it('should update vehicle dates when visit date changes', async () => {
|
|
const createdVisit = await createMaintenanceVisit({
|
|
vehicleId: testVehicle.id,
|
|
customerId: testCustomer.id,
|
|
maintenanceType: 'صيانة عامة',
|
|
description: 'صيانة عامة',
|
|
cost: 300.00,
|
|
kilometers: 50000,
|
|
nextVisitDelay: 3,
|
|
});
|
|
|
|
const newVisitDate = new Date();
|
|
newVisitDate.setDate(newVisitDate.getDate() - 5); // 5 days ago
|
|
|
|
await updateMaintenanceVisit(createdVisit.id, {
|
|
visitDate: newVisitDate,
|
|
nextVisitDelay: 4,
|
|
});
|
|
|
|
const updatedVehicle = await prisma.vehicle.findUnique({
|
|
where: { id: testVehicle.id },
|
|
});
|
|
|
|
expect(updatedVehicle?.lastVisitDate?.getTime()).toBe(newVisitDate.getTime());
|
|
|
|
// Check that suggested next visit date was recalculated
|
|
const expectedNextDate = new Date(newVisitDate);
|
|
expectedNextDate.setMonth(expectedNextDate.getMonth() + 4);
|
|
|
|
const actualNextDate = updatedVehicle!.suggestedNextVisitDate!;
|
|
const timeDiff = Math.abs(actualNextDate.getTime() - expectedNextDate.getTime());
|
|
const daysDiff = timeDiff / (1000 * 60 * 60 * 24);
|
|
|
|
expect(daysDiff).toBeLessThan(2);
|
|
});
|
|
});
|
|
|
|
describe('deleteMaintenanceVisit', () => {
|
|
it('should delete a maintenance visit successfully', async () => {
|
|
const createdVisit = await createMaintenanceVisit({
|
|
vehicleId: testVehicle.id,
|
|
customerId: testCustomer.id,
|
|
maintenanceType: 'تنظيف مكيف',
|
|
description: 'تنظيف وصيانة المكيف',
|
|
cost: 120.00,
|
|
kilometers: 47000,
|
|
nextVisitDelay: 2,
|
|
});
|
|
|
|
await deleteMaintenanceVisit(createdVisit.id);
|
|
|
|
// Check that visit was deleted
|
|
const deletedVisit = await prisma.maintenanceVisit.findUnique({
|
|
where: { id: createdVisit.id },
|
|
});
|
|
expect(deletedVisit).toBeNull();
|
|
|
|
// Check that income was deleted (cascade)
|
|
const income = await prisma.income.findFirst({
|
|
where: { maintenanceVisitId: createdVisit.id },
|
|
});
|
|
expect(income).toBeNull();
|
|
|
|
// Check that vehicle dates were cleared
|
|
const updatedVehicle = await prisma.vehicle.findUnique({
|
|
where: { id: testVehicle.id },
|
|
});
|
|
expect(updatedVehicle?.lastVisitDate).toBeNull();
|
|
expect(updatedVehicle?.suggestedNextVisitDate).toBeNull();
|
|
});
|
|
|
|
it('should update vehicle dates to most recent remaining visit after deletion', async () => {
|
|
// Create two visits
|
|
const firstVisit = await createMaintenanceVisit({
|
|
vehicleId: testVehicle.id,
|
|
customerId: testCustomer.id,
|
|
maintenanceType: 'زيت قديم',
|
|
description: 'تغيير زيت قديم',
|
|
cost: 100.00,
|
|
kilometers: 45000,
|
|
nextVisitDelay: 3,
|
|
visitDate: new Date('2023-01-01'),
|
|
});
|
|
|
|
const secondVisit = await createMaintenanceVisit({
|
|
vehicleId: testVehicle.id,
|
|
customerId: testCustomer.id,
|
|
maintenanceType: 'زيت جديد',
|
|
description: 'تغيير زيت جديد',
|
|
cost: 150.00,
|
|
kilometers: 50000,
|
|
nextVisitDelay: 2,
|
|
visitDate: new Date('2023-02-01'),
|
|
});
|
|
|
|
// Delete the more recent visit
|
|
await deleteMaintenanceVisit(secondVisit.id);
|
|
|
|
// Check that vehicle dates were updated to the remaining visit
|
|
const updatedVehicle = await prisma.vehicle.findUnique({
|
|
where: { id: testVehicle.id },
|
|
});
|
|
|
|
expect(updatedVehicle?.lastVisitDate?.getTime()).toBe(new Date('2023-01-01').getTime());
|
|
|
|
// Check that suggested next visit date was recalculated based on first visit
|
|
const expectedNextDate = new Date('2023-01-01');
|
|
expectedNextDate.setMonth(expectedNextDate.getMonth() + 3);
|
|
|
|
const actualNextDate = updatedVehicle!.suggestedNextVisitDate!;
|
|
expect(actualNextDate.getTime()).toBe(expectedNextDate.getTime());
|
|
});
|
|
});
|
|
|
|
describe('getVehicleMaintenanceHistory', () => {
|
|
it('should get maintenance history for a vehicle', async () => {
|
|
await createMaintenanceVisit({
|
|
vehicleId: testVehicle.id,
|
|
customerId: testCustomer.id,
|
|
maintenanceType: 'زيت أول',
|
|
description: 'تغيير زيت أول',
|
|
cost: 100.00,
|
|
kilometers: 45000,
|
|
nextVisitDelay: 3,
|
|
});
|
|
|
|
await createMaintenanceVisit({
|
|
vehicleId: testVehicle.id,
|
|
customerId: testCustomer.id,
|
|
maintenanceType: 'زيت ثاني',
|
|
description: 'تغيير زيت ثاني',
|
|
cost: 120.00,
|
|
kilometers: 48000,
|
|
nextVisitDelay: 3,
|
|
});
|
|
|
|
const history = await getVehicleMaintenanceHistory(testVehicle.id);
|
|
|
|
expect(history).toHaveLength(2);
|
|
expect(history[0].vehicleId).toBe(testVehicle.id);
|
|
expect(history[1].vehicleId).toBe(testVehicle.id);
|
|
|
|
// Should be ordered by visit date descending
|
|
expect(new Date(history[0].visitDate).getTime()).toBeGreaterThanOrEqual(
|
|
new Date(history[1].visitDate).getTime()
|
|
);
|
|
});
|
|
});
|
|
|
|
describe('getCustomerMaintenanceHistory', () => {
|
|
it('should get maintenance history for a customer', async () => {
|
|
await createMaintenanceVisit({
|
|
vehicleId: testVehicle.id,
|
|
customerId: testCustomer.id,
|
|
maintenanceType: 'صيانة أولى',
|
|
description: 'صيانة أولى',
|
|
cost: 200.00,
|
|
kilometers: 45000,
|
|
nextVisitDelay: 3,
|
|
});
|
|
|
|
await createMaintenanceVisit({
|
|
vehicleId: testVehicle.id,
|
|
customerId: testCustomer.id,
|
|
maintenanceType: 'صيانة ثانية',
|
|
description: 'صيانة ثانية',
|
|
cost: 250.00,
|
|
kilometers: 48000,
|
|
nextVisitDelay: 2,
|
|
});
|
|
|
|
const history = await getCustomerMaintenanceHistory(testCustomer.id);
|
|
|
|
expect(history).toHaveLength(2);
|
|
expect(history[0].customerId).toBe(testCustomer.id);
|
|
expect(history[1].customerId).toBe(testCustomer.id);
|
|
|
|
// Should be ordered by visit date descending
|
|
expect(new Date(history[0].visitDate).getTime()).toBeGreaterThanOrEqual(
|
|
new Date(history[1].visitDate).getTime()
|
|
);
|
|
});
|
|
});
|
|
}); |