navigation modes per price update

This commit is contained in:
paritosh18
2025-12-31 14:42:30 +05:30
parent 401734096d
commit c2d3ab9da2
2 changed files with 54 additions and 39 deletions

View File

@@ -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 venues 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>;

View File

@@ -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) => ({