archetecture security fix
This commit is contained in:
@@ -0,0 +1,223 @@
|
||||
"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.listReservations = listReservations;
|
||||
exports.getReservation = getReservation;
|
||||
exports.createReservation = createReservation;
|
||||
exports.updateReservation = updateReservation;
|
||||
const prisma_1 = require("../../lib/prisma");
|
||||
const errors_1 = require("../../http/errors");
|
||||
const licenseValidationService_1 = require("../../services/licenseValidationService");
|
||||
const reservation_pricing_service_1 = require("./reservation.pricing.service");
|
||||
const reservation_insurance_service_1 = require("./reservation.insurance.service");
|
||||
const reservation_additional_driver_service_1 = require("./reservation.additional-driver.service");
|
||||
const reservation_presenter_1 = require("./reservation.presenter");
|
||||
const repo = __importStar(require("./reservation.repo"));
|
||||
async function listReservations(companyId, query) {
|
||||
const page = query.page ?? 1;
|
||||
const pageSize = query.pageSize ?? 20;
|
||||
const where = { companyId };
|
||||
if (query.status)
|
||||
where.status = query.status;
|
||||
if (query.vehicleId)
|
||||
where.vehicleId = query.vehicleId;
|
||||
if (query.source)
|
||||
where.source = query.source;
|
||||
if (query.startDate)
|
||||
where.startDate = { gte: new Date(query.startDate) };
|
||||
if (query.endDate)
|
||||
where.endDate = { lte: new Date(query.endDate) };
|
||||
const [reservations, total] = await repo.findMany(where, (page - 1) * pageSize, pageSize);
|
||||
return {
|
||||
data: reservations.map(reservation_presenter_1.serializeReservationForDashboard),
|
||||
total,
|
||||
page,
|
||||
pageSize,
|
||||
totalPages: Math.ceil(total / pageSize),
|
||||
};
|
||||
}
|
||||
async function getReservation(id, companyId) {
|
||||
const reservation = await repo.findById(id, companyId);
|
||||
return (0, reservation_presenter_1.serializeReservationForDashboard)(reservation);
|
||||
}
|
||||
async function createReservation(companyId, body) {
|
||||
const vehicle = await repo.findVehicle(body.vehicleId, companyId);
|
||||
await repo.findCustomer(body.customerId, companyId);
|
||||
const start = new Date(body.startDate);
|
||||
const end = new Date(body.endDate);
|
||||
const totalDays = Math.ceil((end.getTime() - start.getTime()) / (1000 * 60 * 60 * 24));
|
||||
const conflict = await repo.findConflict(body.vehicleId, start, end);
|
||||
if (conflict)
|
||||
throw new errors_1.AppError('Vehicle is not available for the selected dates', 409, 'vehicle_unavailable');
|
||||
let discountAmount = 0;
|
||||
let offerId = body.offerId ?? null;
|
||||
if (body.promoCodeUsed) {
|
||||
const offer = await repo.findActiveOffer(companyId, body.promoCodeUsed);
|
||||
if (offer) {
|
||||
offerId = offer.id;
|
||||
const base = vehicle.dailyRate * totalDays;
|
||||
if (offer.type === 'PERCENTAGE')
|
||||
discountAmount = Math.round(base * offer.discountValue / 100);
|
||||
else if (offer.type === 'FIXED_AMOUNT')
|
||||
discountAmount = offer.discountValue;
|
||||
else if (offer.type === 'FREE_DAY')
|
||||
discountAmount = vehicle.dailyRate * offer.discountValue;
|
||||
await repo.incrementOfferRedemption(offer.id);
|
||||
}
|
||||
}
|
||||
const depositAmount = body.depositAmount ?? 0;
|
||||
const additionalDriversList = body.additionalDrivers ?? [];
|
||||
const baseAmount = vehicle.dailyRate * totalDays;
|
||||
const { applied, total: pricingTotal } = await (0, reservation_pricing_service_1.applyPricingRules)(companyId, body.customerId, additionalDriversList, vehicle.dailyRate, totalDays);
|
||||
const totalAmount = baseAmount - discountAmount + pricingTotal + depositAmount;
|
||||
const reservation = await repo.create({
|
||||
companyId,
|
||||
vehicleId: body.vehicleId,
|
||||
customerId: body.customerId,
|
||||
startDate: start,
|
||||
endDate: end,
|
||||
pickupLocation: body.pickupLocation ?? null,
|
||||
returnLocation: body.returnLocation ?? null,
|
||||
offerId,
|
||||
promoCodeUsed: body.promoCodeUsed ?? null,
|
||||
source: 'DASHBOARD',
|
||||
dailyRate: vehicle.dailyRate,
|
||||
discountAmount,
|
||||
totalDays,
|
||||
totalAmount,
|
||||
depositAmount,
|
||||
notes: body.notes ?? null,
|
||||
extras: body.paymentMode ? { paymentMode: body.paymentMode } : undefined,
|
||||
pricingRulesApplied: applied,
|
||||
pricingRulesTotal: pricingTotal,
|
||||
});
|
||||
const insuranceIds = body.selectedInsurancePolicyIds ?? [];
|
||||
const additionalDrivers = body.additionalDrivers ?? [];
|
||||
if (insuranceIds.length > 0) {
|
||||
await (0, reservation_insurance_service_1.applyInsurancesToReservation)(reservation.id, companyId, insuranceIds, totalDays, baseAmount);
|
||||
}
|
||||
if (additionalDrivers.length > 0) {
|
||||
await (0, reservation_additional_driver_service_1.applyAdditionalDriversToReservation)(reservation.id, companyId, additionalDrivers, totalDays);
|
||||
}
|
||||
await (0, licenseValidationService_1.validateAndFlagLicense)(body.customerId, companyId).catch(() => null);
|
||||
return reservation;
|
||||
}
|
||||
async function updateReservation(id, companyId, body) {
|
||||
const reservation = await repo.findByIdWithRelations(id, companyId);
|
||||
if (reservation.status === 'CANCELLED' || reservation.status === 'NO_SHOW') {
|
||||
throw new errors_1.AppError('This reservation can no longer be edited', 400, 'invalid_status');
|
||||
}
|
||||
const workflow = (0, reservation_presenter_1.buildReservationWorkflow)(reservation);
|
||||
const requested = Object.keys(body).filter((k) => body[k] !== undefined);
|
||||
const bookingFields = ['startDate', 'endDate', 'pickupLocation', 'returnLocation', 'depositAmount', 'notes', 'paymentMode'];
|
||||
const returnFields = ['returnLocation', 'damageChargeAmount', 'damageChargeNote'];
|
||||
if (!workflow.coreEditable && !workflow.returnEditable) {
|
||||
throw new errors_1.AppError('This reservation is locked for editing', 400, 'reservation_locked');
|
||||
}
|
||||
if (workflow.coreEditable) {
|
||||
const invalid = requested.filter((f) => !bookingFields.includes(f));
|
||||
if (invalid.length > 0)
|
||||
throw new errors_1.AppError('Only booking details can be edited at this stage', 400, 'invalid_fields');
|
||||
const nextStart = body.startDate ? new Date(body.startDate) : reservation.startDate;
|
||||
const nextEnd = body.endDate ? new Date(body.endDate) : reservation.endDate;
|
||||
const totalDays = Math.ceil((nextEnd.getTime() - nextStart.getTime()) / (1000 * 60 * 60 * 24));
|
||||
if (nextEnd <= nextStart || totalDays <= 0)
|
||||
throw new errors_1.AppError('End date must be after start date', 400, 'invalid_dates');
|
||||
if (body.startDate || body.endDate) {
|
||||
const conflict = await repo.findConflict(reservation.vehicleId, nextStart, nextEnd, reservation.id);
|
||||
if (conflict)
|
||||
throw new errors_1.AppError('Vehicle is not available for the selected dates', 409, 'vehicle_unavailable');
|
||||
}
|
||||
const baseAmount = reservation.dailyRate * totalDays;
|
||||
const { applied, total: pricingRulesTotal } = await (0, reservation_pricing_service_1.applyPricingRules)(companyId, reservation.customerId, reservation.additionalDrivers.map((d) => ({ dateOfBirth: d.dateOfBirth, licenseIssuedAt: d.licenseIssuedAt })), reservation.dailyRate, totalDays);
|
||||
const insuranceUpdates = reservation.insurances.map((ins) => ({
|
||||
id: ins.id,
|
||||
totalCharge: (0, reservation_pricing_service_1.calculateUpdatedInsuranceCharge)(ins.chargeType, ins.chargeValue, totalDays, baseAmount),
|
||||
}));
|
||||
const driverUpdates = reservation.additionalDrivers.map((d) => ({
|
||||
id: d.id,
|
||||
totalCharge: (0, reservation_pricing_service_1.calculateUpdatedAdditionalDriverCharge)(d.chargeType, d.chargeValue, totalDays),
|
||||
}));
|
||||
const insuranceTotal = insuranceUpdates.reduce((s, i) => s + i.totalCharge, 0);
|
||||
const additionalDriverTotal = driverUpdates.reduce((s, d) => s + d.totalCharge, 0);
|
||||
const depositAmount = body.depositAmount ?? reservation.depositAmount;
|
||||
const totalAmount = baseAmount - reservation.discountAmount + pricingRulesTotal + insuranceTotal + additionalDriverTotal + depositAmount;
|
||||
const extras = (0, reservation_presenter_1.parseReservationExtras)(reservation.extras);
|
||||
if (body.paymentMode !== undefined) {
|
||||
const next = (0, reservation_presenter_1.normalizeOptionalString)(body.paymentMode);
|
||||
if (next)
|
||||
extras.paymentMode = next;
|
||||
else
|
||||
delete extras.paymentMode;
|
||||
}
|
||||
await prisma_1.prisma.$transaction([
|
||||
prisma_1.prisma.reservation.update({
|
||||
where: { id: reservation.id },
|
||||
data: {
|
||||
startDate: nextStart,
|
||||
endDate: nextEnd,
|
||||
totalDays,
|
||||
pickupLocation: body.pickupLocation !== undefined ? (0, reservation_presenter_1.normalizeOptionalString)(body.pickupLocation) : reservation.pickupLocation,
|
||||
returnLocation: body.returnLocation !== undefined ? (0, reservation_presenter_1.normalizeOptionalString)(body.returnLocation) : reservation.returnLocation,
|
||||
depositAmount,
|
||||
notes: body.notes !== undefined ? (0, reservation_presenter_1.normalizeOptionalString)(body.notes) : reservation.notes,
|
||||
pricingRulesApplied: applied,
|
||||
pricingRulesTotal,
|
||||
insuranceTotal,
|
||||
additionalDriverTotal,
|
||||
totalAmount,
|
||||
extras: (Object.keys(extras).length > 0 ? extras : {}),
|
||||
},
|
||||
}),
|
||||
...insuranceUpdates.map((ins) => prisma_1.prisma.reservationInsurance.update({ where: { id: ins.id }, data: { totalCharge: ins.totalCharge } })),
|
||||
...driverUpdates.map((d) => prisma_1.prisma.additionalDriver.update({ where: { id: d.id }, data: { totalCharge: d.totalCharge } })),
|
||||
]);
|
||||
}
|
||||
else {
|
||||
const invalid = requested.filter((f) => !returnFields.includes(f));
|
||||
if (invalid.length > 0)
|
||||
throw new errors_1.AppError('Only return details can be edited after vehicle return', 400, 'invalid_fields');
|
||||
await prisma_1.prisma.reservation.update({
|
||||
where: { id: reservation.id },
|
||||
data: {
|
||||
returnLocation: body.returnLocation !== undefined ? (0, reservation_presenter_1.normalizeOptionalString)(body.returnLocation) : reservation.returnLocation,
|
||||
damageChargeAmount: body.damageChargeAmount !== undefined ? body.damageChargeAmount : reservation.damageChargeAmount,
|
||||
damageChargeNote: body.damageChargeNote !== undefined ? (0, reservation_presenter_1.normalizeOptionalString)(body.damageChargeNote) : reservation.damageChargeNote,
|
||||
},
|
||||
});
|
||||
}
|
||||
return repo.findById(id, companyId).then(reservation_presenter_1.serializeReservationForDashboard);
|
||||
}
|
||||
//# sourceMappingURL=reservation.service.js.map
|
||||
Reference in New Issue
Block a user