From 6bbcb36b10579408ab309d9f5f23d577cb9b035d Mon Sep 17 00:00:00 2001 From: Mayank Mishra Date: Thu, 20 Nov 2025 15:23:15 +0530 Subject: [PATCH] Add editAgreementDetails and acceptHostApplication handlers; update serverless.yml and Prisma schema --- prisma/schema.prisma | 178 +++++++++--------- serverless.yml | 15 ++ .../handlers/acceptHostApplication.ts | 72 +++++++ .../handlers/editAgreementDetails.ts | 90 +++++++++ .../minglaradmin/services/minglar.service.ts | 49 ++++- 5 files changed, 314 insertions(+), 90 deletions(-) create mode 100644 src/modules/minglaradmin/handlers/acceptHostApplication.ts create mode 100644 src/modules/minglaradmin/handlers/editAgreementDetails.ts diff --git a/prisma/schema.prisma b/prisma/schema.prisma index 27a5268..3f8157e 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -637,60 +637,62 @@ model Token { //HOST MODELS model HostHeader { - id Int @id @default(autoincrement()) - userXid Int @map("user_xid") - user User @relation("HostUser", fields: [userXid], references: [id], onDelete: Cascade) - companyName String @map("company_name") - hostRefNumber String @map("host_ref_number") - address1 String @map("address_1") - address2 String? @map("address_2") - cityXid Int @map("city_xid") - cities Cities @relation(fields: [cityXid], references: [id], onDelete: Restrict) - stateXid Int @map("state_xid") - states States @relation(fields: [stateXid], references: [id], onDelete: Restrict) - countryXid Int @map("country_xid") - countries Countries @relation(fields: [countryXid], references: [id], onDelete: Restrict) - pinCode String @map("pin_code") - logoPath String? @map("logo_path") - isSubsidairy Boolean @default(false) @map("is_subsidairy") - registrationNumber String @map("registration_number") - panNumber String @map("pan_number") - gstNumber String? @map("gst_number") - formationDate DateTime @map("formation_date") - companyType String @map("company_type") - websiteUrl String? @map("website_url") - instagramUrl String? @map("instagram_url") - facebookUrl String? @map("facebook_url") - linkedinUrl String? @map("linkedin_url") - twitterUrl String? @map("twitter_url") - currencyXid Int @map("currency_xid") - currencies Currencies @relation(fields: [currencyXid], references: [id], onDelete: Restrict) - stepper Int @default(1) @map("stepper") - hostStatusInternal String @default("pending") @map("host_status_internal") - hostStatusDisplay String @default("pending") @map("host_status_Display") - adminStatusInternal String @default("pending") @map("admin_status_internal") - adminStatusDisplay String @default("pending") @map("admin_status_display") - amStatus String @default("pending") @map("am_status") - agreementAccepted Boolean @default(false) @map("agreement_accepted") - accountManagerXid Int? @map("account_manager_xid") - accountManager User? @relation("AccountManager", fields: [accountManagerXid], references: [id], onDelete: Restrict) - isApproved Boolean @default(false) @map("is_approved") - agreementStartDate DateTime? @map("agreement_start_date") - durationNumber Int? @map("duration_number") - durationFrequency String? @map("duration_frequency") - isCommisionBase Boolean @default(false) @map("is_commision_base") - commisionPer Float? @map("commision_per") - amountPerBooking Int? @map("amount_per_booking") - isActive Boolean @default(true) @map("is_active") - createdAt DateTime @default(now()) @map("created_at") - updatedAt DateTime @updatedAt @map("updated_at") - deletedAt DateTime? @map("deleted_at") - HostBankDetails HostBankDetails[] - HostDocuments HostDocuments[] - HostSuggestion HostSuggestion[] - hostParent HostParent[] - HostTrack HostTrack[] - Activities Activities[] + id Int @id @default(autoincrement()) + userXid Int @map("user_xid") + user User @relation("HostUser", fields: [userXid], references: [id], onDelete: Cascade) + companyName String @map("company_name") + hostRefNumber String @map("host_ref_number") + address1 String @map("address_1") + address2 String? @map("address_2") + cityXid Int @map("city_xid") + cities Cities @relation(fields: [cityXid], references: [id], onDelete: Restrict) + stateXid Int @map("state_xid") + states States @relation(fields: [stateXid], references: [id], onDelete: Restrict) + countryXid Int @map("country_xid") + countries Countries @relation(fields: [countryXid], references: [id], onDelete: Restrict) + pinCode String @map("pin_code") + logoPath String? @map("logo_path") + isSubsidairy Boolean @default(false) @map("is_subsidairy") + registrationNumber String @map("registration_number") + panNumber String @map("pan_number") + gstNumber String? @map("gst_number") + formationDate DateTime @map("formation_date") + companyType String @map("company_type") + websiteUrl String? @map("website_url") + instagramUrl String? @map("instagram_url") + facebookUrl String? @map("facebook_url") + linkedinUrl String? @map("linkedin_url") + twitterUrl String? @map("twitter_url") + currencyXid Int @map("currency_xid") + currencies Currencies @relation(fields: [currencyXid], references: [id], onDelete: Restrict) + stepper Int @default(1) @map("stepper") + hostStatusInternal String @default("pending") @map("host_status_internal") + hostStatusDisplay String @default("pending") @map("host_status_Display") + adminStatusInternal String @default("pending") @map("admin_status_internal") + adminStatusDisplay String @default("pending") @map("admin_status_display") + amStatus String @default("pending") @map("am_status") + agreementAccepted Boolean @default(false) @map("agreement_accepted") + accountManagerXid Int? @map("account_manager_xid") + accountManager User? @relation("AccountManager", fields: [accountManagerXid], references: [id], onDelete: Restrict) + isApproved Boolean @default(false) @map("is_approved") + agreementStartDate DateTime? @map("agreement_start_date") + durationNumber Int? @map("duration_number") + durationFrequency String? @map("duration_frequency") + isCommisionBase Boolean @default(false) @map("is_commision_base") + commisionPer Float? @map("commision_per") + amountPerBooking Int? @map("amount_per_booking") + payoutDurationNum Int? @map("payout_duration_num") + payoutDurationFrequency String? @map("payout_duration_frequency") + isActive Boolean @default(true) @map("is_active") + createdAt DateTime @default(now()) @map("created_at") + updatedAt DateTime @updatedAt @map("updated_at") + deletedAt DateTime? @map("deleted_at") + HostBankDetails HostBankDetails[] + HostDocuments HostDocuments[] + HostSuggestion HostSuggestion[] + hostParent HostParent[] + HostTrack HostTrack[] + Activities Activities[] @@map("host_header") @@schema("hst") @@ -743,8 +745,8 @@ model HostSuggestion { comments String @map("comments") isparent Boolean @default(false) @map("is_parent") isreviewed Boolean @default(false) @map("is_reviewed") - reviewedByXid Int @map("reviewed_by_xid") - reviewedBy User @relation("UserReviewedSuggestions", fields: [reviewedByXid], references: [id], onDelete: Cascade) + reviewedByXid Int? @map("reviewed_by_xid") + reviewedBy User? @relation("UserReviewedSuggestions", fields: [reviewedByXid], references: [id], onDelete: Cascade) reviewOn DateTime? @map("review_on") isActive Boolean @default(true) @map("is_active") createdAt DateTime @default(now()) @map("created_at") @@ -832,44 +834,44 @@ model Activities { host HostHeader @relation(fields: [hostXid], references: [id], onDelete: Cascade) activityTypeXid Int @map("activity_type_xid") activityType ActivityTypes @relation(fields: [activityTypeXid], references: [id], onDelete: Restrict) - frequenciesXid Int? @map("frequencies_xid") - frequency Frequencies? @relation(fields: [frequenciesXid], references: [id], onDelete: Restrict) + frequenciesXid Int? @map("frequencies_xid") + frequency Frequencies? @relation(fields: [frequenciesXid], references: [id], onDelete: Restrict) activityRefNumber String? @map("activity_ref_number") - activityTitle String? @map("activity_title") - activityDescription String? @map("activity_description") - checkInLat Float? @map("check_in_lat") - checkInLong Float? @map("check_in_long") - checkInAddress String? @map("check_in_address") - isCheckOutSame Boolean? @default(true) @map("is_check_out_same") + activityTitle String? @map("activity_title") + activityDescription String? @map("activity_description") + checkInLat Float? @map("check_in_lat") + checkInLong Float? @map("check_in_long") + checkInAddress String? @map("check_in_address") + isCheckOutSame Boolean? @default(true) @map("is_check_out_same") checkOutLat Float? @map("check_out_lat") checkOutLong Float? @map("check_out_long") checkOutAddress String? @map("check_out_address") energyLevelXid Int? @map("energy_level_xid") energyLevel EnergyLevels? @relation(fields: [energyLevelXid], references: [id], onDelete: Restrict) - activityDurationMins Int? @map("activity_duration_mins") - foodAvailable Boolean? @default(false) @map("food_available") - foodIsChargeable Boolean? @default(false) @map("food_is_chargeable") - alcoholAvailable Boolean? @default(false) @map("alcohol_available") - trainerAvailable Boolean? @default(false) @map("trainer_available") - trainerIsChargeable Boolean? @default(false) @map("trainer_is_chargeable") - pickUpDropAvailable Boolean? @default(false) @map("pick_up_drop_available") - pickUpDropIsChargeable Boolean? @default(false) @map("pick_up_drop_is_chargeable") - inActivityAvailable Boolean? @default(false) @map("in_activity_available") - inActivityIsChargeable Boolean? @default(false) @map("in_activity_is_chargeable") - equipmentAvailable Boolean? @default(false) @map("equipment_available") - equipmentIsChargeable Boolean? @default(false) @map("equipment_is_chargeable") - cancellationAvailable Boolean? @default(false) @map("cancellation_available") - cancellationAllowedBeforeMins Int? @map("cancellation_allowed_before_mins") - currencyXid Int? @map("currency_xid") - currencies Currencies? @relation(fields: [currencyXid], references: [id], onDelete: Restrict) - sustainabilityScore Int? @map("sustainability_score") - safetyScore Int? @map("safety_score") - totalScore Int? @map("total_score") - isInstantBooking Boolean? @default(false) @map("is_instant_booking") - activityInternalStatus String? @default("pending") @map("activity_internal_status") - activityDisplayStatus String? @default("pending") @map("activity_display_status") - amInternalStatus String? @default("pending") @map("am_internal_status") - amDisplayStatus String? @default("pending") @map("am_display_status") + activityDurationMins Int? @map("activity_duration_mins") + foodAvailable Boolean? @default(false) @map("food_available") + foodIsChargeable Boolean? @default(false) @map("food_is_chargeable") + alcoholAvailable Boolean? @default(false) @map("alcohol_available") + trainerAvailable Boolean? @default(false) @map("trainer_available") + trainerIsChargeable Boolean? @default(false) @map("trainer_is_chargeable") + pickUpDropAvailable Boolean? @default(false) @map("pick_up_drop_available") + pickUpDropIsChargeable Boolean? @default(false) @map("pick_up_drop_is_chargeable") + inActivityAvailable Boolean? @default(false) @map("in_activity_available") + inActivityIsChargeable Boolean? @default(false) @map("in_activity_is_chargeable") + equipmentAvailable Boolean? @default(false) @map("equipment_available") + equipmentIsChargeable Boolean? @default(false) @map("equipment_is_chargeable") + cancellationAvailable Boolean? @default(false) @map("cancellation_available") + cancellationAllowedBeforeMins Int? @map("cancellation_allowed_before_mins") + currencyXid Int? @map("currency_xid") + currencies Currencies? @relation(fields: [currencyXid], references: [id], onDelete: Restrict) + sustainabilityScore Int? @map("sustainability_score") + safetyScore Int? @map("safety_score") + totalScore Int? @map("total_score") + isInstantBooking Boolean? @default(false) @map("is_instant_booking") + activityInternalStatus String? @default("pending") @map("activity_internal_status") + activityDisplayStatus String? @default("pending") @map("activity_display_status") + amInternalStatus String? @default("pending") @map("am_internal_status") + amDisplayStatus String? @default("pending") @map("am_display_status") isActive Boolean @default(true) @map("is_active") createdAt DateTime @default(now()) @map("created_at") updatedAt DateTime @updatedAt @map("updated_at") diff --git a/serverless.yml b/serverless.yml index 6083dab..d7eb8d3 100644 --- a/serverless.yml +++ b/serverless.yml @@ -412,6 +412,21 @@ functions: path: /minglaradmin/assign-am-to-host method: patch + editAgreementDetails: + handler: src/modules/minglaradmin/handlers/editAgreementDetails.handler + package: + patterns: + - 'src/modules/minglaradmin/**' + - 'common/**' + - 'src/common/**' + - 'node_modules/@prisma/client/**' + - 'node_modules/.prisma/**' + + events: + - httpApi: + path: /minglaradmin/edit-agreement-details + method: patch + addCompanyDetails: handler: src/modules/host/handlers/addCompanyDetails.handler package: diff --git a/src/modules/minglaradmin/handlers/acceptHostApplication.ts b/src/modules/minglaradmin/handlers/acceptHostApplication.ts new file mode 100644 index 0000000..61a912e --- /dev/null +++ b/src/modules/minglaradmin/handlers/acceptHostApplication.ts @@ -0,0 +1,72 @@ +import { APIGatewayProxyEvent, APIGatewayProxyResult, Context } from 'aws-lambda'; +import { PrismaService } from '../../../common/database/prisma.service'; +import { verifyMinglarAdminToken } from '../../../common/middlewares/jwt/authForMinglarAdmin'; +import { safeHandler } from '../../../common/utils/handlers/safeHandler'; +import ApiError from '../../../common/utils/helper/ApiError'; +import { MinglarService } from '../services/minglar.service'; + +const prismaService = new PrismaService(); +const minglarService = new MinglarService(prismaService); + +interface AddSuggestionBody { + hostXid: number; + title: string; + comments: string; +} + +/** + * Add suggestion handler for host applications + * Allows Minglar Admin, Co_Admin, and Account Manager to add suggestions + * Types: Setup Profile, Review Account, Add Payment Details, Agreement + */ +export const handler = safeHandler(async ( + event: APIGatewayProxyEvent, + context?: Context +): Promise => { + // Verify authentication token + const token = event.headers['x-auth-token'] || event.headers['X-Auth-Token']; + if (!token) { + throw new ApiError(401, 'This is a protected route. Please provide a valid token.'); + } + + // Verify token and get user info + const userInfo = await verifyMinglarAdminToken(token); + + // Get user details + const user = await prismaService.user.findUnique({ + where: { id: userInfo.id }, + select: { id: true, roleXid: true } + }); + + if (!user) { + throw new ApiError(404, 'User not found'); + } + + // Parse request body + let body: AddSuggestionBody; + + try { + body = event.body ? JSON.parse(event.body) : {}; + } catch (error) { + throw new ApiError(400, 'Invalid JSON in request body'); + } + + const { hostXid } = body; + + + // Add suggestion using service + await minglarService.acceptHostApplication(hostXid, user.id); + + return { + statusCode: 200, + headers: { + 'Content-Type': 'application/json', + 'Access-Control-Allow-Origin': '*', + }, + body: JSON.stringify({ + success: true, + message: 'Application accepted successfully', + data: null, + }), + }; +}); diff --git a/src/modules/minglaradmin/handlers/editAgreementDetails.ts b/src/modules/minglaradmin/handlers/editAgreementDetails.ts new file mode 100644 index 0000000..4f43ae3 --- /dev/null +++ b/src/modules/minglaradmin/handlers/editAgreementDetails.ts @@ -0,0 +1,90 @@ +import { verifyOnlyMinglarAdminToken } from '@/common/middlewares/jwt/authForOnlyMinglarAdmin'; +import { APIGatewayProxyEvent, APIGatewayProxyResult, Context } from 'aws-lambda'; +import { PrismaService } from '../../../common/database/prisma.service'; +import { safeHandler } from '../../../common/utils/handlers/safeHandler'; +import ApiError from '../../../common/utils/helper/ApiError'; +import { MinglarService } from '../services/minglar.service'; + +const prismaService = new PrismaService(); +const minglarService = new MinglarService(prismaService); + +interface assignAMToHostBody { + host_xid: number, + agreementStartDate: string, + duration: number, + isCommisionBase: boolean, + commisionPer: number, + amountPerBooking: number, + durationFrequency: string, + payoutDurationNum: number, + payoutDurationFrequency: string +} + +export const handler = safeHandler(async ( + event: APIGatewayProxyEvent, + context?: Context +): Promise => { + // Verify authentication token + const token = event.headers['x-auth-token'] || event.headers['X-Auth-Token']; + if (!token) { + throw new ApiError(401, 'This is a protected route. Please provide a valid token.'); + } + + // Verify token and get user info + const userInfo = await verifyOnlyMinglarAdminToken(token); + + // Get user details including role + const user = await prismaService.user.findUnique({ + where: { id: userInfo.id }, + select: { id: true, roleXid: true } + }); + + if (!user) { + throw new ApiError(404, 'User not found'); + } + + // Parse request body + let body: assignAMToHostBody; + + try { + body = event.body ? JSON.parse(event.body) : {}; + } catch (error) { + throw new ApiError(400, 'Invalid JSON in request body'); + } + + const { + host_xid, + agreementStartDate, + duration, + isCommisionBase, + commisionPer, + amountPerBooking, + durationFrequency, + payoutDurationNum, + payoutDurationFrequency + } = body; + + await minglarService.editAgreementDetails( + host_xid, + agreementStartDate, + duration, + isCommisionBase, + commisionPer, + amountPerBooking, + durationFrequency, + payoutDurationNum, + payoutDurationFrequency + ); + + return { + statusCode: 200, + headers: { + 'Content-Type': 'application/json', + 'Access-Control-Allow-Origin': '*', + }, + body: JSON.stringify({ + success: true, + message: 'Details edited successfully', + }), + }; +}); diff --git a/src/modules/minglaradmin/services/minglar.service.ts b/src/modules/minglaradmin/services/minglar.service.ts index dcbd692..04ebfb8 100644 --- a/src/modules/minglaradmin/services/minglar.service.ts +++ b/src/modules/minglaradmin/services/minglar.service.ts @@ -5,7 +5,7 @@ import * as bcrypt from 'bcryptjs'; import { z } from 'zod'; import { hostCompanyDetailsSchema } from '../../../common/utils/validation/host/hostCompanyDetails.validation'; import { CreateMinglarDto, UpdateMinglarDto } from '../dto/minglar.dto'; -import { User } from '@prisma/client'; +import { HostHeader, User } from '@prisma/client'; import { ROLE } from '@/common/utils/constants/common.constant'; import { MINGLAR_INVITATION_STATUS, MINGLAR_STATUS_DISPLAY, MINGLAR_STATUS_INTERNAL } from '@/common/utils/constants/minglar.constant'; import { HOST_STATUS_DISPLAY, HOST_STATUS_INTERNAL } from '@/common/utils/constants/host.constant'; @@ -539,7 +539,7 @@ export class MinglarService { async addHostSuggestion(hostXid: number, title: string, comments: string, reviewedByXid: number) { // Check if host exists const hostHeader = await this.prisma.hostHeader.findUnique({ - where: { userXid: hostXid }, + where: { id: hostXid }, select: { id: true } }); console.log(hostHeader) @@ -620,6 +620,51 @@ export class MinglarService { return suggestions; } + async editAgreementDetails( + host_xid: number, + agreementStartDate: string, + duration: number, + isCommisionBase: boolean, + commisionPer: number, + amountPerBooking: number, + durationFrequency: string, + payoutDurationNum: number, + payoutDurationFrequency: string + ) { + return await this.prisma.hostHeader.update({ + where: { id: host_xid }, + data: { + durationNumber: Number(duration), + durationFrequency: durationFrequency, + agreementStartDate: new Date(agreementStartDate), + isCommisionBase: isCommisionBase, + commisionPer: commisionPer ? Number(commisionPer) : null, // Convert to number if exists + amountPerBooking: amountPerBooking ? Number(amountPerBooking) : null, // Convert to number if exists + payoutDurationNum: Number(payoutDurationNum), // Convert to number + payoutDurationFrequency: payoutDurationFrequency + } + }) + } + + async acceptHostApplication(host_xid: number, user_xid: number) { + return await this.prisma.hostHeader.update({ + where: { + id: host_xid, + hostStatusInternal: HOST_STATUS_INTERNAL.HOST_SUBMITTED, + hostStatusDisplay: HOST_STATUS_DISPLAY.UNDER_REVIEW, + adminStatusInternal: MINGLAR_STATUS_INTERNAL.AM_TO_REVIEW, + adminStatusDisplay: MINGLAR_STATUS_DISPLAY.TO_REVIEW + }, + data: { + isApproved: true, + hostStatusInternal: HOST_STATUS_INTERNAL.APPROVED, + hostStatusDisplay: HOST_STATUS_DISPLAY.APPROVED, + adminStatusInternal: MINGLAR_STATUS_INTERNAL.AM_APPROVED, + adminStatusDisplay: MINGLAR_STATUS_DISPLAY.APPROVED + } + }) + } + }