From 82ba980b6f5a1e08354fef456ed636d3258a96ee Mon Sep 17 00:00:00 2001 From: paritosh18 Date: Thu, 8 Jan 2026 12:14:12 +0530 Subject: [PATCH] Refactor HostService to improve validation logic and error handling for activity payloads. Added checks for draft status in various validation conditions, ensuring proper error messages are thrown for required fields and amounts. Cleaned up import statements for better organization. --- src/modules/host/services/host.service.ts | 158 ++++++++++++---------- 1 file changed, 85 insertions(+), 73 deletions(-) diff --git a/src/modules/host/services/host.service.ts b/src/modules/host/services/host.service.ts index 678d93e..559483a 100644 --- a/src/modules/host/services/host.service.ts +++ b/src/modules/host/services/host.service.ts @@ -1,16 +1,15 @@ // src/modules/host/services/host.service.ts import { Injectable } from '@nestjs/common'; -import { - AddPaymentDetailsDTO, - CreateHostDto, - UpdateHostDto, -} from '../dto/host.dto'; +import { PrismaClient, User } from '@prisma/client'; import * as bcrypt from 'bcryptjs'; -import ApiError from '../../../common/utils/helper/ApiError'; -import { User } from '@prisma/client'; -import { PrismaClient } from '@prisma/client'; import { z } from 'zod'; -import { hostCompanyDetailsSchema } from '../../../common/utils/validation/host/hostCompanyDetails.validation'; +import { getPresignedUrl } from '../../../common/middlewares/aws/getPreSignedUrl'; +import { + RESTRICTION_NAME, + ROLE, + ROLE_NAME, + USER_STATUS, +} from '../../../common/utils/constants/common.constant'; import { ACTIVITY_AM_DISPLAY_STATUS, ACTIVITY_AM_INTERNAL_STATUS, @@ -26,15 +25,15 @@ import { MINGLAR_STATUS_DISPLAY, MINGLAR_STATUS_INTERNAL, } from '../../../common/utils/constants/minglar.constant'; -import { - RESTRICTION_NAME, - ROLE, - ROLE_NAME, - USER_STATUS, -} from '../../../common/utils/constants/common.constant'; -import { getPresignedUrl } from '../../../common/middlewares/aws/getPreSignedUrl'; +import ApiError from '../../../common/utils/helper/ApiError'; +import { hostCompanyDetailsSchema } from '../../../common/utils/validation/host/hostCompanyDetails.validation'; import config from '../../../config/config'; import { CreateActivityInput } from '../dto/createActivity.schema'; +import { + AddPaymentDetailsDTO, + CreateHostDto, + UpdateHostDto, +} from '../dto/host.dto'; function sanitizeDocumentName(name?: string) { if (!name) return null; @@ -2451,37 +2450,41 @@ export class HostService { * CANCELLATION VALIDATION (NO CONVERSION) * ===================================================== */ if (payload.cancellationAvailable) { - if ( - typeof payload.cancellationAllowedBeforeMins !== 'number' || - Number.isNaN(payload.cancellationAllowedBeforeMins) || - payload.cancellationAllowedBeforeMins <= 0 - ) { - throw new ApiError( - 400, - 'cancellationAllowedBeforeMins must be a positive number (in minutes)', - ); - } + if (!isDraft) { + if ( + typeof payload.cancellationAllowedBeforeMins !== 'number' || + Number.isNaN(payload.cancellationAllowedBeforeMins) || + payload.cancellationAllowedBeforeMins <= 0 + ) { + throw new ApiError( + 400, + 'cancellationAllowedBeforeMins must be a positive number (in minutes)', + ); + } - if ( - durationMins > 0 && - payload.cancellationAllowedBeforeMins >= durationMins - ) { - throw new ApiError( - 400, - 'cancellationAllowedBeforeMins must be less than activity duration', - ); + if ( + durationMins > 0 && + payload.cancellationAllowedBeforeMins >= durationMins + ) { + throw new ApiError( + 400, + 'cancellationAllowedBeforeMins must be less than activity duration', + ); + } } } else { delete payload.cancellationAllowedBeforeMins; } if (payload.trainerAvailable) { - if ( - typeof payload.trainerTotalAmount !== 'number' || - Number.isNaN(payload.trainerTotalAmount) || - payload.trainerTotalAmount <= 0 - ) { - throw new ApiError(400, 'trainerTotalAmount must be > 0'); + if (!isDraft) { + if ( + typeof payload.trainerTotalAmount !== 'number' || + Number.isNaN(payload.trainerTotalAmount) || + payload.trainerTotalAmount <= 0 + ) { + throw new ApiError(400, 'trainerTotalAmount must be > 0'); + } } } else { delete payload.trainerTotalAmount; @@ -2494,19 +2497,24 @@ export class HostService { payload.venues?.forEach((v, idx) => { v.isMinPeopleReqMandatory = toBool(v.isMinPeopleReqMandatory); - if (!v.venueName) { - throw new ApiError(400, `venues[${idx}] venueName required`); - } + if (!isDraft) { + if (!v.venueName) { + throw new ApiError(400, `venues[${idx}] venueName required`); + } - if (v.isMinPeopleReqMandatory && !v.minPeopleRequired) { - throw new ApiError( - 400, - `venues[${idx}] min people requirement missing`, - ); - } + if (v.isMinPeopleReqMandatory && !v.minPeopleRequired) { + throw new ApiError( + 400, + `venues[${idx}] min people requirement missing`, + ); + } - if (!Array.isArray(v.prices) || !v.prices.length) { - throw new ApiError(400, `venues[${idx}] must have at least one price`); + if (!Array.isArray(v.prices) || !v.prices.length) { + throw new ApiError( + 400, + `venues[${idx}] must have at least one price`, + ); + } } }); @@ -2815,34 +2823,38 @@ export class HostService { if (payload.foodAvailable && payload.foodIsChargeable) { const foodTotalAmount = toNumber(payload.foodTotalAmount) ?? 0; - if (foodTotalAmount <= 0) + + if (!isDraft && foodTotalAmount <= 0) { throw new ApiError( 400, 'foodTotalAmount must be > 0 when foodIsChargeable', ); + } - const { basePrice, taxDetails } = computeBasePriceAndTaxes( - foodTotalAmount, - rootTaxes, - ); + if (foodTotalAmount > 0) { + const { basePrice, taxDetails } = computeBasePriceAndTaxes( + foodTotalAmount, + rootTaxes, + ); - const foodCost = await tx.activityFoodCost.create({ - data: { - activityXid, - baseAmount: basePrice, - totalAmount: foodTotalAmount, - }, - }); - - if (taxDetails.length) { - await tx.activityFoodTaxes.createMany({ - data: taxDetails.map((t) => ({ - activityFoodCostXid: foodCost.id, - taxXid: t.taxXid, - taxPer: t.taxPer, - taxAmount: t.taxAmount, - })), + const foodCost = await tx.activityFoodCost.create({ + data: { + activityXid, + baseAmount: basePrice, + totalAmount: foodTotalAmount, + }, }); + + if (taxDetails.length) { + await tx.activityFoodTaxes.createMany({ + data: taxDetails.map((t) => ({ + activityFoodCostXid: foodCost.id, + taxXid: t.taxXid, + taxPer: t.taxPer, + taxAmount: t.taxAmount, + })), + }); + } } } @@ -3072,7 +3084,7 @@ export class HostService { ? (toNumber(mode.totalPrice) ?? 0) : 0; - if (isChargeable && totalPrice <= 0) { + if (!isDraft && isChargeable && totalPrice <= 0) { throw new ApiError( 400, 'totalPrice must be > 0 when navigation mode is chargeable',