archetecture security fix
This commit is contained in:
@@ -0,0 +1,14 @@
|
||||
export declare function presentVehicle(vehicle: any): any;
|
||||
export declare function presentVehicleList(vehicles: any[], meta: {
|
||||
total: number;
|
||||
page: number;
|
||||
pageSize: number;
|
||||
totalPages: number;
|
||||
}): {
|
||||
total: number;
|
||||
page: number;
|
||||
pageSize: number;
|
||||
totalPages: number;
|
||||
data: any[];
|
||||
};
|
||||
//# sourceMappingURL=vehicle.presenter.d.ts.map
|
||||
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"vehicle.presenter.d.ts","sourceRoot":"","sources":["../../../src/modules/vehicles/vehicle.presenter.ts"],"names":[],"mappings":"AAIA,wBAAgB,cAAc,CAAC,OAAO,EAAE,GAAG,OAO1C;AAED,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,GAAG,EAAE,EAAE,IAAI,EAAE;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAA;CAAE;WAA5D,MAAM;UAAQ,MAAM;cAAY,MAAM;gBAAc,MAAM;;EAE5H"}
|
||||
@@ -0,0 +1,19 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.presentVehicle = presentVehicle;
|
||||
exports.presentVehicleList = presentVehicleList;
|
||||
function normalizeLocations(value) {
|
||||
return Array.isArray(value) ? value.filter((entry) => typeof entry === 'string') : [];
|
||||
}
|
||||
function presentVehicle(vehicle) {
|
||||
return {
|
||||
...vehicle,
|
||||
pickupLocations: normalizeLocations(vehicle?.pickupLocations),
|
||||
allowDifferentDropoff: Boolean(vehicle?.allowDifferentDropoff),
|
||||
dropoffLocations: normalizeLocations(vehicle?.dropoffLocations),
|
||||
};
|
||||
}
|
||||
function presentVehicleList(vehicles, meta) {
|
||||
return { data: vehicles, ...meta };
|
||||
}
|
||||
//# sourceMappingURL=vehicle.presenter.js.map
|
||||
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"vehicle.presenter.js","sourceRoot":"","sources":["../../../src/modules/vehicles/vehicle.presenter.ts"],"names":[],"mappings":";;AAIA,wCAOC;AAED,gDAEC;AAfD,SAAS,kBAAkB,CAAC,KAAc;IACxC,OAAO,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,KAAK,EAAmB,EAAE,CAAC,OAAO,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,EAAE,CAAA;AACxG,CAAC;AAED,SAAgB,cAAc,CAAC,OAAY;IACzC,OAAO;QACL,GAAG,OAAO;QACV,eAAe,EAAE,kBAAkB,CAAC,OAAO,EAAE,eAAe,CAAC;QAC7D,qBAAqB,EAAE,OAAO,CAAC,OAAO,EAAE,qBAAqB,CAAC;QAC9D,gBAAgB,EAAE,kBAAkB,CAAC,OAAO,EAAE,gBAAgB,CAAC;KAChE,CAAA;AACH,CAAC;AAED,SAAgB,kBAAkB,CAAC,QAAe,EAAE,IAA2E;IAC7H,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,GAAG,IAAI,EAAE,CAAA;AACpC,CAAC"}
|
||||
@@ -0,0 +1,23 @@
|
||||
export declare function findMany(where: any, skip: number, take: number): Promise<any>;
|
||||
export declare function findById(id: string, companyId: string): Promise<any>;
|
||||
export declare function findFirst(id: string, companyId: string): Promise<any>;
|
||||
export declare function create(data: any): Promise<any>;
|
||||
export declare function updateById(id: string, data: any): Promise<any>;
|
||||
export declare function softDelete(id: string, companyId: string): Promise<any>;
|
||||
export declare function setPublished(id: string, companyId: string, isPublished: boolean): Promise<any>;
|
||||
export declare function findReservationConflicts(vehicleId: string, companyId: string, start: Date, end: Date): Promise<any>;
|
||||
export declare function findCalendarBlockConflicts(vehicleId: string, start: Date, end: Date): Promise<any>;
|
||||
export declare function findCalendarEvents(vehicleId: string, companyId: string, rangeStart: Date, rangeEnd: Date): Promise<any>;
|
||||
export declare function createCalendarBlock(data: any): Promise<any>;
|
||||
export declare function deleteCalendarBlock(id: string, vehicleId: string): Promise<any>;
|
||||
export declare function findMaintenanceLogs(vehicleId: string): Promise<any>;
|
||||
export declare function createMaintenanceLog(data: any): Promise<any>;
|
||||
export declare function findPricingConfiguration(vehicleId: string): Promise<any>;
|
||||
export declare function createPricingConfiguration(data: any): Promise<any>;
|
||||
export declare function updatePricingConfiguration(id: string, data: any): Promise<any>;
|
||||
export declare function findPricingRule(id: string, configurationId: string): Promise<any>;
|
||||
export declare function createPricingRule(data: any): Promise<any>;
|
||||
export declare function updatePricingRule(id: string, data: any): Promise<any>;
|
||||
export declare function deletePricingRule(id: string, configurationId: string): Promise<any>;
|
||||
export declare function createPriceHistory(data: any): Promise<any>;
|
||||
//# sourceMappingURL=vehicle.repo.d.ts.map
|
||||
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"vehicle.repo.d.ts","sourceRoot":"","sources":["../../../src/modules/vehicles/vehicle.repo.ts"],"names":[],"mappings":"AAEA,wBAAsB,QAAQ,CAAC,KAAK,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,gBAKpE;AAED,wBAAsB,QAAQ,CAAC,EAAE,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,gBAE3D;AAED,wBAAsB,SAAS,CAAC,EAAE,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,gBAE5D;AAED,wBAAsB,MAAM,CAAC,IAAI,EAAE,GAAG,gBAErC;AAED,wBAAsB,UAAU,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,GAAG,gBAErD;AAED,wBAAsB,UAAU,CAAC,EAAE,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,gBAE7D;AAED,wBAAsB,YAAY,CAAC,EAAE,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,WAAW,EAAE,OAAO,gBAErF;AAED,wBAAsB,wBAAwB,CAAC,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,IAAI,EAAE,GAAG,EAAE,IAAI,gBAW1G;AAED,wBAAsB,0BAA0B,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,IAAI,EAAE,GAAG,EAAE,IAAI,gBAKzF;AAED,wBAAsB,kBAAkB,CAAC,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,UAAU,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,gBAqB9G;AAED,wBAAsB,mBAAmB,CAAC,IAAI,EAAE,GAAG,gBAElD;AAED,wBAAsB,mBAAmB,CAAC,EAAE,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,gBAEtE;AAED,wBAAsB,mBAAmB,CAAC,SAAS,EAAE,MAAM,gBAE1D;AAED,wBAAsB,oBAAoB,CAAC,IAAI,EAAE,GAAG,gBAEnD;AAED,wBAAsB,wBAAwB,CAAC,SAAS,EAAE,MAAM,gBAQ/D;AAED,wBAAsB,0BAA0B,CAAC,IAAI,EAAE,GAAG,gBAQzD;AAED,wBAAsB,0BAA0B,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,GAAG,gBASrE;AAED,wBAAsB,eAAe,CAAC,EAAE,EAAE,MAAM,EAAE,eAAe,EAAE,MAAM,gBAExE;AAED,wBAAsB,iBAAiB,CAAC,IAAI,EAAE,GAAG,gBAEhD;AAED,wBAAsB,iBAAiB,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,GAAG,gBAE5D;AAED,wBAAsB,iBAAiB,CAAC,EAAE,EAAE,MAAM,EAAE,eAAe,EAAE,MAAM,gBAE1E;AAED,wBAAsB,kBAAkB,CAAC,IAAI,EAAE,GAAG,gBAEjD"}
|
||||
+145
@@ -0,0 +1,145 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.findMany = findMany;
|
||||
exports.findById = findById;
|
||||
exports.findFirst = findFirst;
|
||||
exports.create = create;
|
||||
exports.updateById = updateById;
|
||||
exports.softDelete = softDelete;
|
||||
exports.setPublished = setPublished;
|
||||
exports.findReservationConflicts = findReservationConflicts;
|
||||
exports.findCalendarBlockConflicts = findCalendarBlockConflicts;
|
||||
exports.findCalendarEvents = findCalendarEvents;
|
||||
exports.createCalendarBlock = createCalendarBlock;
|
||||
exports.deleteCalendarBlock = deleteCalendarBlock;
|
||||
exports.findMaintenanceLogs = findMaintenanceLogs;
|
||||
exports.createMaintenanceLog = createMaintenanceLog;
|
||||
exports.findPricingConfiguration = findPricingConfiguration;
|
||||
exports.createPricingConfiguration = createPricingConfiguration;
|
||||
exports.updatePricingConfiguration = updatePricingConfiguration;
|
||||
exports.findPricingRule = findPricingRule;
|
||||
exports.createPricingRule = createPricingRule;
|
||||
exports.updatePricingRule = updatePricingRule;
|
||||
exports.deletePricingRule = deletePricingRule;
|
||||
exports.createPriceHistory = createPriceHistory;
|
||||
const prisma_1 = require("../../lib/prisma");
|
||||
async function findMany(where, skip, take) {
|
||||
return Promise.all([
|
||||
prisma_1.prisma.vehicle.findMany({ where, skip, take, orderBy: { createdAt: 'desc' } }),
|
||||
prisma_1.prisma.vehicle.count({ where }),
|
||||
]);
|
||||
}
|
||||
async function findById(id, companyId) {
|
||||
return prisma_1.prisma.vehicle.findFirst({ where: { id, companyId } });
|
||||
}
|
||||
async function findFirst(id, companyId) {
|
||||
return prisma_1.prisma.vehicle.findFirst({ where: { id, companyId } });
|
||||
}
|
||||
async function create(data) {
|
||||
return prisma_1.prisma.vehicle.create({ data });
|
||||
}
|
||||
async function updateById(id, data) {
|
||||
return prisma_1.prisma.vehicle.update({ where: { id }, data });
|
||||
}
|
||||
async function softDelete(id, companyId) {
|
||||
return prisma_1.prisma.vehicle.updateMany({ where: { id, companyId }, data: { status: 'OUT_OF_SERVICE', isPublished: false } });
|
||||
}
|
||||
async function setPublished(id, companyId, isPublished) {
|
||||
return prisma_1.prisma.vehicle.updateMany({ where: { id, companyId }, data: { isPublished } });
|
||||
}
|
||||
async function findReservationConflicts(vehicleId, companyId, start, end) {
|
||||
return prisma_1.prisma.reservation.findMany({
|
||||
where: {
|
||||
vehicleId,
|
||||
companyId,
|
||||
status: { in: ['CONFIRMED', 'ACTIVE'] },
|
||||
startDate: { lt: end },
|
||||
endDate: { gt: start },
|
||||
},
|
||||
select: { id: true, startDate: true, endDate: true, status: true },
|
||||
});
|
||||
}
|
||||
async function findCalendarBlockConflicts(vehicleId, start, end) {
|
||||
return prisma_1.prisma.vehicleCalendarBlock.findMany({
|
||||
where: { vehicleId, startDate: { lt: end }, endDate: { gt: start } },
|
||||
select: { id: true, startDate: true, endDate: true, type: true, reason: true },
|
||||
});
|
||||
}
|
||||
async function findCalendarEvents(vehicleId, companyId, rangeStart, rangeEnd) {
|
||||
return Promise.all([
|
||||
prisma_1.prisma.reservation.findMany({
|
||||
where: {
|
||||
vehicleId,
|
||||
companyId,
|
||||
status: { in: ['DRAFT', 'CONFIRMED', 'ACTIVE'] },
|
||||
startDate: { lt: rangeEnd },
|
||||
endDate: { gt: rangeStart },
|
||||
},
|
||||
select: {
|
||||
id: true, startDate: true, endDate: true, status: true,
|
||||
customer: { select: { firstName: true, lastName: true } },
|
||||
},
|
||||
orderBy: { startDate: 'asc' },
|
||||
}),
|
||||
prisma_1.prisma.vehicleCalendarBlock.findMany({
|
||||
where: { vehicleId, startDate: { lt: rangeEnd }, endDate: { gt: rangeStart } },
|
||||
orderBy: { startDate: 'asc' },
|
||||
}),
|
||||
]);
|
||||
}
|
||||
async function createCalendarBlock(data) {
|
||||
return prisma_1.prisma.vehicleCalendarBlock.create({ data });
|
||||
}
|
||||
async function deleteCalendarBlock(id, vehicleId) {
|
||||
return prisma_1.prisma.vehicleCalendarBlock.deleteMany({ where: { id, vehicleId } });
|
||||
}
|
||||
async function findMaintenanceLogs(vehicleId) {
|
||||
return prisma_1.prisma.maintenanceLog.findMany({ where: { vehicleId }, orderBy: { performedAt: 'desc' } });
|
||||
}
|
||||
async function createMaintenanceLog(data) {
|
||||
return prisma_1.prisma.maintenanceLog.create({ data });
|
||||
}
|
||||
async function findPricingConfiguration(vehicleId) {
|
||||
return prisma_1.prisma.vehiclePricingConfiguration.findUnique({
|
||||
where: { vehicleId },
|
||||
include: {
|
||||
rules: { orderBy: [{ sortOrder: 'asc' }, { startDate: 'asc' }, { createdAt: 'asc' }] },
|
||||
history: { orderBy: { createdAt: 'desc' }, take: 20 },
|
||||
},
|
||||
});
|
||||
}
|
||||
async function createPricingConfiguration(data) {
|
||||
return prisma_1.prisma.vehiclePricingConfiguration.create({
|
||||
data,
|
||||
include: {
|
||||
rules: { orderBy: [{ sortOrder: 'asc' }, { startDate: 'asc' }, { createdAt: 'asc' }] },
|
||||
history: { orderBy: { createdAt: 'desc' }, take: 20 },
|
||||
},
|
||||
});
|
||||
}
|
||||
async function updatePricingConfiguration(id, data) {
|
||||
return prisma_1.prisma.vehiclePricingConfiguration.update({
|
||||
where: { id },
|
||||
data,
|
||||
include: {
|
||||
rules: { orderBy: [{ sortOrder: 'asc' }, { startDate: 'asc' }, { createdAt: 'asc' }] },
|
||||
history: { orderBy: { createdAt: 'desc' }, take: 20 },
|
||||
},
|
||||
});
|
||||
}
|
||||
async function findPricingRule(id, configurationId) {
|
||||
return prisma_1.prisma.vehiclePricingRule.findFirst({ where: { id, configurationId } });
|
||||
}
|
||||
async function createPricingRule(data) {
|
||||
return prisma_1.prisma.vehiclePricingRule.create({ data });
|
||||
}
|
||||
async function updatePricingRule(id, data) {
|
||||
return prisma_1.prisma.vehiclePricingRule.update({ where: { id }, data });
|
||||
}
|
||||
async function deletePricingRule(id, configurationId) {
|
||||
return prisma_1.prisma.vehiclePricingRule.deleteMany({ where: { id, configurationId } });
|
||||
}
|
||||
async function createPriceHistory(data) {
|
||||
return prisma_1.prisma.vehiclePriceHistory.create({ data });
|
||||
}
|
||||
//# sourceMappingURL=vehicle.repo.js.map
|
||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1,3 @@
|
||||
declare const router: import("express-serve-static-core").Router;
|
||||
export default router;
|
||||
//# sourceMappingURL=vehicle.routes.d.ts.map
|
||||
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"vehicle.routes.d.ts","sourceRoot":"","sources":["../../../src/modules/vehicles/vehicle.routes.ts"],"names":[],"mappings":"AAgBA,QAAA,MAAM,MAAM,4CAAW,CAAA;AA0KvB,eAAe,MAAM,CAAA"}
|
||||
+255
@@ -0,0 +1,255 @@
|
||||
"use strict";
|
||||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
||||
if (k2 === undefined) k2 = k;
|
||||
var desc = Object.getOwnPropertyDescriptor(m, k);
|
||||
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
||||
desc = { enumerable: true, get: function() { return m[k]; } };
|
||||
}
|
||||
Object.defineProperty(o, k2, desc);
|
||||
}) : (function(o, m, k, k2) {
|
||||
if (k2 === undefined) k2 = k;
|
||||
o[k2] = m[k];
|
||||
}));
|
||||
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
||||
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
||||
}) : function(o, v) {
|
||||
o["default"] = v;
|
||||
});
|
||||
var __importStar = (this && this.__importStar) || (function () {
|
||||
var ownKeys = function(o) {
|
||||
ownKeys = Object.getOwnPropertyNames || function (o) {
|
||||
var ar = [];
|
||||
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
||||
return ar;
|
||||
};
|
||||
return ownKeys(o);
|
||||
};
|
||||
return function (mod) {
|
||||
if (mod && mod.__esModule) return mod;
|
||||
var result = {};
|
||||
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
||||
__setModuleDefault(result, mod);
|
||||
return result;
|
||||
};
|
||||
})();
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
const express_1 = require("express");
|
||||
const requireCompanyAuth_1 = require("../../middleware/requireCompanyAuth");
|
||||
const requireTenant_1 = require("../../middleware/requireTenant");
|
||||
const requireSubscription_1 = require("../../middleware/requireSubscription");
|
||||
const requireRole_1 = require("../../middleware/requireRole");
|
||||
const validate_1 = require("../../http/validate");
|
||||
const respond_1 = require("../../http/respond");
|
||||
const upload_1 = require("../../http/upload");
|
||||
const service = __importStar(require("./vehicle.service"));
|
||||
const vehicle_schemas_1 = require("./vehicle.schemas");
|
||||
const router = (0, express_1.Router)();
|
||||
router.use(requireCompanyAuth_1.requireCompanyAuth, requireTenant_1.requireTenant, requireSubscription_1.requireSubscription);
|
||||
router.get('/', async (req, res, next) => {
|
||||
try {
|
||||
const query = (0, validate_1.parseQuery)(vehicle_schemas_1.listQuerySchema, req);
|
||||
const result = await service.listVehicles(req.companyId, query);
|
||||
res.json(result);
|
||||
}
|
||||
catch (err) {
|
||||
next(err);
|
||||
}
|
||||
});
|
||||
router.post('/', (0, requireRole_1.requireRole)('MANAGER'), async (req, res, next) => {
|
||||
try {
|
||||
const body = (0, validate_1.parseBody)(vehicle_schemas_1.vehicleSchema, req);
|
||||
const vehicle = await service.createVehicle(body, req.companyId);
|
||||
(0, respond_1.created)(res, vehicle);
|
||||
}
|
||||
catch (err) {
|
||||
next(err);
|
||||
}
|
||||
});
|
||||
router.get('/:id', async (req, res, next) => {
|
||||
try {
|
||||
const { id } = (0, validate_1.parseParams)(vehicle_schemas_1.idParamSchema, req);
|
||||
const vehicle = await service.getVehicle(id, req.companyId);
|
||||
(0, respond_1.ok)(res, vehicle);
|
||||
}
|
||||
catch (err) {
|
||||
next(err);
|
||||
}
|
||||
});
|
||||
router.patch('/:id', (0, requireRole_1.requireRole)('MANAGER'), async (req, res, next) => {
|
||||
try {
|
||||
const { id } = (0, validate_1.parseParams)(vehicle_schemas_1.idParamSchema, req);
|
||||
const body = (0, validate_1.parseBody)(vehicle_schemas_1.vehicleSchema.partial(), req);
|
||||
const vehicle = await service.updateVehicle(id, req.companyId, body);
|
||||
(0, respond_1.ok)(res, vehicle);
|
||||
}
|
||||
catch (err) {
|
||||
next(err);
|
||||
}
|
||||
});
|
||||
router.delete('/:id', (0, requireRole_1.requireRole)('MANAGER'), async (req, res, next) => {
|
||||
try {
|
||||
const { id } = (0, validate_1.parseParams)(vehicle_schemas_1.idParamSchema, req);
|
||||
await service.deleteVehicle(id, req.companyId);
|
||||
(0, respond_1.ok)(res, { success: true });
|
||||
}
|
||||
catch (err) {
|
||||
next(err);
|
||||
}
|
||||
});
|
||||
router.post('/:id/photos', (0, requireRole_1.requireRole)('MANAGER'), upload_1.imageUpload.array('photos', 10), async (req, res, next) => {
|
||||
try {
|
||||
const { id } = (0, validate_1.parseParams)(vehicle_schemas_1.idParamSchema, req);
|
||||
const files = req.files;
|
||||
(0, upload_1.assertImageFiles)(files);
|
||||
const updated = await service.uploadPhotos(id, req.companyId, files);
|
||||
(0, respond_1.ok)(res, updated);
|
||||
}
|
||||
catch (err) {
|
||||
next(err);
|
||||
}
|
||||
});
|
||||
router.delete('/:id/photos/:idx', (0, requireRole_1.requireRole)('MANAGER'), async (req, res, next) => {
|
||||
try {
|
||||
const { id, idx } = (0, validate_1.parseParams)(vehicle_schemas_1.photoIdxSchema, req);
|
||||
const updated = await service.deletePhoto(id, req.companyId, parseInt(idx, 10));
|
||||
(0, respond_1.ok)(res, updated);
|
||||
}
|
||||
catch (err) {
|
||||
next(err);
|
||||
}
|
||||
});
|
||||
router.patch('/:id/status', (0, requireRole_1.requireRole)('MANAGER'), async (req, res, next) => {
|
||||
try {
|
||||
const { id } = (0, validate_1.parseParams)(vehicle_schemas_1.idParamSchema, req);
|
||||
const { status } = (0, validate_1.parseBody)(vehicle_schemas_1.statusSchema, req);
|
||||
(0, respond_1.ok)(res, await service.setStatus(id, req.companyId, status));
|
||||
}
|
||||
catch (err) {
|
||||
next(err);
|
||||
}
|
||||
});
|
||||
router.patch('/:id/publish', (0, requireRole_1.requireRole)('MANAGER'), async (req, res, next) => {
|
||||
try {
|
||||
const { id } = (0, validate_1.parseParams)(vehicle_schemas_1.idParamSchema, req);
|
||||
const { isPublished } = (0, validate_1.parseBody)(vehicle_schemas_1.publishSchema, req);
|
||||
await service.setPublished(id, req.companyId, isPublished);
|
||||
(0, respond_1.ok)(res, { success: true, isPublished });
|
||||
}
|
||||
catch (err) {
|
||||
next(err);
|
||||
}
|
||||
});
|
||||
router.get('/:id/availability', async (req, res, next) => {
|
||||
try {
|
||||
const { id } = (0, validate_1.parseParams)(vehicle_schemas_1.idParamSchema, req);
|
||||
const { startDate, endDate } = (0, validate_1.parseQuery)(vehicle_schemas_1.availabilityQuerySchema, req);
|
||||
const result = await service.checkAvailability(id, req.companyId, startDate, endDate);
|
||||
(0, respond_1.ok)(res, result);
|
||||
}
|
||||
catch (err) {
|
||||
next(err);
|
||||
}
|
||||
});
|
||||
router.get('/:id/calendar', async (req, res, next) => {
|
||||
try {
|
||||
const { id } = (0, validate_1.parseParams)(vehicle_schemas_1.idParamSchema, req);
|
||||
const { year, month } = (0, validate_1.parseQuery)(vehicle_schemas_1.calendarQuerySchema, req);
|
||||
const events = await service.getCalendar(id, req.companyId, year, month);
|
||||
(0, respond_1.ok)(res, events);
|
||||
}
|
||||
catch (err) {
|
||||
next(err);
|
||||
}
|
||||
});
|
||||
router.post('/:id/calendar/blocks', (0, requireRole_1.requireRole)('MANAGER'), async (req, res, next) => {
|
||||
try {
|
||||
const { id } = (0, validate_1.parseParams)(vehicle_schemas_1.idParamSchema, req);
|
||||
const body = (0, validate_1.parseBody)(vehicle_schemas_1.calendarBlockSchema, req);
|
||||
const block = await service.createCalendarBlock(id, req.companyId, body);
|
||||
(0, respond_1.created)(res, block);
|
||||
}
|
||||
catch (err) {
|
||||
next(err);
|
||||
}
|
||||
});
|
||||
router.delete('/:id/calendar/blocks/:blockId', (0, requireRole_1.requireRole)('MANAGER'), async (req, res, next) => {
|
||||
try {
|
||||
const { id, blockId } = (0, validate_1.parseParams)(vehicle_schemas_1.blockIdParamSchema, req);
|
||||
await service.deleteCalendarBlock(id, req.companyId, blockId);
|
||||
(0, respond_1.ok)(res, { success: true });
|
||||
}
|
||||
catch (err) {
|
||||
next(err);
|
||||
}
|
||||
});
|
||||
router.get('/:id/maintenance', async (req, res, next) => {
|
||||
try {
|
||||
const { id } = (0, validate_1.parseParams)(vehicle_schemas_1.idParamSchema, req);
|
||||
const logs = await service.getMaintenanceLogs(id, req.companyId);
|
||||
(0, respond_1.ok)(res, logs);
|
||||
}
|
||||
catch (err) {
|
||||
next(err);
|
||||
}
|
||||
});
|
||||
router.post('/:id/maintenance', (0, requireRole_1.requireRole)('MANAGER'), async (req, res, next) => {
|
||||
try {
|
||||
const { id } = (0, validate_1.parseParams)(vehicle_schemas_1.idParamSchema, req);
|
||||
const body = (0, validate_1.parseBody)(vehicle_schemas_1.maintenanceLogSchema, req);
|
||||
const log = await service.createMaintenanceLog(id, req.companyId, body);
|
||||
(0, respond_1.created)(res, log);
|
||||
}
|
||||
catch (err) {
|
||||
next(err);
|
||||
}
|
||||
});
|
||||
router.get('/:id/pricing', async (req, res, next) => {
|
||||
try {
|
||||
const { id } = (0, validate_1.parseParams)(vehicle_schemas_1.idParamSchema, req);
|
||||
(0, respond_1.ok)(res, await service.getVehiclePricing(id, req.companyId));
|
||||
}
|
||||
catch (err) {
|
||||
next(err);
|
||||
}
|
||||
});
|
||||
router.put('/:id/pricing', (0, requireRole_1.requireRole)('MANAGER'), async (req, res, next) => {
|
||||
try {
|
||||
const { id } = (0, validate_1.parseParams)(vehicle_schemas_1.idParamSchema, req);
|
||||
const body = (0, validate_1.parseBody)(vehicle_schemas_1.pricingConfigSchema, req);
|
||||
(0, respond_1.ok)(res, await service.updateVehiclePricing(id, req.companyId, req.employee.id, body));
|
||||
}
|
||||
catch (err) {
|
||||
next(err);
|
||||
}
|
||||
});
|
||||
router.post('/:id/pricing/rules', (0, requireRole_1.requireRole)('MANAGER'), async (req, res, next) => {
|
||||
try {
|
||||
const { id } = (0, validate_1.parseParams)(vehicle_schemas_1.idParamSchema, req);
|
||||
const body = (0, validate_1.parseBody)(vehicle_schemas_1.pricingRuleSchema, req);
|
||||
(0, respond_1.created)(res, await service.createVehiclePricingRule(id, req.companyId, req.employee.id, body));
|
||||
}
|
||||
catch (err) {
|
||||
next(err);
|
||||
}
|
||||
});
|
||||
router.patch('/:id/pricing/rules/:ruleId', (0, requireRole_1.requireRole)('MANAGER'), async (req, res, next) => {
|
||||
try {
|
||||
const { id, ruleId } = (0, validate_1.parseParams)(vehicle_schemas_1.pricingRuleParamSchema, req);
|
||||
const body = (0, validate_1.parseBody)(vehicle_schemas_1.pricingRuleSchema.partial(), req);
|
||||
(0, respond_1.ok)(res, await service.updateVehiclePricingRule(id, req.companyId, ruleId, req.employee.id, body));
|
||||
}
|
||||
catch (err) {
|
||||
next(err);
|
||||
}
|
||||
});
|
||||
router.delete('/:id/pricing/rules/:ruleId', (0, requireRole_1.requireRole)('MANAGER'), async (req, res, next) => {
|
||||
try {
|
||||
const { id, ruleId } = (0, validate_1.parseParams)(vehicle_schemas_1.pricingRuleParamSchema, req);
|
||||
(0, respond_1.ok)(res, await service.deleteVehiclePricingRule(id, req.companyId, ruleId, req.employee.id));
|
||||
}
|
||||
catch (err) {
|
||||
next(err);
|
||||
}
|
||||
});
|
||||
exports.default = router;
|
||||
//# sourceMappingURL=vehicle.routes.js.map
|
||||
File diff suppressed because one or more lines are too long
+283
@@ -0,0 +1,283 @@
|
||||
import { z } from 'zod';
|
||||
export declare const vehicleSchema: z.ZodObject<{
|
||||
make: z.ZodString;
|
||||
model: z.ZodString;
|
||||
year: z.ZodNumber;
|
||||
color: z.ZodDefault<z.ZodString>;
|
||||
licensePlate: z.ZodString;
|
||||
vin: z.ZodOptional<z.ZodString>;
|
||||
category: z.ZodEnum<["ECONOMY", "COMPACT", "MIDSIZE", "FULLSIZE", "SUV", "LUXURY", "VAN", "TRUCK"]>;
|
||||
seats: z.ZodDefault<z.ZodNumber>;
|
||||
transmission: z.ZodDefault<z.ZodEnum<["AUTOMATIC", "MANUAL"]>>;
|
||||
fuelType: z.ZodDefault<z.ZodEnum<["GASOLINE", "DIESEL", "ELECTRIC", "HYBRID"]>>;
|
||||
features: z.ZodDefault<z.ZodArray<z.ZodString, "many">>;
|
||||
dailyRate: z.ZodOptional<z.ZodNumber>;
|
||||
mileage: z.ZodOptional<z.ZodNumber>;
|
||||
notes: z.ZodOptional<z.ZodString>;
|
||||
isPublished: z.ZodDefault<z.ZodBoolean>;
|
||||
status: z.ZodOptional<z.ZodEnum<["AVAILABLE", "RESERVED", "READY", "RENTED", "RETURNED", "NEEDS_CLEANING", "MAINTENANCE", "DAMAGE_REVIEW", "OUT_OF_SERVICE"]>>;
|
||||
pickupLocations: z.ZodDefault<z.ZodArray<z.ZodString, "many">>;
|
||||
allowDifferentDropoff: z.ZodDefault<z.ZodBoolean>;
|
||||
dropoffLocations: z.ZodDefault<z.ZodArray<z.ZodString, "many">>;
|
||||
}, "strip", z.ZodTypeAny, {
|
||||
make: string;
|
||||
model: string;
|
||||
year: number;
|
||||
color: string;
|
||||
licensePlate: string;
|
||||
category: "ECONOMY" | "COMPACT" | "MIDSIZE" | "FULLSIZE" | "SUV" | "LUXURY" | "VAN" | "TRUCK";
|
||||
seats: number;
|
||||
transmission: "AUTOMATIC" | "MANUAL";
|
||||
fuelType: "GASOLINE" | "DIESEL" | "ELECTRIC" | "HYBRID";
|
||||
features: string[];
|
||||
isPublished: boolean;
|
||||
pickupLocations: string[];
|
||||
allowDifferentDropoff: boolean;
|
||||
dropoffLocations: string[];
|
||||
vin?: string | undefined;
|
||||
status?: "AVAILABLE" | "RESERVED" | "READY" | "RENTED" | "RETURNED" | "NEEDS_CLEANING" | "MAINTENANCE" | "DAMAGE_REVIEW" | "OUT_OF_SERVICE" | undefined;
|
||||
dailyRate?: number | undefined;
|
||||
mileage?: number | undefined;
|
||||
notes?: string | undefined;
|
||||
}, {
|
||||
make: string;
|
||||
model: string;
|
||||
year: number;
|
||||
licensePlate: string;
|
||||
category: "ECONOMY" | "COMPACT" | "MIDSIZE" | "FULLSIZE" | "SUV" | "LUXURY" | "VAN" | "TRUCK";
|
||||
color?: string | undefined;
|
||||
vin?: string | undefined;
|
||||
status?: "AVAILABLE" | "RESERVED" | "READY" | "RENTED" | "RETURNED" | "NEEDS_CLEANING" | "MAINTENANCE" | "DAMAGE_REVIEW" | "OUT_OF_SERVICE" | undefined;
|
||||
seats?: number | undefined;
|
||||
transmission?: "AUTOMATIC" | "MANUAL" | undefined;
|
||||
fuelType?: "GASOLINE" | "DIESEL" | "ELECTRIC" | "HYBRID" | undefined;
|
||||
features?: string[] | undefined;
|
||||
dailyRate?: number | undefined;
|
||||
mileage?: number | undefined;
|
||||
notes?: string | undefined;
|
||||
isPublished?: boolean | undefined;
|
||||
pickupLocations?: string[] | undefined;
|
||||
allowDifferentDropoff?: boolean | undefined;
|
||||
dropoffLocations?: string[] | undefined;
|
||||
}>;
|
||||
export declare const listQuerySchema: z.ZodObject<{
|
||||
status: z.ZodOptional<z.ZodString>;
|
||||
category: z.ZodOptional<z.ZodString>;
|
||||
published: z.ZodOptional<z.ZodString>;
|
||||
page: z.ZodDefault<z.ZodNumber>;
|
||||
pageSize: z.ZodDefault<z.ZodNumber>;
|
||||
}, "strip", z.ZodTypeAny, {
|
||||
page: number;
|
||||
pageSize: number;
|
||||
category?: string | undefined;
|
||||
status?: string | undefined;
|
||||
published?: string | undefined;
|
||||
}, {
|
||||
category?: string | undefined;
|
||||
status?: string | undefined;
|
||||
published?: string | undefined;
|
||||
page?: number | undefined;
|
||||
pageSize?: number | undefined;
|
||||
}>;
|
||||
export declare const availabilityQuerySchema: z.ZodObject<{
|
||||
startDate: z.ZodString;
|
||||
endDate: z.ZodString;
|
||||
}, "strip", z.ZodTypeAny, {
|
||||
startDate: string;
|
||||
endDate: string;
|
||||
}, {
|
||||
startDate: string;
|
||||
endDate: string;
|
||||
}>;
|
||||
export declare const calendarQuerySchema: z.ZodObject<{
|
||||
year: z.ZodNumber;
|
||||
month: z.ZodNumber;
|
||||
}, "strip", z.ZodTypeAny, {
|
||||
year: number;
|
||||
month: number;
|
||||
}, {
|
||||
year: number;
|
||||
month: number;
|
||||
}>;
|
||||
export declare const calendarBlockSchema: z.ZodObject<{
|
||||
startDate: z.ZodUnion<[z.ZodString, z.ZodString]>;
|
||||
endDate: z.ZodUnion<[z.ZodString, z.ZodString]>;
|
||||
reason: z.ZodOptional<z.ZodString>;
|
||||
type: z.ZodDefault<z.ZodEnum<["MANUAL", "MAINTENANCE"]>>;
|
||||
}, "strip", z.ZodTypeAny, {
|
||||
type: "MANUAL" | "MAINTENANCE";
|
||||
startDate: string;
|
||||
endDate: string;
|
||||
reason?: string | undefined;
|
||||
}, {
|
||||
startDate: string;
|
||||
endDate: string;
|
||||
type?: "MANUAL" | "MAINTENANCE" | undefined;
|
||||
reason?: string | undefined;
|
||||
}>;
|
||||
export declare const maintenanceLogSchema: z.ZodObject<{
|
||||
type: z.ZodString;
|
||||
description: z.ZodOptional<z.ZodString>;
|
||||
cost: z.ZodOptional<z.ZodNumber>;
|
||||
mileage: z.ZodOptional<z.ZodNumber>;
|
||||
performedAt: z.ZodString;
|
||||
nextDueAt: z.ZodOptional<z.ZodString>;
|
||||
nextDueMileage: z.ZodOptional<z.ZodNumber>;
|
||||
}, "strip", z.ZodTypeAny, {
|
||||
type: string;
|
||||
performedAt: string;
|
||||
mileage?: number | undefined;
|
||||
description?: string | undefined;
|
||||
cost?: number | undefined;
|
||||
nextDueAt?: string | undefined;
|
||||
nextDueMileage?: number | undefined;
|
||||
}, {
|
||||
type: string;
|
||||
performedAt: string;
|
||||
mileage?: number | undefined;
|
||||
description?: string | undefined;
|
||||
cost?: number | undefined;
|
||||
nextDueAt?: string | undefined;
|
||||
nextDueMileage?: number | undefined;
|
||||
}>;
|
||||
export declare const pricingConfigSchema: z.ZodObject<{
|
||||
pricingMode: z.ZodEnum<["MANUAL", "AUTOMATIC"]>;
|
||||
baseDailyRate: z.ZodNumber;
|
||||
weeklyRate: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
|
||||
weekendRate: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
|
||||
holidayRate: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
|
||||
monthlyRate: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
|
||||
longTermDailyRate: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
|
||||
minimumDailyRate: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
|
||||
maximumDailyRate: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
|
||||
maxDailyPriceMovementPct: z.ZodDefault<z.ZodNumber>;
|
||||
automaticPricingEnabled: z.ZodDefault<z.ZodBoolean>;
|
||||
approvalRequired: z.ZodDefault<z.ZodBoolean>;
|
||||
note: z.ZodOptional<z.ZodString>;
|
||||
}, "strip", z.ZodTypeAny, {
|
||||
pricingMode: "AUTOMATIC" | "MANUAL";
|
||||
baseDailyRate: number;
|
||||
maxDailyPriceMovementPct: number;
|
||||
automaticPricingEnabled: boolean;
|
||||
approvalRequired: boolean;
|
||||
weeklyRate?: number | null | undefined;
|
||||
weekendRate?: number | null | undefined;
|
||||
holidayRate?: number | null | undefined;
|
||||
monthlyRate?: number | null | undefined;
|
||||
longTermDailyRate?: number | null | undefined;
|
||||
minimumDailyRate?: number | null | undefined;
|
||||
maximumDailyRate?: number | null | undefined;
|
||||
note?: string | undefined;
|
||||
}, {
|
||||
pricingMode: "AUTOMATIC" | "MANUAL";
|
||||
baseDailyRate: number;
|
||||
weeklyRate?: number | null | undefined;
|
||||
weekendRate?: number | null | undefined;
|
||||
holidayRate?: number | null | undefined;
|
||||
monthlyRate?: number | null | undefined;
|
||||
longTermDailyRate?: number | null | undefined;
|
||||
minimumDailyRate?: number | null | undefined;
|
||||
maximumDailyRate?: number | null | undefined;
|
||||
maxDailyPriceMovementPct?: number | undefined;
|
||||
automaticPricingEnabled?: boolean | undefined;
|
||||
approvalRequired?: boolean | undefined;
|
||||
note?: string | undefined;
|
||||
}>;
|
||||
export declare const pricingRuleSchema: z.ZodObject<{
|
||||
name: z.ZodString;
|
||||
ruleType: z.ZodDefault<z.ZodEnum<["DATE_RANGE", "SEASONAL", "HOLIDAY", "WEEKEND", "SPECIAL_EVENT", "MANUAL_OVERRIDE"]>>;
|
||||
startDate: z.ZodUnion<[z.ZodString, z.ZodString]>;
|
||||
endDate: z.ZodUnion<[z.ZodString, z.ZodString]>;
|
||||
dailyRate: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
|
||||
weeklyRate: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
|
||||
weekendRate: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
|
||||
monthlyRate: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
|
||||
minDailyRate: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
|
||||
maxDailyRate: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
|
||||
automaticAdjustmentPct: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
|
||||
priceAdjustment: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
|
||||
isActive: z.ZodDefault<z.ZodBoolean>;
|
||||
sortOrder: z.ZodDefault<z.ZodNumber>;
|
||||
}, "strip", z.ZodTypeAny, {
|
||||
startDate: string;
|
||||
endDate: string;
|
||||
name: string;
|
||||
ruleType: "DATE_RANGE" | "SEASONAL" | "HOLIDAY" | "WEEKEND" | "SPECIAL_EVENT" | "MANUAL_OVERRIDE";
|
||||
isActive: boolean;
|
||||
sortOrder: number;
|
||||
dailyRate?: number | null | undefined;
|
||||
weeklyRate?: number | null | undefined;
|
||||
weekendRate?: number | null | undefined;
|
||||
monthlyRate?: number | null | undefined;
|
||||
minDailyRate?: number | null | undefined;
|
||||
maxDailyRate?: number | null | undefined;
|
||||
automaticAdjustmentPct?: number | null | undefined;
|
||||
priceAdjustment?: number | null | undefined;
|
||||
}, {
|
||||
startDate: string;
|
||||
endDate: string;
|
||||
name: string;
|
||||
dailyRate?: number | null | undefined;
|
||||
weeklyRate?: number | null | undefined;
|
||||
weekendRate?: number | null | undefined;
|
||||
monthlyRate?: number | null | undefined;
|
||||
ruleType?: "DATE_RANGE" | "SEASONAL" | "HOLIDAY" | "WEEKEND" | "SPECIAL_EVENT" | "MANUAL_OVERRIDE" | undefined;
|
||||
minDailyRate?: number | null | undefined;
|
||||
maxDailyRate?: number | null | undefined;
|
||||
automaticAdjustmentPct?: number | null | undefined;
|
||||
priceAdjustment?: number | null | undefined;
|
||||
isActive?: boolean | undefined;
|
||||
sortOrder?: number | undefined;
|
||||
}>;
|
||||
export declare const publishSchema: z.ZodObject<{
|
||||
isPublished: z.ZodBoolean;
|
||||
}, "strip", z.ZodTypeAny, {
|
||||
isPublished: boolean;
|
||||
}, {
|
||||
isPublished: boolean;
|
||||
}>;
|
||||
export declare const statusSchema: z.ZodObject<{
|
||||
status: z.ZodEnum<["AVAILABLE", "RESERVED", "READY", "RENTED", "RETURNED", "NEEDS_CLEANING", "MAINTENANCE", "DAMAGE_REVIEW", "OUT_OF_SERVICE"]>;
|
||||
}, "strip", z.ZodTypeAny, {
|
||||
status: "AVAILABLE" | "RESERVED" | "READY" | "RENTED" | "RETURNED" | "NEEDS_CLEANING" | "MAINTENANCE" | "DAMAGE_REVIEW" | "OUT_OF_SERVICE";
|
||||
}, {
|
||||
status: "AVAILABLE" | "RESERVED" | "READY" | "RENTED" | "RETURNED" | "NEEDS_CLEANING" | "MAINTENANCE" | "DAMAGE_REVIEW" | "OUT_OF_SERVICE";
|
||||
}>;
|
||||
export declare const idParamSchema: z.ZodObject<{
|
||||
id: z.ZodString;
|
||||
}, "strip", z.ZodTypeAny, {
|
||||
id: string;
|
||||
}, {
|
||||
id: string;
|
||||
}>;
|
||||
export declare const photoIdxSchema: z.ZodObject<{
|
||||
id: z.ZodString;
|
||||
idx: z.ZodString;
|
||||
}, "strip", z.ZodTypeAny, {
|
||||
id: string;
|
||||
idx: string;
|
||||
}, {
|
||||
id: string;
|
||||
idx: string;
|
||||
}>;
|
||||
export declare const blockIdParamSchema: z.ZodObject<{
|
||||
id: z.ZodString;
|
||||
blockId: z.ZodString;
|
||||
}, "strip", z.ZodTypeAny, {
|
||||
id: string;
|
||||
blockId: string;
|
||||
}, {
|
||||
id: string;
|
||||
blockId: string;
|
||||
}>;
|
||||
export declare const pricingRuleParamSchema: z.ZodObject<{
|
||||
id: z.ZodString;
|
||||
ruleId: z.ZodString;
|
||||
}, "strip", z.ZodTypeAny, {
|
||||
id: string;
|
||||
ruleId: string;
|
||||
}, {
|
||||
id: string;
|
||||
ruleId: string;
|
||||
}>;
|
||||
//# sourceMappingURL=vehicle.schemas.d.ts.map
|
||||
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"vehicle.schemas.d.ts","sourceRoot":"","sources":["../../../src/modules/vehicles/vehicle.schemas.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAEvB,eAAO,MAAM,aAAa;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAoBxB,CAAA;AAEF,eAAO,MAAM,eAAe;;;;;;;;;;;;;;;;;;EAM1B,CAAA;AAEF,eAAO,MAAM,uBAAuB;;;;;;;;;EAGlC,CAAA;AAEF,eAAO,MAAM,mBAAmB;;;;;;;;;EAG9B,CAAA;AAEF,eAAO,MAAM,mBAAmB;;;;;;;;;;;;;;;EAK9B,CAAA;AAEF,eAAO,MAAM,oBAAoB;;;;;;;;;;;;;;;;;;;;;;;;EAQ/B,CAAA;AAIF,eAAO,MAAM,mBAAmB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAc9B,CAAA;AAEF,eAAO,MAAM,iBAAiB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAe5B,CAAA;AAEF,eAAO,MAAM,aAAa;;;;;;EAAyC,CAAA;AACnE,eAAO,MAAM,YAAY;;;;;;EAA8J,CAAA;AAEvL,eAAO,MAAM,aAAa;;;;;;EAAkC,CAAA;AAC5D,eAAO,MAAM,cAAc;;;;;;;;;EAAkD,CAAA;AAC7E,eAAO,MAAM,kBAAkB;;;;;;;;;EAAoD,CAAA;AACnF,eAAO,MAAM,sBAAsB;;;;;;;;;EAAmD,CAAA"}
|
||||
@@ -0,0 +1,94 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.pricingRuleParamSchema = exports.blockIdParamSchema = exports.photoIdxSchema = exports.idParamSchema = exports.statusSchema = exports.publishSchema = exports.pricingRuleSchema = exports.pricingConfigSchema = exports.maintenanceLogSchema = exports.calendarBlockSchema = exports.calendarQuerySchema = exports.availabilityQuerySchema = exports.listQuerySchema = exports.vehicleSchema = void 0;
|
||||
const zod_1 = require("zod");
|
||||
exports.vehicleSchema = zod_1.z.object({
|
||||
make: zod_1.z.string().min(1),
|
||||
model: zod_1.z.string().min(1),
|
||||
year: zod_1.z.number().int().min(1990).max(new Date().getFullYear() + 1),
|
||||
color: zod_1.z.string().default(''),
|
||||
licensePlate: zod_1.z.string().min(1),
|
||||
vin: zod_1.z.string().optional(),
|
||||
category: zod_1.z.enum(['ECONOMY', 'COMPACT', 'MIDSIZE', 'FULLSIZE', 'SUV', 'LUXURY', 'VAN', 'TRUCK']),
|
||||
seats: zod_1.z.number().int().min(1).max(20).default(5),
|
||||
transmission: zod_1.z.enum(['AUTOMATIC', 'MANUAL']).default('AUTOMATIC'),
|
||||
fuelType: zod_1.z.enum(['GASOLINE', 'DIESEL', 'ELECTRIC', 'HYBRID']).default('GASOLINE'),
|
||||
features: zod_1.z.array(zod_1.z.string()).default([]),
|
||||
dailyRate: zod_1.z.number().int().min(0).optional(),
|
||||
mileage: zod_1.z.number().int().optional(),
|
||||
notes: zod_1.z.string().optional(),
|
||||
isPublished: zod_1.z.boolean().default(true),
|
||||
status: zod_1.z.enum(['AVAILABLE', 'RESERVED', 'READY', 'RENTED', 'RETURNED', 'NEEDS_CLEANING', 'MAINTENANCE', 'DAMAGE_REVIEW', 'OUT_OF_SERVICE']).optional(),
|
||||
pickupLocations: zod_1.z.array(zod_1.z.string().trim().min(1)).default([]),
|
||||
allowDifferentDropoff: zod_1.z.boolean().default(false),
|
||||
dropoffLocations: zod_1.z.array(zod_1.z.string().trim().min(1)).default([]),
|
||||
});
|
||||
exports.listQuerySchema = zod_1.z.object({
|
||||
status: zod_1.z.string().optional(),
|
||||
category: zod_1.z.string().optional(),
|
||||
published: zod_1.z.string().optional(),
|
||||
page: zod_1.z.coerce.number().int().min(1).default(1),
|
||||
pageSize: zod_1.z.coerce.number().int().min(1).max(100).default(20),
|
||||
});
|
||||
exports.availabilityQuerySchema = zod_1.z.object({
|
||||
startDate: zod_1.z.string(),
|
||||
endDate: zod_1.z.string(),
|
||||
});
|
||||
exports.calendarQuerySchema = zod_1.z.object({
|
||||
year: zod_1.z.coerce.number().int(),
|
||||
month: zod_1.z.coerce.number().int().min(1).max(12),
|
||||
});
|
||||
exports.calendarBlockSchema = zod_1.z.object({
|
||||
startDate: zod_1.z.string().datetime({ offset: true }).or(zod_1.z.string().regex(/^\d{4}-\d{2}-\d{2}$/)),
|
||||
endDate: zod_1.z.string().datetime({ offset: true }).or(zod_1.z.string().regex(/^\d{4}-\d{2}-\d{2}$/)),
|
||||
reason: zod_1.z.string().optional(),
|
||||
type: zod_1.z.enum(['MANUAL', 'MAINTENANCE']).default('MANUAL'),
|
||||
});
|
||||
exports.maintenanceLogSchema = zod_1.z.object({
|
||||
type: zod_1.z.string().min(1),
|
||||
description: zod_1.z.string().optional(),
|
||||
cost: zod_1.z.number().int().optional(),
|
||||
mileage: zod_1.z.number().int().optional(),
|
||||
performedAt: zod_1.z.string().datetime(),
|
||||
nextDueAt: zod_1.z.string().datetime().optional(),
|
||||
nextDueMileage: zod_1.z.number().int().optional(),
|
||||
});
|
||||
const nullableRateSchema = zod_1.z.number().int().min(0).nullable().optional();
|
||||
exports.pricingConfigSchema = zod_1.z.object({
|
||||
pricingMode: zod_1.z.enum(['MANUAL', 'AUTOMATIC']),
|
||||
baseDailyRate: zod_1.z.number().int().min(0),
|
||||
weeklyRate: nullableRateSchema,
|
||||
weekendRate: nullableRateSchema,
|
||||
holidayRate: nullableRateSchema,
|
||||
monthlyRate: nullableRateSchema,
|
||||
longTermDailyRate: nullableRateSchema,
|
||||
minimumDailyRate: nullableRateSchema,
|
||||
maximumDailyRate: nullableRateSchema,
|
||||
maxDailyPriceMovementPct: zod_1.z.number().int().min(0).max(100).default(10),
|
||||
automaticPricingEnabled: zod_1.z.boolean().default(false),
|
||||
approvalRequired: zod_1.z.boolean().default(false),
|
||||
note: zod_1.z.string().trim().min(1).max(500).optional(),
|
||||
});
|
||||
exports.pricingRuleSchema = zod_1.z.object({
|
||||
name: zod_1.z.string().trim().min(1).max(120),
|
||||
ruleType: zod_1.z.enum(['DATE_RANGE', 'SEASONAL', 'HOLIDAY', 'WEEKEND', 'SPECIAL_EVENT', 'MANUAL_OVERRIDE']).default('DATE_RANGE'),
|
||||
startDate: zod_1.z.string().datetime({ offset: true }).or(zod_1.z.string().regex(/^\d{4}-\d{2}-\d{2}$/)),
|
||||
endDate: zod_1.z.string().datetime({ offset: true }).or(zod_1.z.string().regex(/^\d{4}-\d{2}-\d{2}$/)),
|
||||
dailyRate: nullableRateSchema,
|
||||
weeklyRate: nullableRateSchema,
|
||||
weekendRate: nullableRateSchema,
|
||||
monthlyRate: nullableRateSchema,
|
||||
minDailyRate: nullableRateSchema,
|
||||
maxDailyRate: nullableRateSchema,
|
||||
automaticAdjustmentPct: zod_1.z.number().int().min(-100).max(200).nullable().optional(),
|
||||
priceAdjustment: zod_1.z.number().int().nullable().optional(),
|
||||
isActive: zod_1.z.boolean().default(true),
|
||||
sortOrder: zod_1.z.number().int().min(0).max(1000).default(0),
|
||||
});
|
||||
exports.publishSchema = zod_1.z.object({ isPublished: zod_1.z.boolean() });
|
||||
exports.statusSchema = zod_1.z.object({ status: zod_1.z.enum(['AVAILABLE', 'RESERVED', 'READY', 'RENTED', 'RETURNED', 'NEEDS_CLEANING', 'MAINTENANCE', 'DAMAGE_REVIEW', 'OUT_OF_SERVICE']) });
|
||||
exports.idParamSchema = zod_1.z.object({ id: zod_1.z.string() });
|
||||
exports.photoIdxSchema = zod_1.z.object({ id: zod_1.z.string(), idx: zod_1.z.string() });
|
||||
exports.blockIdParamSchema = zod_1.z.object({ id: zod_1.z.string(), blockId: zod_1.z.string() });
|
||||
exports.pricingRuleParamSchema = zod_1.z.object({ id: zod_1.z.string(), ruleId: zod_1.z.string() });
|
||||
//# sourceMappingURL=vehicle.schemas.js.map
|
||||
File diff suppressed because one or more lines are too long
+217
@@ -0,0 +1,217 @@
|
||||
export declare function listVehicles(companyId: string, query: {
|
||||
status?: string;
|
||||
category?: string;
|
||||
published?: string;
|
||||
page?: number;
|
||||
pageSize?: number;
|
||||
}): Promise<{
|
||||
total: number;
|
||||
page: number;
|
||||
pageSize: number;
|
||||
totalPages: number;
|
||||
data: any[];
|
||||
}>;
|
||||
export declare function getVehicle(id: string, companyId: string): Promise<any>;
|
||||
export declare function createVehicle(data: any, companyId: string): Promise<any>;
|
||||
export declare function updateVehicle(id: string, companyId: string, data: any): Promise<any>;
|
||||
export declare function setStatus(id: string, companyId: string, status: string): Promise<any>;
|
||||
export declare function deleteVehicle(id: string, companyId: string): Promise<any>;
|
||||
export declare function uploadPhotos(id: string, companyId: string, files: Express.Multer.File[]): Promise<any>;
|
||||
export declare function deletePhoto(id: string, companyId: string, idx: number): Promise<any>;
|
||||
export declare function setPublished(id: string, companyId: string, isPublished: boolean): Promise<void>;
|
||||
export declare function checkAvailability(id: string, companyId: string, startDate: string, endDate: string): Promise<{
|
||||
available: boolean;
|
||||
conflicts: any;
|
||||
blocks: any;
|
||||
}>;
|
||||
export declare function getCalendar(id: string, companyId: string, year: number, month: number): Promise<any[]>;
|
||||
export declare function createCalendarBlock(id: string, companyId: string, data: {
|
||||
startDate: string;
|
||||
endDate: string;
|
||||
reason?: string;
|
||||
type?: 'MANUAL' | 'MAINTENANCE';
|
||||
}): Promise<any>;
|
||||
export declare function deleteCalendarBlock(vehicleId: string, companyId: string, blockId: string): Promise<void>;
|
||||
export declare function getMaintenanceLogs(id: string, companyId: string): Promise<any>;
|
||||
export declare function createMaintenanceLog(id: string, companyId: string, data: any): Promise<any>;
|
||||
export declare function getVehiclePricing(id: string, companyId: string): Promise<{
|
||||
configuration: {
|
||||
id: any;
|
||||
vehicleId: any;
|
||||
pricingMode: any;
|
||||
baseDailyRate: any;
|
||||
weeklyRate: any;
|
||||
weekendRate: any;
|
||||
holidayRate: any;
|
||||
monthlyRate: any;
|
||||
longTermDailyRate: any;
|
||||
minimumDailyRate: any;
|
||||
maximumDailyRate: any;
|
||||
maxDailyPriceMovementPct: any;
|
||||
automaticPricingEnabled: any;
|
||||
approvalRequired: any;
|
||||
createdAt: any;
|
||||
updatedAt: any;
|
||||
};
|
||||
rules: any;
|
||||
history: any;
|
||||
preview: {
|
||||
today: {
|
||||
date: string;
|
||||
dailyRate: any;
|
||||
pricingType: string;
|
||||
matchedRuleName: any;
|
||||
} | undefined;
|
||||
next14Days: {
|
||||
date: string;
|
||||
dailyRate: any;
|
||||
pricingType: string;
|
||||
matchedRuleName: any;
|
||||
}[];
|
||||
};
|
||||
}>;
|
||||
export declare function updateVehiclePricing(id: string, companyId: string, employeeId: string, data: any): Promise<{
|
||||
configuration: {
|
||||
id: any;
|
||||
vehicleId: any;
|
||||
pricingMode: any;
|
||||
baseDailyRate: any;
|
||||
weeklyRate: any;
|
||||
weekendRate: any;
|
||||
holidayRate: any;
|
||||
monthlyRate: any;
|
||||
longTermDailyRate: any;
|
||||
minimumDailyRate: any;
|
||||
maximumDailyRate: any;
|
||||
maxDailyPriceMovementPct: any;
|
||||
automaticPricingEnabled: any;
|
||||
approvalRequired: any;
|
||||
createdAt: any;
|
||||
updatedAt: any;
|
||||
};
|
||||
rules: any;
|
||||
history: any;
|
||||
preview: {
|
||||
today: {
|
||||
date: string;
|
||||
dailyRate: any;
|
||||
pricingType: string;
|
||||
matchedRuleName: any;
|
||||
} | undefined;
|
||||
next14Days: {
|
||||
date: string;
|
||||
dailyRate: any;
|
||||
pricingType: string;
|
||||
matchedRuleName: any;
|
||||
}[];
|
||||
};
|
||||
}>;
|
||||
export declare function createVehiclePricingRule(id: string, companyId: string, employeeId: string, data: any): Promise<{
|
||||
configuration: {
|
||||
id: any;
|
||||
vehicleId: any;
|
||||
pricingMode: any;
|
||||
baseDailyRate: any;
|
||||
weeklyRate: any;
|
||||
weekendRate: any;
|
||||
holidayRate: any;
|
||||
monthlyRate: any;
|
||||
longTermDailyRate: any;
|
||||
minimumDailyRate: any;
|
||||
maximumDailyRate: any;
|
||||
maxDailyPriceMovementPct: any;
|
||||
automaticPricingEnabled: any;
|
||||
approvalRequired: any;
|
||||
createdAt: any;
|
||||
updatedAt: any;
|
||||
};
|
||||
rules: any;
|
||||
history: any;
|
||||
preview: {
|
||||
today: {
|
||||
date: string;
|
||||
dailyRate: any;
|
||||
pricingType: string;
|
||||
matchedRuleName: any;
|
||||
} | undefined;
|
||||
next14Days: {
|
||||
date: string;
|
||||
dailyRate: any;
|
||||
pricingType: string;
|
||||
matchedRuleName: any;
|
||||
}[];
|
||||
};
|
||||
}>;
|
||||
export declare function updateVehiclePricingRule(id: string, companyId: string, ruleId: string, employeeId: string, data: any): Promise<{
|
||||
configuration: {
|
||||
id: any;
|
||||
vehicleId: any;
|
||||
pricingMode: any;
|
||||
baseDailyRate: any;
|
||||
weeklyRate: any;
|
||||
weekendRate: any;
|
||||
holidayRate: any;
|
||||
monthlyRate: any;
|
||||
longTermDailyRate: any;
|
||||
minimumDailyRate: any;
|
||||
maximumDailyRate: any;
|
||||
maxDailyPriceMovementPct: any;
|
||||
automaticPricingEnabled: any;
|
||||
approvalRequired: any;
|
||||
createdAt: any;
|
||||
updatedAt: any;
|
||||
};
|
||||
rules: any;
|
||||
history: any;
|
||||
preview: {
|
||||
today: {
|
||||
date: string;
|
||||
dailyRate: any;
|
||||
pricingType: string;
|
||||
matchedRuleName: any;
|
||||
} | undefined;
|
||||
next14Days: {
|
||||
date: string;
|
||||
dailyRate: any;
|
||||
pricingType: string;
|
||||
matchedRuleName: any;
|
||||
}[];
|
||||
};
|
||||
}>;
|
||||
export declare function deleteVehiclePricingRule(id: string, companyId: string, ruleId: string, employeeId: string): Promise<{
|
||||
configuration: {
|
||||
id: any;
|
||||
vehicleId: any;
|
||||
pricingMode: any;
|
||||
baseDailyRate: any;
|
||||
weeklyRate: any;
|
||||
weekendRate: any;
|
||||
holidayRate: any;
|
||||
monthlyRate: any;
|
||||
longTermDailyRate: any;
|
||||
minimumDailyRate: any;
|
||||
maximumDailyRate: any;
|
||||
maxDailyPriceMovementPct: any;
|
||||
automaticPricingEnabled: any;
|
||||
approvalRequired: any;
|
||||
createdAt: any;
|
||||
updatedAt: any;
|
||||
};
|
||||
rules: any;
|
||||
history: any;
|
||||
preview: {
|
||||
today: {
|
||||
date: string;
|
||||
dailyRate: any;
|
||||
pricingType: string;
|
||||
matchedRuleName: any;
|
||||
} | undefined;
|
||||
next14Days: {
|
||||
date: string;
|
||||
dailyRate: any;
|
||||
pricingType: string;
|
||||
matchedRuleName: any;
|
||||
}[];
|
||||
};
|
||||
}>;
|
||||
//# sourceMappingURL=vehicle.service.d.ts.map
|
||||
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"vehicle.service.d.ts","sourceRoot":"","sources":["../../../src/modules/vehicles/vehicle.service.ts"],"names":[],"mappings":"AA2VA,wBAAsB,YAAY,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE;IAAE,MAAM,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;CAAE;;;;;;GAUxJ;AAED,wBAAsB,UAAU,CAAC,EAAE,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,gBAI7D;AAED,wBAAsB,aAAa,CAAC,IAAI,EAAE,GAAG,EAAE,SAAS,EAAE,MAAM,gBAM/D;AAID,wBAAsB,aAAa,CAAC,EAAE,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,GAAG,gBAY3E;AAED,wBAAsB,SAAS,CAAC,EAAE,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,gBAK5E;AAED,wBAAsB,aAAa,CAAC,EAAE,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,gBAEhE;AAED,wBAAsB,YAAY,CAAC,EAAE,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,CAAC,MAAM,CAAC,IAAI,EAAE,gBAK7F;AAED,wBAAsB,WAAW,CAAC,EAAE,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,gBAK3E;AAED,wBAAsB,YAAY,CAAC,EAAE,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,WAAW,EAAE,OAAO,iBAErF;AAED,wBAAsB,iBAAiB,CAAC,EAAE,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM;;;;GAQxG;AAED,wBAAsB,WAAW,CAAC,EAAE,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,kBAwB3F;AAED,wBAAsB,mBAAmB,CAAC,EAAE,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE;IAAE,SAAS,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,QAAQ,GAAG,aAAa,CAAA;CAAE,gBAOtK;AAED,wBAAsB,mBAAmB,CAAC,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,iBAI9F;AAED,wBAAsB,kBAAkB,CAAC,EAAE,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,gBAIrE;AAED,wBAAsB,oBAAoB,CAAC,EAAE,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,GAAG,gBASlF;AAED,wBAAsB,iBAAiB,CAAC,EAAE,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAGpE;AAED,wBAAsB,oBAAoB,CAAC,EAAE,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,IAAI,EAAE,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiDtG;AAED,wBAAsB,wBAAwB,CAAC,EAAE,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,IAAI,EAAE,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8B1G;AAED,wBAAsB,wBAAwB,CAAC,EAAE,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,IAAI,EAAE,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkC1H;AAED,wBAAsB,wBAAwB,CAAC,EAAE,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2B/G"}
|
||||
+633
@@ -0,0 +1,633 @@
|
||||
"use strict";
|
||||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
||||
if (k2 === undefined) k2 = k;
|
||||
var desc = Object.getOwnPropertyDescriptor(m, k);
|
||||
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
||||
desc = { enumerable: true, get: function() { return m[k]; } };
|
||||
}
|
||||
Object.defineProperty(o, k2, desc);
|
||||
}) : (function(o, m, k, k2) {
|
||||
if (k2 === undefined) k2 = k;
|
||||
o[k2] = m[k];
|
||||
}));
|
||||
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
||||
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
||||
}) : function(o, v) {
|
||||
o["default"] = v;
|
||||
});
|
||||
var __importStar = (this && this.__importStar) || (function () {
|
||||
var ownKeys = function(o) {
|
||||
ownKeys = Object.getOwnPropertyNames || function (o) {
|
||||
var ar = [];
|
||||
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
||||
return ar;
|
||||
};
|
||||
return ownKeys(o);
|
||||
};
|
||||
return function (mod) {
|
||||
if (mod && mod.__esModule) return mod;
|
||||
var result = {};
|
||||
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
||||
__setModuleDefault(result, mod);
|
||||
return result;
|
||||
};
|
||||
})();
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.listVehicles = listVehicles;
|
||||
exports.getVehicle = getVehicle;
|
||||
exports.createVehicle = createVehicle;
|
||||
exports.updateVehicle = updateVehicle;
|
||||
exports.setStatus = setStatus;
|
||||
exports.deleteVehicle = deleteVehicle;
|
||||
exports.uploadPhotos = uploadPhotos;
|
||||
exports.deletePhoto = deletePhoto;
|
||||
exports.setPublished = setPublished;
|
||||
exports.checkAvailability = checkAvailability;
|
||||
exports.getCalendar = getCalendar;
|
||||
exports.createCalendarBlock = createCalendarBlock;
|
||||
exports.deleteCalendarBlock = deleteCalendarBlock;
|
||||
exports.getMaintenanceLogs = getMaintenanceLogs;
|
||||
exports.createMaintenanceLog = createMaintenanceLog;
|
||||
exports.getVehiclePricing = getVehiclePricing;
|
||||
exports.updateVehiclePricing = updateVehiclePricing;
|
||||
exports.createVehiclePricingRule = createVehiclePricingRule;
|
||||
exports.updateVehiclePricingRule = updateVehiclePricingRule;
|
||||
exports.deleteVehiclePricingRule = deleteVehiclePricingRule;
|
||||
const storage_1 = require("../../lib/storage");
|
||||
const errors_1 = require("../../http/errors");
|
||||
const vehicle_presenter_1 = require("./vehicle.presenter");
|
||||
const repo = __importStar(require("./vehicle.repo"));
|
||||
const DAY_MS = 24 * 60 * 60 * 1000;
|
||||
const RULE_PRIORITY = {
|
||||
MANUAL_OVERRIDE: 6,
|
||||
HOLIDAY: 5,
|
||||
SPECIAL_EVENT: 4,
|
||||
SEASONAL: 3,
|
||||
WEEKEND: 2,
|
||||
DATE_RANGE: 1,
|
||||
};
|
||||
function isPricingStorageMissing(error) {
|
||||
if (!error || typeof error !== 'object')
|
||||
return false;
|
||||
const candidate = error;
|
||||
return (candidate.code === 'P2021' ||
|
||||
candidate.message?.includes('vehicle_pricing_configurations') === true ||
|
||||
candidate.message?.includes('vehicle_pricing_rules') === true ||
|
||||
candidate.message?.includes('vehicle_price_history') === true);
|
||||
}
|
||||
function normalizeLocations(locations) {
|
||||
if (!Array.isArray(locations))
|
||||
return [];
|
||||
const seen = new Set();
|
||||
const normalized = [];
|
||||
for (const entry of locations) {
|
||||
if (typeof entry !== 'string')
|
||||
continue;
|
||||
const value = entry.trim();
|
||||
const key = value.toLocaleLowerCase();
|
||||
if (!value || seen.has(key))
|
||||
continue;
|
||||
seen.add(key);
|
||||
normalized.push(value);
|
||||
}
|
||||
return normalized;
|
||||
}
|
||||
function applyLocationSettings(data) {
|
||||
const patch = { ...data };
|
||||
if (patch.pickupLocations !== undefined) {
|
||||
patch.pickupLocations = normalizeLocations(patch.pickupLocations);
|
||||
}
|
||||
if (patch.dropoffLocations !== undefined) {
|
||||
patch.dropoffLocations = normalizeLocations(patch.dropoffLocations);
|
||||
}
|
||||
if (patch.allowDifferentDropoff === false) {
|
||||
patch.dropoffLocations = [];
|
||||
}
|
||||
if (patch.allowDifferentDropoff === true && patch.dropoffLocations !== undefined && patch.dropoffLocations.length === 0) {
|
||||
throw new errors_1.ValidationError('Drop-off locations are required when different drop-off is enabled');
|
||||
}
|
||||
return patch;
|
||||
}
|
||||
function toStartOfDay(value) {
|
||||
const date = new Date(value);
|
||||
date.setHours(0, 0, 0, 0);
|
||||
return date;
|
||||
}
|
||||
function toEndOfDay(value) {
|
||||
const date = new Date(value);
|
||||
date.setHours(23, 59, 59, 999);
|
||||
return date;
|
||||
}
|
||||
function isWeekend(date) {
|
||||
const day = date.getDay();
|
||||
return day === 0 || day === 6;
|
||||
}
|
||||
function clampRate(value, min, max) {
|
||||
if (min != null && value < min)
|
||||
return min;
|
||||
if (max != null && value > max)
|
||||
return max;
|
||||
return value;
|
||||
}
|
||||
function validateRateBounds(minimum, maximum, label = 'Daily') {
|
||||
if (minimum != null && maximum != null && minimum > maximum) {
|
||||
throw new errors_1.ValidationError(`${label} minimum rate must be less than or equal to the maximum rate`);
|
||||
}
|
||||
}
|
||||
function normalizePricingConfigurationInput(data) {
|
||||
const normalized = {
|
||||
pricingMode: data.pricingMode,
|
||||
baseDailyRate: data.baseDailyRate,
|
||||
weeklyRate: data.weeklyRate ?? null,
|
||||
weekendRate: data.weekendRate ?? null,
|
||||
holidayRate: data.holidayRate ?? null,
|
||||
monthlyRate: data.monthlyRate ?? null,
|
||||
longTermDailyRate: data.longTermDailyRate ?? null,
|
||||
minimumDailyRate: data.minimumDailyRate ?? null,
|
||||
maximumDailyRate: data.maximumDailyRate ?? null,
|
||||
maxDailyPriceMovementPct: data.maxDailyPriceMovementPct,
|
||||
automaticPricingEnabled: data.automaticPricingEnabled,
|
||||
approvalRequired: data.approvalRequired,
|
||||
};
|
||||
validateRateBounds(normalized.minimumDailyRate, normalized.maximumDailyRate);
|
||||
validateRateBounds(normalized.baseDailyRate, normalized.maximumDailyRate, 'Base');
|
||||
return normalized;
|
||||
}
|
||||
function normalizePricingRuleInput(data) {
|
||||
const normalized = {
|
||||
name: data.name,
|
||||
ruleType: data.ruleType,
|
||||
startDate: toStartOfDay(data.startDate),
|
||||
endDate: toEndOfDay(data.endDate),
|
||||
dailyRate: data.dailyRate ?? null,
|
||||
weeklyRate: data.weeklyRate ?? null,
|
||||
weekendRate: data.weekendRate ?? null,
|
||||
monthlyRate: data.monthlyRate ?? null,
|
||||
minDailyRate: data.minDailyRate ?? null,
|
||||
maxDailyRate: data.maxDailyRate ?? null,
|
||||
automaticAdjustmentPct: data.automaticAdjustmentPct ?? null,
|
||||
priceAdjustment: data.priceAdjustment ?? null,
|
||||
isActive: data.isActive,
|
||||
sortOrder: data.sortOrder,
|
||||
};
|
||||
if (normalized.endDate < normalized.startDate) {
|
||||
throw new errors_1.ValidationError('Rule end date must be on or after the start date');
|
||||
}
|
||||
validateRateBounds(normalized.minDailyRate, normalized.maxDailyRate);
|
||||
const hasPricingEffect = [
|
||||
normalized.dailyRate,
|
||||
normalized.weeklyRate,
|
||||
normalized.weekendRate,
|
||||
normalized.monthlyRate,
|
||||
normalized.minDailyRate,
|
||||
normalized.maxDailyRate,
|
||||
normalized.automaticAdjustmentPct,
|
||||
normalized.priceAdjustment,
|
||||
].some((value) => value != null);
|
||||
if (!hasPricingEffect) {
|
||||
throw new errors_1.ValidationError('Pricing rules must define at least one rate, bound, or adjustment');
|
||||
}
|
||||
return normalized;
|
||||
}
|
||||
async function ensurePricingConfigurationForVehicle(vehicleId, companyId) {
|
||||
const vehicle = await repo.findById(vehicleId, companyId);
|
||||
if (!vehicle)
|
||||
throw new errors_1.NotFoundError('Vehicle not found');
|
||||
let configuration = null;
|
||||
let persistenceAvailable = true;
|
||||
try {
|
||||
configuration = await repo.findPricingConfiguration(vehicleId);
|
||||
}
|
||||
catch (error) {
|
||||
if (!isPricingStorageMissing(error))
|
||||
throw error;
|
||||
persistenceAvailable = false;
|
||||
}
|
||||
if (!persistenceAvailable) {
|
||||
return {
|
||||
vehicle,
|
||||
persistenceAvailable,
|
||||
configuration: {
|
||||
id: `transient-${vehicle.id}`,
|
||||
vehicleId: vehicle.id,
|
||||
pricingMode: 'MANUAL',
|
||||
baseDailyRate: vehicle.dailyRate,
|
||||
weeklyRate: vehicle.dailyRate * 7,
|
||||
weekendRate: null,
|
||||
holidayRate: null,
|
||||
monthlyRate: null,
|
||||
longTermDailyRate: null,
|
||||
minimumDailyRate: null,
|
||||
maximumDailyRate: null,
|
||||
maxDailyPriceMovementPct: 10,
|
||||
automaticPricingEnabled: false,
|
||||
approvalRequired: false,
|
||||
rules: [],
|
||||
history: [],
|
||||
createdAt: vehicle.createdAt,
|
||||
updatedAt: vehicle.updatedAt,
|
||||
},
|
||||
};
|
||||
}
|
||||
if (!configuration) {
|
||||
configuration = await repo.createPricingConfiguration({
|
||||
vehicleId,
|
||||
pricingMode: 'MANUAL',
|
||||
baseDailyRate: vehicle.dailyRate,
|
||||
weeklyRate: vehicle.dailyRate * 7,
|
||||
automaticPricingEnabled: false,
|
||||
approvalRequired: false,
|
||||
maxDailyPriceMovementPct: 10,
|
||||
});
|
||||
}
|
||||
return { vehicle, configuration, persistenceAvailable };
|
||||
}
|
||||
async function syncPricingBaseRateFromVehicle(vehicleId, previousDailyRate, nextDailyRate) {
|
||||
const configuration = await repo.findPricingConfiguration(vehicleId);
|
||||
if (!configuration)
|
||||
return;
|
||||
const currentBaseRate = configuration.baseDailyRate ?? previousDailyRate;
|
||||
if (currentBaseRate === nextDailyRate)
|
||||
return;
|
||||
await repo.updatePricingConfiguration(configuration.id, { baseDailyRate: nextDailyRate });
|
||||
await repo.createPriceHistory({
|
||||
configurationId: configuration.id,
|
||||
vehicleId,
|
||||
source: 'CONFIG_UPDATE',
|
||||
previousDailyRate: currentBaseRate,
|
||||
nextDailyRate,
|
||||
previousWeeklyRate: configuration.weeklyRate ?? null,
|
||||
nextWeeklyRate: configuration.weeklyRate ?? null,
|
||||
note: 'Base daily rate synced from vehicle details',
|
||||
});
|
||||
}
|
||||
function buildPricePreview(vehicle, configuration) {
|
||||
const baseDailyRate = configuration.baseDailyRate ?? vehicle.dailyRate;
|
||||
const activeRules = (configuration.rules ?? []).filter((rule) => rule.isActive);
|
||||
const items = Array.from({ length: 14 }, (_, index) => {
|
||||
const date = new Date(Date.now() + (index * DAY_MS));
|
||||
date.setHours(12, 0, 0, 0);
|
||||
let dailyRate = baseDailyRate;
|
||||
let pricingType = configuration.pricingMode === 'AUTOMATIC' && configuration.automaticPricingEnabled
|
||||
? 'AUTOMATIC_BASE'
|
||||
: 'MANUAL_BASE';
|
||||
if (isWeekend(date) && configuration.weekendRate != null) {
|
||||
dailyRate = configuration.weekendRate;
|
||||
pricingType = 'WEEKEND_RATE';
|
||||
}
|
||||
const matchingRule = activeRules
|
||||
.filter((rule) => date >= new Date(rule.startDate) && date <= new Date(rule.endDate))
|
||||
.sort((a, b) => {
|
||||
const priorityDiff = (RULE_PRIORITY[b.ruleType] ?? 0) - (RULE_PRIORITY[a.ruleType] ?? 0);
|
||||
if (priorityDiff !== 0)
|
||||
return priorityDiff;
|
||||
if (a.sortOrder !== b.sortOrder)
|
||||
return b.sortOrder - a.sortOrder;
|
||||
return new Date(b.startDate).getTime() - new Date(a.startDate).getTime();
|
||||
})[0];
|
||||
if (matchingRule) {
|
||||
if (matchingRule.dailyRate != null) {
|
||||
dailyRate = matchingRule.dailyRate;
|
||||
}
|
||||
else {
|
||||
if (matchingRule.weekendRate != null && isWeekend(date)) {
|
||||
dailyRate = matchingRule.weekendRate;
|
||||
}
|
||||
if (matchingRule.priceAdjustment != null) {
|
||||
dailyRate += matchingRule.priceAdjustment;
|
||||
}
|
||||
if (matchingRule.automaticAdjustmentPct != null) {
|
||||
dailyRate = Math.round(dailyRate * (100 + matchingRule.automaticAdjustmentPct) / 100);
|
||||
}
|
||||
}
|
||||
pricingType = configuration.pricingMode === 'AUTOMATIC' ? 'AUTOMATIC_RULE' : 'RULE_RATE';
|
||||
dailyRate = clampRate(dailyRate, matchingRule.minDailyRate ?? configuration.minimumDailyRate, matchingRule.maxDailyRate ?? configuration.maximumDailyRate);
|
||||
}
|
||||
else {
|
||||
dailyRate = clampRate(dailyRate, configuration.minimumDailyRate, configuration.maximumDailyRate);
|
||||
}
|
||||
return {
|
||||
date: date.toISOString().slice(0, 10),
|
||||
dailyRate,
|
||||
pricingType,
|
||||
matchedRuleName: matchingRule?.name ?? null,
|
||||
};
|
||||
});
|
||||
return {
|
||||
today: items[0],
|
||||
next14Days: items,
|
||||
};
|
||||
}
|
||||
function presentVehiclePricing(vehicle, configuration) {
|
||||
return {
|
||||
configuration: {
|
||||
id: configuration.id,
|
||||
vehicleId: configuration.vehicleId,
|
||||
pricingMode: configuration.pricingMode,
|
||||
baseDailyRate: configuration.baseDailyRate ?? vehicle.dailyRate,
|
||||
weeklyRate: configuration.weeklyRate ?? null,
|
||||
weekendRate: configuration.weekendRate ?? null,
|
||||
holidayRate: configuration.holidayRate ?? null,
|
||||
monthlyRate: configuration.monthlyRate ?? null,
|
||||
longTermDailyRate: configuration.longTermDailyRate ?? null,
|
||||
minimumDailyRate: configuration.minimumDailyRate ?? null,
|
||||
maximumDailyRate: configuration.maximumDailyRate ?? null,
|
||||
maxDailyPriceMovementPct: configuration.maxDailyPriceMovementPct,
|
||||
automaticPricingEnabled: configuration.automaticPricingEnabled,
|
||||
approvalRequired: configuration.approvalRequired,
|
||||
createdAt: configuration.createdAt,
|
||||
updatedAt: configuration.updatedAt,
|
||||
},
|
||||
rules: (configuration.rules ?? []).map((rule) => ({
|
||||
id: rule.id,
|
||||
name: rule.name,
|
||||
ruleType: rule.ruleType,
|
||||
startDate: rule.startDate,
|
||||
endDate: rule.endDate,
|
||||
dailyRate: rule.dailyRate,
|
||||
weeklyRate: rule.weeklyRate,
|
||||
weekendRate: rule.weekendRate,
|
||||
monthlyRate: rule.monthlyRate,
|
||||
minDailyRate: rule.minDailyRate,
|
||||
maxDailyRate: rule.maxDailyRate,
|
||||
automaticAdjustmentPct: rule.automaticAdjustmentPct,
|
||||
priceAdjustment: rule.priceAdjustment,
|
||||
isActive: rule.isActive,
|
||||
sortOrder: rule.sortOrder,
|
||||
createdAt: rule.createdAt,
|
||||
updatedAt: rule.updatedAt,
|
||||
})),
|
||||
history: (configuration.history ?? []).map((entry) => ({
|
||||
id: entry.id,
|
||||
source: entry.source,
|
||||
changedByEmployeeId: entry.changedByEmployeeId,
|
||||
previousDailyRate: entry.previousDailyRate,
|
||||
nextDailyRate: entry.nextDailyRate,
|
||||
previousWeeklyRate: entry.previousWeeklyRate,
|
||||
nextWeeklyRate: entry.nextWeeklyRate,
|
||||
note: entry.note,
|
||||
effectiveFrom: entry.effectiveFrom,
|
||||
createdAt: entry.createdAt,
|
||||
})),
|
||||
preview: buildPricePreview(vehicle, configuration),
|
||||
};
|
||||
}
|
||||
async function listVehicles(companyId, query) {
|
||||
const page = query.page ?? 1;
|
||||
const pageSize = query.pageSize ?? 20;
|
||||
const { status, category, published } = query;
|
||||
const where = { companyId };
|
||||
if (status)
|
||||
where.status = status;
|
||||
if (category)
|
||||
where.category = category;
|
||||
if (published !== undefined)
|
||||
where.isPublished = published === 'true';
|
||||
const [vehicles, total] = await repo.findMany(where, (page - 1) * pageSize, pageSize);
|
||||
return (0, vehicle_presenter_1.presentVehicleList)(vehicles.map(vehicle_presenter_1.presentVehicle), { total, page, pageSize, totalPages: Math.ceil(total / pageSize) });
|
||||
}
|
||||
async function getVehicle(id, companyId) {
|
||||
const vehicle = await repo.findById(id, companyId);
|
||||
if (!vehicle)
|
||||
throw new errors_1.NotFoundError('Vehicle not found');
|
||||
return (0, vehicle_presenter_1.presentVehicle)(vehicle);
|
||||
}
|
||||
async function createVehicle(data, companyId) {
|
||||
return (0, vehicle_presenter_1.presentVehicle)(await repo.create({
|
||||
dailyRate: 0,
|
||||
...applyLocationSettings(data),
|
||||
companyId,
|
||||
}));
|
||||
}
|
||||
const PUBLISHED_STATUSES = new Set(['AVAILABLE', 'RESERVED', 'READY', 'RENTED']);
|
||||
async function updateVehicle(id, companyId, data) {
|
||||
const patch = applyLocationSettings(data);
|
||||
if (patch.status) {
|
||||
patch.isPublished = PUBLISHED_STATUSES.has(patch.status);
|
||||
}
|
||||
const existing = await repo.findFirst(id, companyId);
|
||||
if (!existing)
|
||||
throw new errors_1.NotFoundError('Vehicle not found');
|
||||
const updated = await repo.updateById(id, patch);
|
||||
if (patch.dailyRate !== undefined && typeof patch.dailyRate === 'number') {
|
||||
await syncPricingBaseRateFromVehicle(id, existing.dailyRate, patch.dailyRate);
|
||||
}
|
||||
return (0, vehicle_presenter_1.presentVehicle)(updated);
|
||||
}
|
||||
async function setStatus(id, companyId, status) {
|
||||
const existing = await repo.findFirst(id, companyId);
|
||||
if (!existing)
|
||||
throw new errors_1.NotFoundError('Vehicle not found');
|
||||
const isPublished = PUBLISHED_STATUSES.has(status);
|
||||
return (0, vehicle_presenter_1.presentVehicle)(await repo.updateById(id, { status, isPublished }));
|
||||
}
|
||||
async function deleteVehicle(id, companyId) {
|
||||
return repo.softDelete(id, companyId);
|
||||
}
|
||||
async function uploadPhotos(id, companyId, files) {
|
||||
const vehicle = await repo.findById(id, companyId);
|
||||
if (!vehicle)
|
||||
throw new errors_1.NotFoundError('Vehicle not found');
|
||||
const urls = await Promise.all(files.map((f) => (0, storage_1.uploadImage)(f.buffer, `companies/${companyId}/vehicles`)));
|
||||
return (0, vehicle_presenter_1.presentVehicle)(await repo.updateById(id, { photos: [...vehicle.photos, ...urls] }));
|
||||
}
|
||||
async function deletePhoto(id, companyId, idx) {
|
||||
const vehicle = await repo.findById(id, companyId);
|
||||
if (!vehicle)
|
||||
throw new errors_1.NotFoundError('Vehicle not found');
|
||||
const photos = vehicle.photos.filter((_, i) => i !== idx);
|
||||
return (0, vehicle_presenter_1.presentVehicle)(await repo.updateById(id, { photos }));
|
||||
}
|
||||
async function setPublished(id, companyId, isPublished) {
|
||||
await repo.setPublished(id, companyId, isPublished);
|
||||
}
|
||||
async function checkAvailability(id, companyId, startDate, endDate) {
|
||||
const start = new Date(startDate);
|
||||
const end = new Date(endDate);
|
||||
const [conflicts, blocks] = await Promise.all([
|
||||
repo.findReservationConflicts(id, companyId, start, end),
|
||||
repo.findCalendarBlockConflicts(id, start, end),
|
||||
]);
|
||||
return { available: conflicts.length === 0 && blocks.length === 0, conflicts, blocks };
|
||||
}
|
||||
async function getCalendar(id, companyId, year, month) {
|
||||
const vehicle = await repo.findById(id, companyId);
|
||||
if (!vehicle)
|
||||
throw new errors_1.NotFoundError('Vehicle not found');
|
||||
const rangeStart = new Date(year, month - 1, 1);
|
||||
const rangeEnd = new Date(year, month, 1);
|
||||
const [reservations, blocks] = await repo.findCalendarEvents(id, companyId, rangeStart, rangeEnd);
|
||||
return [
|
||||
...reservations.map((r) => ({
|
||||
id: r.id,
|
||||
type: 'RESERVATION',
|
||||
startDate: r.startDate,
|
||||
endDate: r.endDate,
|
||||
status: r.status,
|
||||
label: r.customer ? `${r.customer.firstName} ${r.customer.lastName}` : 'Reserved',
|
||||
})),
|
||||
...blocks.map((b) => ({
|
||||
id: b.id,
|
||||
type: b.type === 'MAINTENANCE' ? 'MAINTENANCE' : 'BLOCK',
|
||||
startDate: b.startDate,
|
||||
endDate: b.endDate,
|
||||
status: null,
|
||||
label: b.reason ?? (b.type === 'MAINTENANCE' ? 'Maintenance' : 'Blocked'),
|
||||
})),
|
||||
];
|
||||
}
|
||||
async function createCalendarBlock(id, companyId, data) {
|
||||
const start = new Date(data.startDate);
|
||||
const end = new Date(data.endDate);
|
||||
if (end <= start)
|
||||
throw new errors_1.ValidationError('endDate must be after startDate');
|
||||
const vehicle = await repo.findById(id, companyId);
|
||||
if (!vehicle)
|
||||
throw new errors_1.NotFoundError('Vehicle not found');
|
||||
return repo.createCalendarBlock({ vehicleId: id, startDate: start, endDate: end, reason: data.reason, type: data.type ?? 'MANUAL' });
|
||||
}
|
||||
async function deleteCalendarBlock(vehicleId, companyId, blockId) {
|
||||
const vehicle = await repo.findById(vehicleId, companyId);
|
||||
if (!vehicle)
|
||||
throw new errors_1.NotFoundError('Vehicle not found');
|
||||
await repo.deleteCalendarBlock(blockId, vehicleId);
|
||||
}
|
||||
async function getMaintenanceLogs(id, companyId) {
|
||||
const vehicle = await repo.findById(id, companyId);
|
||||
if (!vehicle)
|
||||
throw new errors_1.NotFoundError('Vehicle not found');
|
||||
return repo.findMaintenanceLogs(id);
|
||||
}
|
||||
async function createMaintenanceLog(id, companyId, data) {
|
||||
const vehicle = await repo.findById(id, companyId);
|
||||
if (!vehicle)
|
||||
throw new errors_1.NotFoundError('Vehicle not found');
|
||||
return repo.createMaintenanceLog({
|
||||
...data,
|
||||
vehicleId: id,
|
||||
performedAt: new Date(data.performedAt),
|
||||
nextDueAt: data.nextDueAt ? new Date(data.nextDueAt) : undefined,
|
||||
});
|
||||
}
|
||||
async function getVehiclePricing(id, companyId) {
|
||||
const { vehicle, configuration } = await ensurePricingConfigurationForVehicle(id, companyId);
|
||||
return presentVehiclePricing(vehicle, configuration);
|
||||
}
|
||||
async function updateVehiclePricing(id, companyId, employeeId, data) {
|
||||
const { vehicle, configuration, persistenceAvailable } = await ensurePricingConfigurationForVehicle(id, companyId);
|
||||
if (!persistenceAvailable) {
|
||||
throw new errors_1.AppError('Vehicle pricing tables are not available yet. Run the latest database migration, then retry.', 503, 'pricing_migration_required');
|
||||
}
|
||||
const nextConfig = normalizePricingConfigurationInput(data);
|
||||
const currentBaseDailyRate = configuration.baseDailyRate ?? vehicle.dailyRate;
|
||||
const currentWeeklyRate = configuration.weeklyRate ?? null;
|
||||
const changed = [
|
||||
'pricingMode',
|
||||
'baseDailyRate',
|
||||
'weeklyRate',
|
||||
'weekendRate',
|
||||
'holidayRate',
|
||||
'monthlyRate',
|
||||
'longTermDailyRate',
|
||||
'minimumDailyRate',
|
||||
'maximumDailyRate',
|
||||
'maxDailyPriceMovementPct',
|
||||
'automaticPricingEnabled',
|
||||
'approvalRequired',
|
||||
].some((key) => (configuration[key] ?? null) !== (nextConfig[key] ?? null));
|
||||
await repo.updatePricingConfiguration(configuration.id, nextConfig);
|
||||
if (vehicle.dailyRate !== nextConfig.baseDailyRate) {
|
||||
await repo.updateById(id, { dailyRate: nextConfig.baseDailyRate });
|
||||
}
|
||||
if (changed) {
|
||||
await repo.createPriceHistory({
|
||||
configurationId: configuration.id,
|
||||
vehicleId: id,
|
||||
source: 'CONFIG_UPDATE',
|
||||
changedByEmployeeId: employeeId,
|
||||
previousDailyRate: currentBaseDailyRate,
|
||||
nextDailyRate: nextConfig.baseDailyRate,
|
||||
previousWeeklyRate: currentWeeklyRate,
|
||||
nextWeeklyRate: nextConfig.weeklyRate ?? null,
|
||||
note: data.note,
|
||||
});
|
||||
}
|
||||
return getVehiclePricing(id, companyId);
|
||||
}
|
||||
async function createVehiclePricingRule(id, companyId, employeeId, data) {
|
||||
const { configuration, persistenceAvailable } = await ensurePricingConfigurationForVehicle(id, companyId);
|
||||
if (!persistenceAvailable) {
|
||||
throw new errors_1.AppError('Vehicle pricing tables are not available yet. Run the latest database migration, then retry.', 503, 'pricing_migration_required');
|
||||
}
|
||||
const payload = normalizePricingRuleInput(data);
|
||||
const rule = await repo.createPricingRule({
|
||||
configurationId: configuration.id,
|
||||
...payload,
|
||||
});
|
||||
await repo.createPriceHistory({
|
||||
configurationId: configuration.id,
|
||||
vehicleId: id,
|
||||
source: payload.ruleType === 'MANUAL_OVERRIDE' ? 'MANUAL_OVERRIDE' : 'RULE_CREATED',
|
||||
changedByEmployeeId: employeeId,
|
||||
previousDailyRate: configuration.baseDailyRate ?? null,
|
||||
nextDailyRate: payload.dailyRate,
|
||||
previousWeeklyRate: configuration.weeklyRate ?? null,
|
||||
nextWeeklyRate: payload.weeklyRate,
|
||||
note: `Created pricing rule: ${rule.name}`,
|
||||
effectiveFrom: rule.startDate,
|
||||
});
|
||||
return getVehiclePricing(id, companyId);
|
||||
}
|
||||
async function updateVehiclePricingRule(id, companyId, ruleId, employeeId, data) {
|
||||
const { configuration, persistenceAvailable } = await ensurePricingConfigurationForVehicle(id, companyId);
|
||||
if (!persistenceAvailable) {
|
||||
throw new errors_1.AppError('Vehicle pricing tables are not available yet. Run the latest database migration, then retry.', 503, 'pricing_migration_required');
|
||||
}
|
||||
const existingRule = await repo.findPricingRule(ruleId, configuration.id);
|
||||
if (!existingRule)
|
||||
throw new errors_1.NotFoundError('Pricing rule not found');
|
||||
const mergedRule = normalizePricingRuleInput({
|
||||
...existingRule,
|
||||
...data,
|
||||
startDate: data.startDate ?? existingRule.startDate,
|
||||
endDate: data.endDate ?? existingRule.endDate,
|
||||
});
|
||||
const updatedRule = await repo.updatePricingRule(ruleId, mergedRule);
|
||||
await repo.createPriceHistory({
|
||||
configurationId: configuration.id,
|
||||
vehicleId: id,
|
||||
source: mergedRule.ruleType === 'MANUAL_OVERRIDE' ? 'MANUAL_OVERRIDE' : 'RULE_UPDATED',
|
||||
changedByEmployeeId: employeeId,
|
||||
previousDailyRate: existingRule.dailyRate,
|
||||
nextDailyRate: updatedRule.dailyRate,
|
||||
previousWeeklyRate: existingRule.weeklyRate,
|
||||
nextWeeklyRate: updatedRule.weeklyRate,
|
||||
note: `Updated pricing rule: ${updatedRule.name}`,
|
||||
effectiveFrom: updatedRule.startDate,
|
||||
});
|
||||
return getVehiclePricing(id, companyId);
|
||||
}
|
||||
async function deleteVehiclePricingRule(id, companyId, ruleId, employeeId) {
|
||||
const { configuration, persistenceAvailable } = await ensurePricingConfigurationForVehicle(id, companyId);
|
||||
if (!persistenceAvailable) {
|
||||
throw new errors_1.AppError('Vehicle pricing tables are not available yet. Run the latest database migration, then retry.', 503, 'pricing_migration_required');
|
||||
}
|
||||
const existingRule = await repo.findPricingRule(ruleId, configuration.id);
|
||||
if (!existingRule)
|
||||
throw new errors_1.NotFoundError('Pricing rule not found');
|
||||
await repo.deletePricingRule(ruleId, configuration.id);
|
||||
await repo.createPriceHistory({
|
||||
configurationId: configuration.id,
|
||||
vehicleId: id,
|
||||
source: existingRule.ruleType === 'MANUAL_OVERRIDE' ? 'MANUAL_OVERRIDE' : 'RULE_DELETED',
|
||||
changedByEmployeeId: employeeId,
|
||||
previousDailyRate: existingRule.dailyRate,
|
||||
nextDailyRate: configuration.baseDailyRate ?? null,
|
||||
previousWeeklyRate: existingRule.weeklyRate,
|
||||
nextWeeklyRate: configuration.weeklyRate ?? null,
|
||||
note: `Deleted pricing rule: ${existingRule.name}`,
|
||||
effectiveFrom: existingRule.startDate,
|
||||
});
|
||||
return getVehiclePricing(id, companyId);
|
||||
}
|
||||
//# sourceMappingURL=vehicle.service.js.map
|
||||
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user