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.

This commit is contained in:
paritosh18
2026-01-08 12:14:12 +05:30
parent 5ec819b881
commit 82ba980b6f

View File

@@ -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',