navigation modes per price update
This commit is contained in:
@@ -2,20 +2,18 @@ import { z } from 'zod';
|
||||
|
||||
/* ================= MEDIA ================= */
|
||||
export const MediaDto = z.object({
|
||||
mediaType: z.string().optional(), // "image/jpeg", "video/mp4", etc.
|
||||
mediaFileName: z.string(), // S3 file URL
|
||||
mediaType: z.string().optional(),
|
||||
mediaFileName: z.string(),
|
||||
});
|
||||
|
||||
/* ================= PRICE =================
|
||||
* ❌ No tax info here; root-level only
|
||||
*/
|
||||
/* ================= PRICE ================= */
|
||||
export const PriceDto = z.object({
|
||||
noOfSession: z.number().int().optional().default(1),
|
||||
isPackage: z.boolean().optional().default(false),
|
||||
sessionValidity: z.number().int().optional().default(0),
|
||||
sessionValidityFrequency: z.string().optional().default('Days'),
|
||||
basePrice: z.number().int().optional().default(0),
|
||||
sellPrice: z.number().int(), // required
|
||||
sellPrice: z.number().int(),
|
||||
});
|
||||
|
||||
/* ================= VENUE ================= */
|
||||
@@ -28,11 +26,7 @@ export const VenueDto = z.object({
|
||||
minPeopleRequired: z.number().int().nullable().optional(),
|
||||
minReqfullfilledBeforeMins: z.number().int().nullable().optional(),
|
||||
venueDescription: z.string().optional(),
|
||||
|
||||
// ✅ new: media per venue (for ActivityVenueArtifacts)
|
||||
media: z.array(MediaDto).optional().default([]),
|
||||
|
||||
// price list per venue
|
||||
prices: z.array(PriceDto).optional().default([]),
|
||||
});
|
||||
|
||||
@@ -58,6 +52,13 @@ export const EquipmentDto = z.object({
|
||||
equipmentTotalPrice: z.number().int().optional().default(0),
|
||||
});
|
||||
|
||||
/* ================= NAVIGATION MODE ================= */
|
||||
export const NavigationModeDto = z.object({
|
||||
navigationModeXid: z.number().int(),
|
||||
isChargeable: z.boolean().optional().default(false),
|
||||
totalPrice: z.number().int().optional().default(0),
|
||||
});
|
||||
|
||||
/* ================= ELIGIBILITY ================= */
|
||||
export const EligibilityDto = z.object({
|
||||
isAgeRestriction: z.boolean().optional().default(false),
|
||||
@@ -93,16 +94,13 @@ export const OtherDetailsDto = z.object({
|
||||
|
||||
/* ================= CREATE ACTIVITY ================= */
|
||||
export const CreateActivityDto = z.object({
|
||||
/* 🔑 REQUIRED */
|
||||
activityXid: z.number().int(),
|
||||
|
||||
/* OPTIONAL CORE */
|
||||
activityTypeXid: z.number().int().optional(),
|
||||
frequenciesXid: z.number().int().nullable().optional(),
|
||||
activityTitle: z.string().optional(),
|
||||
activityDescription: z.string().optional(),
|
||||
|
||||
/* LOCATION */
|
||||
checkInLat: z.number().nullable().optional(),
|
||||
checkInLong: z.number().nullable().optional(),
|
||||
checkInAddress: z.string().nullable().optional(),
|
||||
@@ -111,13 +109,11 @@ export const CreateActivityDto = z.object({
|
||||
checkOutLong: z.number().nullable().optional(),
|
||||
checkOutAddress: z.string().nullable().optional(),
|
||||
|
||||
/* DURATION / ENERGY */
|
||||
energyLevelXid: z.number().int().nullable().optional(),
|
||||
durationDays: z.number().int().optional(),
|
||||
durationHours: z.number().int().optional(),
|
||||
durationMins: z.number().int().optional(),
|
||||
|
||||
/* FLAGS */
|
||||
foodAvailable: z.boolean().optional().default(false),
|
||||
foodIsChargeable: z.boolean().optional().default(false),
|
||||
alcoholAvailable: z.boolean().optional().default(false),
|
||||
@@ -137,37 +133,35 @@ export const CreateActivityDto = z.object({
|
||||
cancellationAvailable: z.boolean().optional().default(false),
|
||||
cancellationAllowedBeforeMins: z.number().int().nullable().optional(),
|
||||
|
||||
/* MONEY / CURRENCY */
|
||||
currencyXid: z.number().int().nullable().optional(),
|
||||
sustainabilityScore: z.number().int().nullable().optional(),
|
||||
safetyScore: z.number().int().nullable().optional(),
|
||||
isInstantBooking: z.boolean().optional().default(false),
|
||||
|
||||
/* 🔥 ROOT-LEVEL TAX (SINGLE SOURCE OF TRUTH) */
|
||||
taxXids: z.array(z.number().int()).optional().default([]),
|
||||
|
||||
/* 🔥 MEDIA ARRAYS */
|
||||
media: z.array(MediaDto).optional().default([]), // Activity-level media
|
||||
venues: z.array(VenueDto).optional().default([]), // Each venue’s media + prices
|
||||
media: z.array(MediaDto).optional().default([]),
|
||||
venues: z.array(VenueDto).optional().default([]),
|
||||
|
||||
/* RELATION ARRAYS */
|
||||
foodTypeIds: z.array(z.number().int()).optional().default([]),
|
||||
cuisineIds: z.array(z.number().int()).optional().default([]),
|
||||
pickupTransports: z.array(PickupTransportDto).optional().default([]),
|
||||
navigationModes: z.array(z.number().int()).optional().default([]),
|
||||
|
||||
navigationModes: z
|
||||
.array(NavigationModeDto)
|
||||
.optional()
|
||||
.default([]),
|
||||
|
||||
equipments: z.array(EquipmentDto).optional().default([]),
|
||||
amenitiesIds: z.array(z.number().int()).optional().default([]),
|
||||
|
||||
foodTotalAmount: z.number().int().optional().default(0),
|
||||
|
||||
/* EXTRA OBJECTS */
|
||||
eligibility: EligibilityDto.optional(),
|
||||
otherDetails: OtherDetailsDto.optional(),
|
||||
|
||||
allowedEntryTypes: z.array(z.number().int()).optional().default([]),
|
||||
navigationModeIsChargeable: z.boolean().optional().default(false),
|
||||
trainerTotalAmount: z.number().int().optional().default(0),
|
||||
navigationModeTotalPrice: z.number().int().optional().default(0),
|
||||
});
|
||||
|
||||
export type CreateActivityInput = z.infer<typeof CreateActivityDto>;
|
||||
|
||||
@@ -3025,34 +3025,55 @@ export class HostService {
|
||||
await tx.activityNavigationModesTaxes.deleteMany({
|
||||
where: { activityNavigationModeXid: { in: oldNavIds } },
|
||||
});
|
||||
|
||||
await tx.activityNavigationModes.deleteMany({
|
||||
where: { id: { in: oldNavIds } },
|
||||
});
|
||||
}
|
||||
|
||||
if (
|
||||
Array.isArray(payload.navigationModes) &&
|
||||
payload.navigationModes.length
|
||||
) {
|
||||
const totalPrice = toNumber(payload.navigationModeTotalPrice) ?? 0;
|
||||
const { basePrice, taxDetails } = computeBasePriceAndTaxes(
|
||||
totalPrice,
|
||||
rootTaxes,
|
||||
);
|
||||
/* --------------------------------
|
||||
* 1️⃣2️⃣ CREATE NAVIGATION MODES (PER MODE)
|
||||
* -------------------------------- */
|
||||
if (Array.isArray(payload.navigationModes)) {
|
||||
for (const mode of payload.navigationModes) {
|
||||
const isChargeable = toBool(mode.isChargeable);
|
||||
const totalPrice = isChargeable
|
||||
? (toNumber(mode.totalPrice) ?? 0)
|
||||
: 0;
|
||||
|
||||
for (const modeId of payload.navigationModes) {
|
||||
if (isChargeable && totalPrice <= 0) {
|
||||
throw new ApiError(
|
||||
400,
|
||||
'totalPrice must be > 0 when navigation mode is chargeable',
|
||||
);
|
||||
}
|
||||
|
||||
let basePrice = 0;
|
||||
let taxDetails: Array<{
|
||||
taxXid: number;
|
||||
taxPer: number;
|
||||
taxAmount: number;
|
||||
}> = [];
|
||||
|
||||
if (isChargeable) {
|
||||
const result = computeBasePriceAndTaxes(totalPrice, rootTaxes);
|
||||
|
||||
basePrice = result.basePrice;
|
||||
taxDetails = result.taxDetails;
|
||||
}
|
||||
|
||||
/* 1️⃣ CREATE NAVIGATION MODE ROW */
|
||||
const navMode = await tx.activityNavigationModes.create({
|
||||
data: {
|
||||
activityXid,
|
||||
navigationModeXid: modeId,
|
||||
isInActivityChargeable: toBool(
|
||||
payload.navigationModeIsChargeable,
|
||||
),
|
||||
navigationModeXid: mode.navigationModeXid,
|
||||
isInActivityChargeable: isChargeable,
|
||||
navigationModesBasePrice: basePrice,
|
||||
navigationModesTotalPrice: totalPrice,
|
||||
},
|
||||
});
|
||||
|
||||
/* 2️⃣ CREATE TAXES (ONLY IF CHARGEABLE) */
|
||||
if (taxDetails.length) {
|
||||
await tx.activityNavigationModesTaxes.createMany({
|
||||
data: taxDetails.map((t) => ({
|
||||
|
||||
Reference in New Issue
Block a user