2025-11-10 15:05:01 +05:30
|
|
|
// src/modules/host/services/host.service.ts
|
|
|
|
|
import { Injectable } from '@nestjs/common';
|
|
|
|
|
import { PrismaService } from '../../../common/database/prisma.service';
|
2025-11-12 19:59:54 +05:30
|
|
|
import { AddPaymentDetailsDTO, CreateHostDto, UpdateHostDto } from '../dto/host.dto';
|
2025-11-12 16:03:57 +05:30
|
|
|
import * as bcrypt from 'bcryptjs';
|
|
|
|
|
import ApiError from '../../../common/utils/helper/ApiError';
|
|
|
|
|
import { User } from '@prisma/client';
|
2025-11-13 15:53:35 +05:30
|
|
|
import { z } from 'zod';
|
|
|
|
|
import { hostCompanyDetailsSchema } from '@/common/utils/validation/host/hostCompanyDetails.validation';
|
2025-11-13 17:48:09 +05:30
|
|
|
import { HOST_STATUS_DISPLAY, HOST_STATUS_INTERNAL, STEPPER } from '@/common/utils/constants/host.constant';
|
|
|
|
|
import { MINGLAR_STATUS_DISPLAY, MINGLAR_STATUS_INTERNAL } from '@/common/utils/constants/minglar.constant';
|
|
|
|
|
import { ROLE } from '@/common/utils/constants/common.constant';
|
2025-11-13 15:53:35 +05:30
|
|
|
|
|
|
|
|
type HostCompanyDetailsInput = z.infer<typeof hostCompanyDetailsSchema>;
|
|
|
|
|
|
|
|
|
|
// Document input after S3 upload (with S3 URL as filePath)
|
|
|
|
|
interface HostDocumentInput {
|
|
|
|
|
documentTypeXid: number;
|
|
|
|
|
documentName: string;
|
|
|
|
|
filePath: string; // S3 URL
|
|
|
|
|
}
|
2025-11-10 15:05:01 +05:30
|
|
|
|
|
|
|
|
@Injectable()
|
|
|
|
|
export class HostService {
|
2025-11-12 16:03:57 +05:30
|
|
|
constructor(private prisma: PrismaService) { }
|
2025-11-10 15:05:01 +05:30
|
|
|
|
|
|
|
|
async createHost(data: CreateHostDto) {
|
|
|
|
|
return this.prisma.user.create({ data });
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async getAllHosts() {
|
|
|
|
|
return this.prisma.user.findMany({ where: { roleXid: 3 } });
|
|
|
|
|
}
|
|
|
|
|
|
2025-11-14 14:08:47 +05:30
|
|
|
async getHostIdByUserXid(user_xid: number) {
|
|
|
|
|
const host = await this.prisma.hostHeader.findFirst({
|
|
|
|
|
where: { userXid: user_xid },
|
2025-11-14 15:15:13 +05:30
|
|
|
select: { id: true, companyName: true, countryXid: true, stepper: true },
|
2025-11-14 14:08:47 +05:30
|
|
|
});
|
|
|
|
|
return host;
|
|
|
|
|
}
|
|
|
|
|
|
2025-11-10 15:05:01 +05:30
|
|
|
async getHostById(id: number) {
|
2025-11-14 15:04:01 +05:30
|
|
|
const host = await this.prisma.hostHeader.findFirst({
|
|
|
|
|
where: { userXid: id },
|
2025-11-13 14:59:50 +05:30
|
|
|
include: {
|
2025-11-14 15:04:01 +05:30
|
|
|
hostParent: true,
|
|
|
|
|
HostBankDetails: true,
|
|
|
|
|
HostDocuments: true,
|
|
|
|
|
HostSuggestion: true,
|
|
|
|
|
HostTrack: true,
|
|
|
|
|
}
|
2025-11-13 14:59:50 +05:30
|
|
|
});
|
|
|
|
|
|
2025-11-14 15:04:01 +05:30
|
|
|
if (!host) {
|
|
|
|
|
throw new ApiError(404, 'Host record not found.');
|
2025-11-12 16:03:57 +05:30
|
|
|
}
|
2025-11-13 14:59:50 +05:30
|
|
|
|
2025-11-10 15:05:01 +05:30
|
|
|
return host;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async updateHost(id: number, data: UpdateHostDto) {
|
|
|
|
|
return this.prisma.user.update({
|
|
|
|
|
where: { id },
|
|
|
|
|
data,
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async deleteHost(id: number) {
|
|
|
|
|
return this.prisma.user.delete({ where: { id } });
|
|
|
|
|
}
|
2025-11-12 16:03:57 +05:30
|
|
|
|
|
|
|
|
async getHostByEmail(email: string): Promise<User> {
|
|
|
|
|
return this.prisma.user.findUnique({ where: { emailAddress: email } });
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async verifyHostOtp(email: string, otp: string): Promise<boolean> {
|
|
|
|
|
const user = await this.prisma.user.findUnique({
|
|
|
|
|
where: { emailAddress: email },
|
|
|
|
|
select: {
|
|
|
|
|
id: true,
|
|
|
|
|
emailAddress: true,
|
|
|
|
|
UserOtp: {
|
|
|
|
|
where: { isActive: true, isVerified: false },
|
|
|
|
|
orderBy: { createdAt: 'desc' },
|
|
|
|
|
take: 1,
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
if (!user) {
|
|
|
|
|
throw new ApiError(404, 'User not found.');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const userOtp = user.UserOtp[0];
|
|
|
|
|
|
|
|
|
|
if (!userOtp) {
|
|
|
|
|
throw new ApiError(400, 'No OTP found.');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (new Date() > userOtp.expiresOn) {
|
|
|
|
|
throw new ApiError(400, 'OTP has expired.');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const isMatch = await bcrypt.compare(otp, userOtp.otpCode);
|
|
|
|
|
|
|
|
|
|
if (!isMatch) {
|
|
|
|
|
throw new ApiError(400, 'Invalid OTP.');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
await this.prisma.userOtp.update({
|
|
|
|
|
where: { id: userOtp.id },
|
|
|
|
|
data: {
|
|
|
|
|
isVerified: true,
|
|
|
|
|
verifiedOn: new Date(),
|
|
|
|
|
isActive: false,
|
|
|
|
|
},
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async loginForHost(emailAddress: string, userPassword: string) {
|
|
|
|
|
const existingUser = await this.prisma.user.findUnique({
|
|
|
|
|
where: { emailAddress: emailAddress },
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
if (!existingUser) {
|
|
|
|
|
throw new ApiError(404, 'User not found');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (existingUser.roleXid !== 4) {
|
|
|
|
|
throw new ApiError(403, 'Access denied. Not a host user.');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const matchPassword = await bcrypt.compare(userPassword, existingUser.userPassword);
|
|
|
|
|
if (!matchPassword) {
|
|
|
|
|
throw new ApiError(401, 'Invalid credentials');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return existingUser;
|
|
|
|
|
}
|
|
|
|
|
|
2025-11-13 14:59:50 +05:30
|
|
|
async createMinglarUser(email: string) {
|
2025-11-12 16:03:57 +05:30
|
|
|
const newUser = await this.prisma.user.create({
|
2025-11-13 17:48:09 +05:30
|
|
|
data: { emailAddress: email, roleXid: ROLE.HOST },
|
2025-11-12 16:03:57 +05:30
|
|
|
});
|
|
|
|
|
return newUser;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async createPassword(user_xid: number, password: string): Promise<boolean> {
|
|
|
|
|
// Find user by id
|
|
|
|
|
const user = await this.prisma.user.findUnique({
|
|
|
|
|
where: { id: user_xid },
|
|
|
|
|
select: { id: true, emailAddress: true, userPassword: true },
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
if (!user) {
|
|
|
|
|
throw new ApiError(404, 'User not found');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Check if password already exists
|
|
|
|
|
if (user.userPassword) {
|
|
|
|
|
throw new ApiError(400, 'Password already exists. Use update password instead.');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Hash the password
|
|
|
|
|
const saltRounds = parseInt(process.env.SALT_ROUNDS || '10', 10);
|
|
|
|
|
const hashedPassword = await bcrypt.hash(password, saltRounds);
|
|
|
|
|
|
|
|
|
|
// Update user with hashed password
|
|
|
|
|
await this.prisma.user.update({
|
|
|
|
|
where: { id: user.id },
|
|
|
|
|
data: { userPassword: hashedPassword },
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
2025-11-12 19:59:54 +05:30
|
|
|
|
2025-11-14 14:08:47 +05:30
|
|
|
async addPaymentDetails(data: AddPaymentDetailsDTO): Promise<AddPaymentDetailsDTO> {
|
2025-11-12 19:59:54 +05:30
|
|
|
const addedPaymentDetails = await this.prisma.hostBankDetails.create({
|
|
|
|
|
data,
|
|
|
|
|
});
|
2025-11-14 14:08:47 +05:30
|
|
|
|
2025-11-12 19:59:54 +05:30
|
|
|
if (!addedPaymentDetails) {
|
|
|
|
|
throw new ApiError(400, 'Failed to add payment details');
|
|
|
|
|
}
|
2025-11-14 14:08:47 +05:30
|
|
|
|
2025-11-12 19:59:54 +05:30
|
|
|
return addedPaymentDetails;
|
|
|
|
|
}
|
2025-11-13 15:53:35 +05:30
|
|
|
|
2025-11-17 19:05:26 +05:30
|
|
|
async addOrUpdateCompanyDetails(
|
2025-11-14 14:08:47 +05:30
|
|
|
user_xid: number,
|
2025-11-13 15:53:35 +05:30
|
|
|
companyData: HostCompanyDetailsInput,
|
2025-11-17 19:05:26 +05:30
|
|
|
documents: HostDocumentInput[],
|
|
|
|
|
parentCompanyData?: any | null,
|
|
|
|
|
parentDocuments?: HostDocumentInput[]
|
2025-11-13 15:53:35 +05:30
|
|
|
) {
|
|
|
|
|
return await this.prisma.$transaction(async (tx) => {
|
2025-11-17 19:05:26 +05:30
|
|
|
// Check if host already has a company
|
|
|
|
|
const existingHostCompany = await tx.hostHeader.findFirst({
|
|
|
|
|
where: { userXid: user_xid },
|
|
|
|
|
include: { hostParent: true },
|
2025-11-13 15:53:35 +05:30
|
|
|
});
|
2025-11-17 19:05:26 +05:30
|
|
|
|
|
|
|
|
// CREATE
|
|
|
|
|
if (!existingHostCompany) {
|
|
|
|
|
// Optionally check unique registration number
|
|
|
|
|
const existingByReg = await tx.hostHeader.findFirst({
|
|
|
|
|
where: { registrationNumber: companyData.registrationNumber },
|
|
|
|
|
});
|
|
|
|
|
if (existingByReg) throw new ApiError(400, 'Company already exists with this registration number');
|
|
|
|
|
|
|
|
|
|
const refNumber = await this.generateHostRefNumber(tx);
|
|
|
|
|
|
|
|
|
|
const createdHost = await tx.hostHeader.create({
|
|
|
|
|
data: {
|
|
|
|
|
userXid: user_xid,
|
|
|
|
|
companyName: companyData.companyName,
|
|
|
|
|
hostRefNumber: refNumber,
|
|
|
|
|
address1: companyData.address1,
|
|
|
|
|
address2: companyData.address2,
|
|
|
|
|
cityXid: companyData.cityXid,
|
|
|
|
|
stateXid: companyData.stateXid,
|
|
|
|
|
countryXid: companyData.countryXid,
|
|
|
|
|
pinCode: companyData.pinCode,
|
|
|
|
|
logoPath: companyData.logoPath || null,
|
|
|
|
|
isSubsidairy: companyData.isSubsidairy,
|
|
|
|
|
registrationNumber: companyData.registrationNumber,
|
|
|
|
|
panNumber: companyData.panNumber,
|
|
|
|
|
gstNumber: companyData.gstNumber || null,
|
|
|
|
|
formationDate: new Date(companyData.formationDate),
|
|
|
|
|
companyType: companyData.companyType,
|
|
|
|
|
websiteUrl: companyData.websiteUrl || null,
|
|
|
|
|
instagramUrl: companyData.instagramUrl || null,
|
|
|
|
|
facebookUrl: companyData.facebookUrl || null,
|
|
|
|
|
linkedinUrl: companyData.linkedinUrl || null,
|
|
|
|
|
twitterUrl: companyData.twitterUrl || null,
|
|
|
|
|
currencyXid: companyData.currencyXid,
|
|
|
|
|
stepper: STEPPER.UNDER_REVIEW,
|
|
|
|
|
hostStatusInternal: HOST_STATUS_INTERNAL.HOST_SUBMITTED,
|
|
|
|
|
hostStatusDisplay: HOST_STATUS_DISPLAY.UNDER_REVIEW,
|
|
|
|
|
adminStatusInternal: MINGLAR_STATUS_INTERNAL.ADMIN_TO_REVIEW,
|
|
|
|
|
adminStatusDisplay: MINGLAR_STATUS_DISPLAY.NEW,
|
|
|
|
|
},
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// Create host documents
|
|
|
|
|
if (documents?.length) {
|
|
|
|
|
const docsData = documents.map((doc) => ({
|
|
|
|
|
hostXid: createdHost.id,
|
|
|
|
|
documentTypeXid: doc.documentTypeXid,
|
|
|
|
|
documentName: doc.documentName,
|
|
|
|
|
filePath: doc.filePath,
|
|
|
|
|
}));
|
|
|
|
|
await tx.hostDocuments.createMany({ data: docsData });
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Parent company and its docs (if present)
|
|
|
|
|
if (companyData.isSubsidairy && parentCompanyData) {
|
|
|
|
|
const createdParent = await tx.hostParent.create({
|
|
|
|
|
data: {
|
|
|
|
|
hostXid: createdHost.id,
|
|
|
|
|
companyName: parentCompanyData.companyName,
|
|
|
|
|
address1: parentCompanyData.address1,
|
|
|
|
|
address2: parentCompanyData.address2 || null,
|
|
|
|
|
cityXid: parentCompanyData.cityXid,
|
|
|
|
|
stateXid: parentCompanyData.stateXid,
|
|
|
|
|
countryXid: parentCompanyData.countryXid,
|
|
|
|
|
pinCode: parentCompanyData.pinCode,
|
|
|
|
|
logoPath: parentCompanyData.logoPath || null,
|
|
|
|
|
isSubsidairy: false,
|
|
|
|
|
registrationNumber: parentCompanyData.registrationNumber,
|
|
|
|
|
panNumber: parentCompanyData.panNumber,
|
|
|
|
|
gstNumber: parentCompanyData.gstNumber || null,
|
|
|
|
|
formationDate: new Date(parentCompanyData.formationDate),
|
|
|
|
|
companyType: parentCompanyData.companyType,
|
|
|
|
|
websiteUrl: parentCompanyData.websiteUrl || null,
|
|
|
|
|
instagramUrl: parentCompanyData.instagramUrl || null,
|
|
|
|
|
facebookUrl: parentCompanyData.facebookUrl || null,
|
|
|
|
|
linkedinUrl: parentCompanyData.linkedinUrl || null,
|
|
|
|
|
twitterUrl: parentCompanyData.twitterUrl || null,
|
|
|
|
|
},
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
if (parentDocuments?.length) {
|
|
|
|
|
const parentDocsData = parentDocuments.map((doc) => ({
|
|
|
|
|
hostParentXid: createdParent.id,
|
|
|
|
|
documentTypeXid: doc.documentTypeXid,
|
|
|
|
|
documentName: doc.documentName,
|
|
|
|
|
filePath: doc.filePath,
|
|
|
|
|
}));
|
|
|
|
|
await tx.hostParenetDocuments.createMany({ data: parentDocsData });
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return createdHost;
|
2025-11-13 15:53:35 +05:30
|
|
|
}
|
2025-11-14 14:08:47 +05:30
|
|
|
|
2025-11-17 19:05:26 +05:30
|
|
|
// UPDATE existing
|
|
|
|
|
// Prevent changing hostRefNumber
|
|
|
|
|
const updatedHost = await tx.hostHeader.update({
|
|
|
|
|
where: { id: existingHostCompany.id },
|
2025-11-13 15:53:35 +05:30
|
|
|
data: {
|
|
|
|
|
companyName: companyData.companyName,
|
|
|
|
|
address1: companyData.address1,
|
|
|
|
|
address2: companyData.address2,
|
|
|
|
|
cityXid: companyData.cityXid,
|
|
|
|
|
stateXid: companyData.stateXid,
|
|
|
|
|
countryXid: companyData.countryXid,
|
|
|
|
|
pinCode: companyData.pinCode,
|
2025-11-17 19:05:26 +05:30
|
|
|
logoPath: companyData.logoPath || null,
|
2025-11-13 15:53:35 +05:30
|
|
|
isSubsidairy: companyData.isSubsidairy,
|
|
|
|
|
registrationNumber: companyData.registrationNumber,
|
|
|
|
|
panNumber: companyData.panNumber,
|
2025-11-17 19:05:26 +05:30
|
|
|
gstNumber: companyData.gstNumber || null,
|
2025-11-13 15:53:35 +05:30
|
|
|
formationDate: new Date(companyData.formationDate),
|
|
|
|
|
companyType: companyData.companyType,
|
2025-11-17 19:05:26 +05:30
|
|
|
websiteUrl: companyData.websiteUrl || null,
|
|
|
|
|
instagramUrl: companyData.instagramUrl || null,
|
|
|
|
|
facebookUrl: companyData.facebookUrl || null,
|
|
|
|
|
linkedinUrl: companyData.linkedinUrl || null,
|
|
|
|
|
twitterUrl: companyData.twitterUrl || null,
|
2025-11-13 15:53:35 +05:30
|
|
|
currencyXid: companyData.currencyXid,
|
2025-11-17 19:05:26 +05:30
|
|
|
// hostRefNumber: DO NOT UPDATE
|
2025-11-13 15:53:35 +05:30
|
|
|
},
|
|
|
|
|
});
|
2025-11-14 14:08:47 +05:30
|
|
|
|
2025-11-17 19:05:26 +05:30
|
|
|
// Replace host documents (delete old, insert new)
|
|
|
|
|
await tx.hostDocuments.deleteMany({ where: { hostXid: updatedHost.id } });
|
|
|
|
|
if (documents?.length) {
|
2025-11-13 15:53:35 +05:30
|
|
|
const docsData = documents.map((doc) => ({
|
2025-11-17 19:05:26 +05:30
|
|
|
hostXid: updatedHost.id,
|
2025-11-13 15:53:35 +05:30
|
|
|
documentTypeXid: doc.documentTypeXid,
|
|
|
|
|
documentName: doc.documentName,
|
|
|
|
|
filePath: doc.filePath,
|
|
|
|
|
}));
|
|
|
|
|
await tx.hostDocuments.createMany({ data: docsData });
|
|
|
|
|
}
|
2025-11-14 14:08:47 +05:30
|
|
|
|
2025-11-17 19:05:26 +05:30
|
|
|
// Parent company create/update and replace parent docs
|
|
|
|
|
if (companyData.isSubsidairy) {
|
|
|
|
|
// existingHostCompany.hostParent may be array or single object depending on Prisma schema
|
|
|
|
|
let parentRecord = (existingHostCompany as any).hostParent;
|
|
|
|
|
if (Array.isArray(parentRecord)) parentRecord = parentRecord[0];
|
|
|
|
|
|
|
|
|
|
if (!parentRecord) {
|
|
|
|
|
// create
|
|
|
|
|
const createdParent = await tx.hostParent.create({
|
|
|
|
|
data: {
|
|
|
|
|
hostXid: updatedHost.id,
|
|
|
|
|
companyName: parentCompanyData.companyName,
|
|
|
|
|
address1: parentCompanyData.address1,
|
|
|
|
|
address2: parentCompanyData.address2 || null,
|
|
|
|
|
cityXid: parentCompanyData.cityXid,
|
|
|
|
|
stateXid: parentCompanyData.stateXid,
|
|
|
|
|
countryXid: parentCompanyData.countryXid,
|
|
|
|
|
pinCode: parentCompanyData.pinCode,
|
|
|
|
|
logoPath: parentCompanyData.logoPath || null,
|
|
|
|
|
isSubsidairy: false,
|
|
|
|
|
registrationNumber: parentCompanyData.registrationNumber,
|
|
|
|
|
panNumber: parentCompanyData.panNumber,
|
|
|
|
|
gstNumber: parentCompanyData.gstNumber || null,
|
|
|
|
|
formationDate: new Date(parentCompanyData.formationDate),
|
|
|
|
|
companyType: parentCompanyData.companyType,
|
|
|
|
|
websiteUrl: parentCompanyData.websiteUrl || null,
|
|
|
|
|
instagramUrl: parentCompanyData.instagramUrl || null,
|
|
|
|
|
facebookUrl: parentCompanyData.facebookUrl || null,
|
|
|
|
|
linkedinUrl: parentCompanyData.linkedinUrl || null,
|
|
|
|
|
twitterUrl: parentCompanyData.twitterUrl || null,
|
|
|
|
|
},
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
if (parentDocuments?.length) {
|
|
|
|
|
const parentDocsData = parentDocuments.map((doc) => ({
|
|
|
|
|
hostParentXid: createdParent.id,
|
|
|
|
|
documentTypeXid: doc.documentTypeXid,
|
|
|
|
|
documentName: doc.documentName,
|
|
|
|
|
filePath: doc.filePath,
|
|
|
|
|
}));
|
|
|
|
|
await tx.hostParenetDocuments.createMany({ data: parentDocsData });
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
// update
|
|
|
|
|
await tx.hostParent.update({
|
|
|
|
|
where: { id: parentRecord.id },
|
|
|
|
|
data: {
|
|
|
|
|
companyName: parentCompanyData.companyName,
|
|
|
|
|
address1: parentCompanyData.address1,
|
|
|
|
|
address2: parentCompanyData.address2 || null,
|
|
|
|
|
cityXid: parentCompanyData.cityXid,
|
|
|
|
|
stateXid: parentCompanyData.stateXid,
|
|
|
|
|
countryXid: parentCompanyData.countryXid,
|
|
|
|
|
pinCode: parentCompanyData.pinCode,
|
|
|
|
|
logoPath: parentCompanyData.logoPath || null,
|
|
|
|
|
registrationNumber: parentCompanyData.registrationNumber,
|
|
|
|
|
panNumber: parentCompanyData.panNumber,
|
|
|
|
|
gstNumber: parentCompanyData.gstNumber || null,
|
|
|
|
|
formationDate: new Date(parentCompanyData.formationDate),
|
|
|
|
|
companyType: parentCompanyData.companyType,
|
|
|
|
|
websiteUrl: parentCompanyData.websiteUrl || null,
|
|
|
|
|
instagramUrl: parentCompanyData.instagramUrl || null,
|
|
|
|
|
facebookUrl: parentCompanyData.facebookUrl || null,
|
|
|
|
|
linkedinUrl: parentCompanyData.linkedinUrl || null,
|
|
|
|
|
twitterUrl: parentCompanyData.twitterUrl || null,
|
|
|
|
|
},
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// replace parent docs
|
|
|
|
|
await tx.hostParenetDocuments.deleteMany({ where: { hostParentXid: parentRecord.id } });
|
|
|
|
|
if (parentDocuments?.length) {
|
|
|
|
|
const parentDocsData = parentDocuments.map((doc) => ({
|
|
|
|
|
hostParentXid: parentRecord.id,
|
|
|
|
|
documentTypeXid: doc.documentTypeXid,
|
|
|
|
|
documentName: doc.documentName,
|
|
|
|
|
filePath: doc.filePath,
|
|
|
|
|
}));
|
|
|
|
|
await tx.hostParenetDocuments.createMany({ data: parentDocsData });
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
// If previously had a parent and now isSubsidairy=false -> optionally delete parent and its docs
|
|
|
|
|
const previousParent = (existingHostCompany as any).hostParent;
|
|
|
|
|
let prevParentId = null;
|
|
|
|
|
if (Array.isArray(previousParent) && previousParent.length) prevParentId = previousParent[0].id;
|
|
|
|
|
else if (previousParent && previousParent.id) prevParentId = previousParent.id;
|
|
|
|
|
|
|
|
|
|
if (prevParentId) {
|
|
|
|
|
await tx.hostParenetDocuments.deleteMany({ where: { hostParentXid: prevParentId } });
|
|
|
|
|
await tx.hostParent.delete({ where: { id: prevParentId } });
|
2025-11-13 17:48:09 +05:30
|
|
|
}
|
|
|
|
|
}
|
2025-11-14 14:08:47 +05:30
|
|
|
|
2025-11-17 19:05:26 +05:30
|
|
|
return updatedHost;
|
2025-11-13 15:53:35 +05:30
|
|
|
});
|
|
|
|
|
}
|
2025-11-14 14:08:47 +05:30
|
|
|
|
2025-11-17 19:05:26 +05:30
|
|
|
|
2025-11-17 15:28:22 +05:30
|
|
|
async generateHostRefNumber(tx: any) {
|
|
|
|
|
const lastHost = await tx.hostHeader.findFirst({
|
|
|
|
|
orderBy: {
|
|
|
|
|
id: 'desc',
|
|
|
|
|
},
|
|
|
|
|
select: {
|
|
|
|
|
id: true,
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
const nextId = lastHost ? lastHost.id + 1 : 1;
|
|
|
|
|
return `HOSTREFNO-${String(nextId).padStart(6, '0')}`;
|
|
|
|
|
}
|
|
|
|
|
|
2025-11-19 16:55:54 +05:30
|
|
|
async createOrUpdateHeader(
|
|
|
|
|
activityXid: number,
|
|
|
|
|
pqqQuestionXid: number,
|
|
|
|
|
pqqAnswerXid: number,
|
|
|
|
|
comments: string | null
|
|
|
|
|
) {
|
|
|
|
|
// find existing header
|
|
|
|
|
const existing = await this.prisma.activityPQQheader.findFirst({
|
|
|
|
|
where: { activityXid, pqqQuestionXid, deletedAt: null }
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
if (!existing) {
|
|
|
|
|
return await this.prisma.activityPQQheader.create({
|
|
|
|
|
data: {
|
|
|
|
|
activityXid,
|
|
|
|
|
pqqQuestionXid,
|
|
|
|
|
pqqAnswerXid,
|
|
|
|
|
comments
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// mark old supportings deleted
|
|
|
|
|
await this.prisma.activityPQQSupportings.updateMany({
|
|
|
|
|
where: { activityPqqHeaderXid: existing.id },
|
|
|
|
|
data: {
|
|
|
|
|
isActive: false,
|
|
|
|
|
deletedAt: new Date()
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// update header
|
|
|
|
|
return await this.prisma.activityPQQheader.update({
|
|
|
|
|
where: { id: existing.id },
|
|
|
|
|
data: {
|
|
|
|
|
pqqAnswerXid,
|
|
|
|
|
comments
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async addSupportingFile(
|
|
|
|
|
headerId: number,
|
|
|
|
|
mimeType: string,
|
|
|
|
|
fileUrl: string
|
|
|
|
|
) {
|
|
|
|
|
return await this.prisma.activityPQQSupportings.create({
|
|
|
|
|
data: {
|
|
|
|
|
activityPqqHeaderXid: headerId,
|
|
|
|
|
mediaType: mimeType,
|
|
|
|
|
mediaFileName: fileUrl
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
2025-11-10 15:05:01 +05:30
|
|
|
}
|