add pricing feature
This commit is contained in:
@@ -77,6 +77,22 @@ describe('Vehicles API', () => {
|
||||
expect(res.body.data.dropoffLocations).toEqual(['Rabat Downtown'])
|
||||
})
|
||||
|
||||
it('creates a vehicle without a daily rate and defaults it to zero', async () => {
|
||||
const res = await request(app)
|
||||
.post('/api/v1/vehicles')
|
||||
.set(authHeader(token))
|
||||
.send({
|
||||
make: 'Nissan',
|
||||
model: 'Micra',
|
||||
year: 2024,
|
||||
licensePlate: `ZERO-${Date.now()}`,
|
||||
category: 'ECONOMY',
|
||||
})
|
||||
|
||||
expect(res.status).toBe(201)
|
||||
expect(res.body.data.dailyRate).toBe(0)
|
||||
})
|
||||
|
||||
it('returns 400 for missing required fields', async () => {
|
||||
const res = await request(app)
|
||||
.post('/api/v1/vehicles')
|
||||
@@ -228,4 +244,101 @@ describe('Vehicles API', () => {
|
||||
expect(res.status).toBe(404)
|
||||
})
|
||||
})
|
||||
|
||||
describe('Vehicle pricing management', () => {
|
||||
it('returns a default pricing configuration for a vehicle', async () => {
|
||||
const vehicle = await createVehicle(companyId, { dailyRate: 6250 })
|
||||
|
||||
const res = await request(app)
|
||||
.get(`/api/v1/vehicles/${vehicle.id}/pricing`)
|
||||
.set(authHeader(token))
|
||||
|
||||
expect(res.status).toBe(200)
|
||||
expect(res.body.data.configuration.baseDailyRate).toBe(6250)
|
||||
expect(res.body.data.configuration.pricingMode).toBe('MANUAL')
|
||||
expect(Array.isArray(res.body.data.preview.next14Days)).toBe(true)
|
||||
expect(res.body.data.preview.next14Days.length).toBe(14)
|
||||
})
|
||||
|
||||
it('updates vehicle pricing configuration and syncs the vehicle daily rate', async () => {
|
||||
const vehicle = await createVehicle(companyId, { dailyRate: 5000 })
|
||||
|
||||
const res = await request(app)
|
||||
.put(`/api/v1/vehicles/${vehicle.id}/pricing`)
|
||||
.set(authHeader(token))
|
||||
.send({
|
||||
pricingMode: 'AUTOMATIC',
|
||||
baseDailyRate: 6400,
|
||||
weeklyRate: 39900,
|
||||
weekendRate: 7200,
|
||||
holidayRate: null,
|
||||
monthlyRate: 118000,
|
||||
longTermDailyRate: 5600,
|
||||
minimumDailyRate: 5900,
|
||||
maximumDailyRate: 8600,
|
||||
maxDailyPriceMovementPct: 12,
|
||||
automaticPricingEnabled: true,
|
||||
approvalRequired: true,
|
||||
note: 'Summer setup',
|
||||
})
|
||||
|
||||
expect(res.status).toBe(200)
|
||||
expect(res.body.data.configuration.pricingMode).toBe('AUTOMATIC')
|
||||
expect(res.body.data.configuration.baseDailyRate).toBe(6400)
|
||||
expect(res.body.data.history[0].note).toBe('Summer setup')
|
||||
|
||||
const updatedVehicle = await prisma.vehicle.findUniqueOrThrow({ where: { id: vehicle.id } })
|
||||
expect(updatedVehicle.dailyRate).toBe(6400)
|
||||
})
|
||||
|
||||
it('creates, updates, and deletes pricing rules', async () => {
|
||||
const vehicle = await createVehicle(companyId, { dailyRate: 5400 })
|
||||
|
||||
const createRes = await request(app)
|
||||
.post(`/api/v1/vehicles/${vehicle.id}/pricing/rules`)
|
||||
.set(authHeader(token))
|
||||
.send({
|
||||
name: 'Summer high season',
|
||||
ruleType: 'SEASONAL',
|
||||
startDate: '2026-07-01',
|
||||
endDate: '2026-08-31',
|
||||
dailyRate: 7800,
|
||||
weeklyRate: 46900,
|
||||
weekendRate: null,
|
||||
monthlyRate: null,
|
||||
minDailyRate: null,
|
||||
maxDailyRate: null,
|
||||
automaticAdjustmentPct: null,
|
||||
priceAdjustment: null,
|
||||
isActive: true,
|
||||
sortOrder: 30,
|
||||
})
|
||||
|
||||
expect(createRes.status).toBe(201)
|
||||
expect(createRes.body.data.rules.length).toBe(1)
|
||||
const createdRule = createRes.body.data.rules[0]
|
||||
|
||||
const updateRes = await request(app)
|
||||
.patch(`/api/v1/vehicles/${vehicle.id}/pricing/rules/${createdRule.id}`)
|
||||
.set(authHeader(token))
|
||||
.send({
|
||||
dailyRate: 8100,
|
||||
weeklyRate: 48900,
|
||||
sortOrder: 40,
|
||||
})
|
||||
|
||||
expect(updateRes.status).toBe(200)
|
||||
const updatedRule = updateRes.body.data.rules.find((rule: any) => rule.id === createdRule.id)
|
||||
expect(updatedRule.dailyRate).toBe(8100)
|
||||
expect(updatedRule.weeklyRate).toBe(48900)
|
||||
|
||||
const deleteRes = await request(app)
|
||||
.delete(`/api/v1/vehicles/${vehicle.id}/pricing/rules/${createdRule.id}`)
|
||||
.set(authHeader(token))
|
||||
|
||||
expect(deleteRes.status).toBe(200)
|
||||
expect(deleteRes.body.data.rules).toHaveLength(0)
|
||||
expect(deleteRes.body.data.history.length).toBeGreaterThanOrEqual(1)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -32,6 +32,9 @@ const delegates = [
|
||||
'offerVehicle',
|
||||
'offer',
|
||||
'vehicleCalendarBlock',
|
||||
'vehiclePriceHistory',
|
||||
'vehiclePricingRule',
|
||||
'vehiclePricingConfiguration',
|
||||
'maintenanceLog',
|
||||
'vehicle',
|
||||
'employee',
|
||||
|
||||
Reference in New Issue
Block a user