diff --git a/prisma/schema.prisma b/prisma/schema.prisma index 54ccbad..cc26201 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -648,23 +648,23 @@ model HostHeader { userXid Int @map("user_xid") user User @relation("HostUser", fields: [userXid], references: [id], onDelete: Cascade) companyName String @map("company_name") @db.VarChar(100) - hostRefNumber String? @map("host_ref_number") @db.VarChar(30) - address1 String? @map("address_1") @db.VarChar(150) + hostRefNumber String? @map("host_ref_number") @db.VarChar(30) + address1 String? @map("address_1") @db.VarChar(150) address2 String? @map("address_2") @db.VarChar(150) - 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) + 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") @db.VarChar(30) + pinCode String? @map("pin_code") @db.VarChar(30) logoPath String? @map("logo_path") @db.VarChar(400) - isSubsidairy Boolean? @default(false) @map("is_subsidairy") - registrationNumber String? @map("registration_number") @db.VarChar(30) - panNumber String? @map("pan_number") @db.VarChar(30) + isSubsidairy Boolean? @default(false) @map("is_subsidairy") + registrationNumber String? @map("registration_number") @db.VarChar(30) + panNumber String? @map("pan_number") @db.VarChar(30) gstNumber String? @map("gst_number") @db.VarChar(30) - formationDate DateTime? @map("formation_date") - companyType String? @map("company_type") @db.VarChar(30) + formationDate DateTime? @map("formation_date") + companyType String? @map("company_type") @db.VarChar(30) websiteUrl String? @map("website_url") @db.VarChar(50) instagramUrl String? @map("instagram_url") @db.VarChar(80) facebookUrl String? @map("facebook_url") @db.VarChar(80) @@ -673,16 +673,16 @@ model HostHeader { 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") @db.VarChar(20) - hostStatusDisplay String? @default("pending") @map("host_status_Display") @db.VarChar(20) - adminStatusInternal String? @default("pending") @map("admin_status_internal") @db.VarChar(20) - adminStatusDisplay String? @default("pending") @map("admin_status_display") @db.VarChar(20) + hostStatusInternal String? @default("pending") @map("host_status_internal") @db.VarChar(20) + hostStatusDisplay String? @default("pending") @map("host_status_Display") @db.VarChar(20) + adminStatusInternal String? @default("pending") @map("admin_status_internal") @db.VarChar(20) + adminStatusDisplay String? @default("pending") @map("admin_status_display") @db.VarChar(20) amStatus String? @default("pending") @map("am_status") @db.VarChar(20) - agreementAccepted Boolean? @default(false) @map("agreement_accepted") + agreementAccepted Boolean? @default(false) @map("agreement_accepted") accountManagerXid Int? @map("account_manager_xid") accountManager User? @relation("AccountManager", fields: [accountManagerXid], references: [id], onDelete: Restrict) assignedOn DateTime? @map("assigned_on") - isApproved Boolean? @default(false) @map("is_approved") + isApproved Boolean? @default(false) @map("is_approved") agreementStartDate DateTime? @map("agreement_start_date") durationNumber Int? @map("duration_number") durationFrequency String? @map("duration_frequency") @db.VarChar(20) @@ -770,22 +770,22 @@ model HostParent { hostXid Int @map("host_xid") host HostHeader @relation(fields: [hostXid], references: [id], onDelete: Cascade) companyName String @map("company_name") @db.VarChar(50) - address1 String @map("address_1") @db.VarChar(150) + address1 String? @map("address_1") @db.VarChar(150) address2 String? @map("address_2") @db.VarChar(150) - 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") @db.VarChar(30) + 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") @db.VarChar(30) logoPath String? @map("logo_path") @db.VarChar(400) isSubsidairy Boolean @default(false) @map("is_subsidairy") - registrationNumber String @map("registration_number") @db.VarChar(30) - panNumber String @map("pan_number") @db.VarChar(30) + registrationNumber String? @map("registration_number") @db.VarChar(30) + panNumber String? @map("pan_number") @db.VarChar(30) gstNumber String? @map("gst_number") @db.VarChar(30) - formationDate DateTime @map("formation_date") - companyType String @map("company_type") @db.VarChar(30) + formationDate DateTime? @map("formation_date") + companyType String? @map("company_type") @db.VarChar(30) websiteUrl String? @map("website_url") @db.VarChar(80) instagramUrl String? @map("instagram_url") @db.VarChar(80) facebookUrl String? @map("facebook_url") @db.VarChar(80) diff --git a/src/modules/host/handlers/Host_Admin/onboarding/submitCompanyDetails.ts b/src/modules/host/handlers/Host_Admin/onboarding/submitCompanyDetails.ts index 7f22123..27b5c44 100644 --- a/src/modules/host/handlers/Host_Admin/onboarding/submitCompanyDetails.ts +++ b/src/modules/host/handlers/Host_Admin/onboarding/submitCompanyDetails.ts @@ -26,9 +26,9 @@ function normalizeJsonField(fields: any, key: string) { if (!fields[key]) return undefined; const val = fields[key]; - if (typeof val === "object") return val; + if (typeof val === 'object') return val; - if (typeof val === "string") { + if (typeof val === 'string') { try { return JSON.parse(val); } catch (err) { @@ -39,12 +39,12 @@ function normalizeJsonField(fields: any, key: string) { } function cleanEmptyStrings(obj: any) { - if (!obj || typeof obj !== "object") return obj; + if (!obj || typeof obj !== 'object') return obj; const cleaned: any = {}; for (const key of Object.keys(obj)) { - if (obj[key] === "") cleaned[key] = undefined; - else if (typeof obj[key] === "object") cleaned[key] = cleanEmptyStrings(obj[key]); + if (obj[key] === '') cleaned[key] = undefined; + else if (typeof obj[key] === 'object') cleaned[key] = cleanEmptyStrings(obj[key]); else cleaned[key] = obj[key]; } return cleaned; @@ -104,7 +104,7 @@ export const handler = safeHandler(async (event: APIGatewayProxyEvent): Promise< file.on('error', (error) => reject(new ApiError(400, `File upload error: ${error.message}`))); }); - bb.on('field', (fieldname, val) => fields[fieldname] = val); + bb.on('field', (fieldname, val) => (fields[fieldname] = val)); bb.on('close', () => resolve()); bb.on('error', (error) => reject(new ApiError(400, `Multipart parsing error: ${error.message}`))); @@ -113,11 +113,11 @@ export const handler = safeHandler(async (event: APIGatewayProxyEvent): Promise< }); /** 4) Extract and clean isDraft flag */ - const isDraft = fields.isDraft === "true" || fields.isDraft === true; + const isDraft = fields.isDraft === 'true' || fields.isDraft === true; /** 5) PROCESS companyDetails ONCE ONLY (IMPORTANT FIX) */ - let companyDetailsRaw = normalizeJsonField(fields, "companyDetails"); - if (!companyDetailsRaw) throw new ApiError(400, "companyDetails is required."); + let companyDetailsRaw = normalizeJsonField(fields, 'companyDetails'); + if (!companyDetailsRaw) throw new ApiError(400, 'companyDetails is required.'); if (isDraft) { companyDetailsRaw = cleanEmptyStrings(companyDetailsRaw); @@ -128,11 +128,9 @@ export const handler = safeHandler(async (event: APIGatewayProxyEvent): Promise< } } - - /** 6) Profile update if provided */ if (fields.userProfile) { - const userProfileRaw = normalizeJsonField(fields, "userProfile"); + const userProfileRaw = normalizeJsonField(fields, 'userProfile'); if (userProfileRaw) { const { firstName, lastName, mobileNumber } = userProfileRaw; @@ -153,20 +151,20 @@ export const handler = safeHandler(async (event: APIGatewayProxyEvent): Promise< if (!isDraft) { const validate = hostCompanyDetailsSchema.safeParse(companyDetailsRaw); if (!validate.success) { - const message = validate.error.issues.map(i => i.message).join(', '); + const message = validate.error.issues.map((i) => i.message).join(', '); throw new ApiError(400, `Validation failed: ${message}`); } parsedCompany = validate.data; } /** 8) DOCUMENT METADATA */ - const documentsMetadataRaw = normalizeJsonField(fields, "documents"); - if (!Array.isArray(documentsMetadataRaw)) throw new ApiError(400, "documents must be an array."); + const documentsMetadataRaw = normalizeJsonField(fields, 'documents'); + if (!Array.isArray(documentsMetadataRaw)) throw new ApiError(400, 'documents must be an array.'); if (!isDraft) { const docsParse = hostDocumentsSchema.safeParse(documentsMetadataRaw); if (!docsParse.success) { - const message = docsParse.error.issues.map(i => i.message).join(', '); + const message = docsParse.error.issues.map((i) => i.message).join(', '); throw new ApiError(400, `Documents validation failed: ${message}`); } } @@ -177,7 +175,7 @@ export const handler = safeHandler(async (event: APIGatewayProxyEvent): Promise< })); const documentMetadata = documentsMetadata.map((doc: any) => { - const file = files.find(f => f.fieldName === doc.fieldName); + const file = files.find((f) => f.fieldName === doc.fieldName); // In DRAFT mode → allow missing documents if (isDraft && !file) { @@ -192,10 +190,9 @@ export const handler = safeHandler(async (event: APIGatewayProxyEvent): Promise< return { ...doc, file }; }); - /** 9) SPLIT host & parent docs */ - const hostDocs = documentMetadata.filter(d => d.owner === 'host'); - const parentDocs = documentMetadata.filter(d => d.owner === 'parent'); + const hostDocs = documentMetadata.filter((d) => d.owner === 'host'); + const parentDocs = documentMetadata.filter((d) => d.owner === 'parent'); /** 10) VALIDATE PARENT COMPANY (ONLY IN FINAL SUBMISSION) */ let parsedParentCompany: any = null; @@ -207,7 +204,7 @@ export const handler = safeHandler(async (event: APIGatewayProxyEvent): Promise< const parentCheck = parentCompanySchema.safeParse(parsedCompany.parentCompany); if (!parentCheck.success) { - const message = parentCheck.error.issues.map(i => i.message).join(', '); + const message = parentCheck.error.issues.map((i) => i.message).join(', '); throw new ApiError(400, `Parent company validation failed: ${message}`); } @@ -218,33 +215,39 @@ export const handler = safeHandler(async (event: APIGatewayProxyEvent): Promise< /** 11) UPLOAD DOCUMENTS */ async function uploadToS3(buffer, mimeType, originalName, folderType, documentTypeXid?, fieldName?) { - const sanitizeFileName = (name: string) => - name.toLowerCase().replace(/[^a-z0-9.]/g, '_').replace(/_+/g, '_'); + const ext = originalName.split('.').pop() || 'jpg'; - const ext = originalName.split('.').pop() || 'pdf'; + let s3Key = ''; - let s3Key = ""; if (folderType === 'logo') { - s3Key = `Documents/Host/${userInfo.id}/logo/${sanitizeFileName(originalName)}`; - } else if (folderType === 'documents') { - s3Key = `Documents/Host/${userInfo.id}/documents/${sanitizeFileName(`${documentTypeXid}_${fieldName}.${ext}`)}`; - } else if (folderType === 'parent_company') { - s3Key = `Documents/Host/${userInfo.id}/parent_company/${sanitizeFileName(`${documentTypeXid}_${fieldName}.${ext}`)}`; + s3Key = `Documents/Host/${userInfo.id}/logo/company_logo.${ext}`; + } + else if (folderType === 'parent_company_logo') { + s3Key = `Documents/Host/${userInfo.id}/parent_company/logo/parent_company_logo.${ext}`; + } + else if (folderType === 'documents') { + s3Key = `Documents/Host/${userInfo.id}/documents/${documentTypeXid}_${fieldName}.${ext}`; + } + else if (folderType === 'parent_company') { + s3Key = `Documents/Host/${userInfo.id}/parent_company/${documentTypeXid}_${fieldName}.${ext}`; } - await s3.upload({ - Bucket: config.aws.bucketName, - Key: s3Key, - Body: buffer, - ContentType: mimeType, - ACL: 'private' - }).promise(); + await s3 + .upload({ + Bucket: config.aws.bucketName, + Key: s3Key, + Body: buffer, + ContentType: mimeType, + ACL: 'private', + }) + .promise(); return `https://${config.aws.bucketName}.s3.${config.aws.region}.amazonaws.com/${s3Key}`; } + /** Upload host docs */ - const uploadedHostDocs = []; + const uploadedHostDocs: Array = []; for (const doc of hostDocs) { if (isDraft && !doc.file) continue; @@ -254,7 +257,7 @@ export const handler = safeHandler(async (event: APIGatewayProxyEvent): Promise< doc.file.fileName, 'documents', doc.documentTypeXid, - doc.fieldName + doc.fieldName, ); uploadedHostDocs.push({ @@ -264,9 +267,8 @@ export const handler = safeHandler(async (event: APIGatewayProxyEvent): Promise< }); } - /** Upload parent docs */ - const uploadedParentDocs = []; + const uploadedParentDocs: Array = []; for (const doc of parentDocs) { if (!doc.file && isDraft) continue; // skip missing files in draft mode @@ -276,7 +278,7 @@ export const handler = safeHandler(async (event: APIGatewayProxyEvent): Promise< doc.file.fileName, 'parent_company', doc.documentTypeXid, - doc.fieldName + doc.fieldName, ); uploadedParentDocs.push({ @@ -286,6 +288,31 @@ export const handler = safeHandler(async (event: APIGatewayProxyEvent): Promise< }); } + /** UPLOAD LOGO (if provided) */ + const logoFile = files.find((f) => f.fieldName === 'companyLogo' || f.fieldName === 'companyLogoFile'); + if (logoFile) { + const logoUrl = await uploadToS3(logoFile.buffer, logoFile.mimeType, logoFile.fileName, 'logo'); + parsedCompany.logoPath = logoUrl; + } + + /** UPLOAD PARENT COMPANY LOGO (if provided) */ + const parentLogoFile = files.find((f) => f.fieldName === 'parentCompanyLogo'); + if (parentLogoFile) { + const parentLogoUrl = await uploadToS3( + parentLogoFile.buffer, + parentLogoFile.mimeType, + parentLogoFile.fileName, + 'parent_company_logo', + ); + + if (parsedParentCompany) { + parsedParentCompany.logoPath = parentLogoUrl; + } else { + // if no parent object exists yet (drafts or other flows), attach it safely + parsedParentCompany = parsedParentCompany || {}; + parsedParentCompany.logoPath = parentLogoUrl; + } + } /** 12) SAVE / UPDATE HOST ENTRY */ const createdOrUpdated = await hostService.addOrUpdateCompanyDetails( @@ -294,7 +321,7 @@ export const handler = safeHandler(async (event: APIGatewayProxyEvent): Promise< uploadedHostDocs, parsedParentCompany, uploadedParentDocs, - isDraft + isDraft, ); if (!createdOrUpdated) throw new ApiError(400, 'Failed to add/update company details.'); @@ -308,14 +335,14 @@ export const handler = safeHandler(async (event: APIGatewayProxyEvent): Promise< details.hostDetails.accountManager.emailAddress, details.hostDetails.accountManager.firstName, details.hostDetails.companyName, - details.hostDetails.hostRefNumber + details.hostDetails.hostRefNumber, ); } else { await sendEmailToMinglarAdmin( config.MinglarAdminEmail, config.MinglarAdminName, details.hostDetails.companyName, - details.hostDetails.hostRefNumber + details.hostDetails.hostRefNumber, ); } } @@ -329,17 +356,17 @@ export const handler = safeHandler(async (event: APIGatewayProxyEvent): Promise< }, body: JSON.stringify({ success: true, - message: isDraft ? "Company details saved as draft successfully." : "Company details uploaded successfully.", + message: isDraft ? 'Company details saved as draft successfully.' : 'Company details uploaded successfully.', data: { id: createdOrUpdated.id, hostRefNumber: createdOrUpdated.hostRefNumber, - isDraft - } + isDraft, + }, }), }; } catch (error: any) { - console.error("❌ Error in addCompanyDetails:", error); + console.error('❌ Error in addCompanyDetails:', error); throw error; } }); diff --git a/src/modules/host/services/host.service.ts b/src/modules/host/services/host.service.ts index cf9db39..3373c39 100644 --- a/src/modules/host/services/host.service.ts +++ b/src/modules/host/services/host.service.ts @@ -63,15 +63,27 @@ export class HostService { const host = await this.prisma.hostHeader.findFirst({ where: { userXid: id }, include: { - hostParent: true, + hostParent: { + include: { + HostParenetDocuments: { + select: { + id: true, + filePath: true, + documentName: true, + documentTypeXid: true, + documentType: true + } + } + } + }, HostBankDetails: true, HostDocuments: { include: { documentType: true, }, }, - user:{ - select:{ + user: { + select: { id: true, emailAddress: true, firstName: true, @@ -96,8 +108,9 @@ export class HostService { return { stepper: STEPPER.NOT_SUBMITTED } as any; } + const bucket = config.aws.bucketName; + if (host.HostDocuments?.length) { - const bucket = config.aws.bucketName; for (const doc of host.HostDocuments) { if (doc.filePath) { @@ -113,6 +126,40 @@ export class HostService { } } + if (host.logoPath) { + const key = host.logoPath.startsWith('http') + ? host.logoPath.split('.com/')[1] + : host.logoPath; + + host.logoPath = await getPresignedUrl(bucket, key); + } + + if (host.hostParent?.length) { + const parent = host.hostParent[0]; // since you allow only 1 parent + + // Parent company logo + if (parent.logoPath) { + const key = parent.logoPath.startsWith("http") + ? parent.logoPath.split(".com/")[1] + : parent.logoPath; + + parent.logoPath = await getPresignedUrl(bucket, key); + } + + // Parent documents + if (parent.HostParenetDocuments?.length) { + for (const doc of parent.HostParenetDocuments) { + if (doc.filePath) { + const key = doc.filePath.startsWith("http") + ? doc.filePath.split(".com/")[1] + : doc.filePath; + + (doc as any).presignedUrl = await getPresignedUrl(bucket, key); + } + } + } + } + return host; } @@ -337,7 +384,7 @@ export class HostService { documents: HostDocumentInput[], parentCompanyData?: any | null, parentDocuments?: HostDocumentInput[], - isDraft: boolean = false, // Add isDraft parameter with default false + isDraft: boolean = false, ) { return await this.prisma.$transaction(async (tx) => { // Check if host already has a company @@ -363,9 +410,7 @@ export class HostService { ? MINGLAR_STATUS_DISPLAY.DRAFT : MINGLAR_STATUS_DISPLAY.NEW; - const stepper = isDraft - ? STEPPER.NOT_SUBMITTED - : STEPPER.UNDER_REVIEW; + const stepper = isDraft ? STEPPER.NOT_SUBMITTED : STEPPER.UNDER_REVIEW; // CREATE if (!existingHostCompany) { @@ -375,10 +420,7 @@ export class HostService { where: { panNumber: companyData.panNumber }, }); if (existingByPan) - throw new ApiError( - 400, - 'Company already exists with this pan/bin number', - ); + throw new ApiError(400, 'Company already exists with this pan/bin number'); } const refNumber = await this.generateHostRefNumber(tx); @@ -390,17 +432,16 @@ export class HostService { hostRefNumber: refNumber, address1: companyData.address1, address2: companyData.address2, - cities: { connect: { id: companyData.cityXid } }, - states: { connect: { id: companyData.stateXid } }, - countries: { connect: { id: companyData.countryXid } }, + cities: companyData.cityXid ? { connect: { id: companyData.cityXid } } : undefined, + states: companyData.stateXid ? { connect: { id: companyData.stateXid } } : undefined, + countries: companyData.countryXid ? { connect: { id: companyData.countryXid } } : undefined, 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), - formationDate: companyData.formationDate ? new Date(companyData.formationDate) : null, + formationDate: companyData.formationDate ? new Date(companyData.formationDate as any) : null, companyType: companyData.companyType, websiteUrl: companyData.websiteUrl || null, instagramUrl: companyData.instagramUrl || null, @@ -415,7 +456,7 @@ export class HostService { }, }); - // Create host documents + // Create host documents (initial insert) if (documents?.length) { const docsData = documents.map((doc) => ({ hostXid: createdHost.id, @@ -432,20 +473,19 @@ export class HostService { data: { hostXid: createdHost.id, companyName: parentCompanyData.companyName, - address1: parentCompanyData.address1, + address1: parentCompanyData.address1 || null, address2: parentCompanyData.address2 || null, - cityXid: parentCompanyData.cityXid, - stateXid: parentCompanyData.stateXid, - countryXid: parentCompanyData.countryXid, - pinCode: parentCompanyData.pinCode, + cityXid: parentCompanyData.cityXid || null, + stateXid: parentCompanyData.stateXid || null, + countryXid: parentCompanyData.countryXid || null, + pinCode: parentCompanyData.pinCode || null, logoPath: parentCompanyData.logoPath || null, isSubsidairy: false, - registrationNumber: parentCompanyData.registrationNumber, - panNumber: parentCompanyData.panNumber, + registrationNumber: parentCompanyData.registrationNumber || null, + panNumber: parentCompanyData.panNumber || null, gstNumber: parentCompanyData.gstNumber || null, - // formationDate: new Date(parentCompanyData.formationDate), - formationDate: parentCompanyData.formationDate ? new Date(parentCompanyData.formationDate) : null, - companyType: parentCompanyData.companyType, + formationDate: parentCompanyData.formationDate ? new Date(parentCompanyData.formationDate as any) : null, + companyType: parentCompanyData.companyType || null, websiteUrl: parentCompanyData.websiteUrl || null, instagramUrl: parentCompanyData.instagramUrl || null, facebookUrl: parentCompanyData.facebookUrl || null, @@ -475,16 +515,16 @@ export class HostService { companyName: companyData.companyName, address1: companyData.address1, address2: companyData.address2, - cities: { connect: { id: companyData.cityXid } }, - states: { connect: { id: companyData.stateXid } }, - countries: { connect: { id: companyData.countryXid } }, + cities: companyData.cityXid ? { connect: { id: companyData.cityXid } } : undefined, + states: companyData.stateXid ? { connect: { id: companyData.stateXid } } : undefined, + countries: companyData.countryXid ? { connect: { id: companyData.countryXid } } : undefined, pinCode: companyData.pinCode, logoPath: companyData.logoPath || null, isSubsidairy: companyData.isSubsidairy, registrationNumber: companyData.registrationNumber, panNumber: companyData.panNumber, gstNumber: companyData.gstNumber || null, - formationDate: companyData.formationDate ? new Date(companyData.formationDate) : null, + formationDate: companyData.formationDate ? new Date(companyData.formationDate as any) : null, companyType: companyData.companyType, websiteUrl: companyData.websiteUrl || null, instagramUrl: companyData.instagramUrl || null, @@ -499,19 +539,39 @@ export class HostService { }, }); - // Replace host documents (delete old, insert new) - await tx.hostDocuments.deleteMany({ where: { hostXid: updatedHost.id } }); + // REPLACE/UPSERT host documents by documentTypeXid (Option A) if (documents?.length) { - const docsData = documents.map((doc) => ({ - hostXid: updatedHost.id, - documentTypeXid: doc.documentTypeXid, - documentName: doc.documentName, - filePath: doc.filePath, - })); - await tx.hostDocuments.createMany({ data: docsData }); + for (const doc of documents) { + const existingDoc = await tx.hostDocuments.findFirst({ + where: { + hostXid: updatedHost.id, + documentTypeXid: doc.documentTypeXid, + }, + }); + + if (existingDoc) { + // update only filePath (and name if required) + await tx.hostDocuments.update({ + where: { id: existingDoc.id }, + data: { + filePath: doc.filePath, + documentName: doc.documentName || existingDoc.documentName, + }, + }); + } else { + await tx.hostDocuments.create({ + data: { + hostXid: updatedHost.id, + documentTypeXid: doc.documentTypeXid, + documentName: doc.documentName, + filePath: doc.filePath, + }, + }); + } + } } - // Parent company create/update and replace parent docs + // Parent company create/update and replace parent docs by documentTypeXid if (companyData.isSubsidairy) { let parentRecord = (existingHostCompany as any).hostParent; if (Array.isArray(parentRecord)) parentRecord = parentRecord[0]; @@ -522,19 +582,19 @@ export class HostService { data: { hostXid: updatedHost.id, companyName: parentCompanyData.companyName, - address1: parentCompanyData.address1, + address1: parentCompanyData.address1 || null, address2: parentCompanyData.address2 || null, - cityXid: parentCompanyData.cityXid, - stateXid: parentCompanyData.stateXid, - countryXid: parentCompanyData.countryXid, - pinCode: parentCompanyData.pinCode, + cityXid: parentCompanyData.cityXid || null, + stateXid: parentCompanyData.stateXid || null, + countryXid: parentCompanyData.countryXid || null, + pinCode: parentCompanyData.pinCode || null, logoPath: parentCompanyData.logoPath || null, isSubsidairy: false, - registrationNumber: parentCompanyData.registrationNumber, - panNumber: parentCompanyData.panNumber, + registrationNumber: parentCompanyData.registrationNumber || null, + panNumber: parentCompanyData.panNumber || null, gstNumber: parentCompanyData.gstNumber || null, - formationDate: parentCompanyData.formationDate ? new Date(parentCompanyData.formationDate) : null, - companyType: parentCompanyData.companyType, + formationDate: parentCompanyData.formationDate ? new Date(parentCompanyData.formationDate as any) : null, + companyType: parentCompanyData.companyType || null, websiteUrl: parentCompanyData.websiteUrl || null, instagramUrl: parentCompanyData.instagramUrl || null, facebookUrl: parentCompanyData.facebookUrl || null, @@ -544,32 +604,35 @@ export class HostService { }); 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 }); + for (const doc of parentDocuments) { + await tx.hostParenetDocuments.create({ + data: { + hostParentXid: createdParent.id, + documentTypeXid: doc.documentTypeXid, + documentName: doc.documentName, + filePath: doc.filePath, + }, + }); + } } } else { - // update + // update existing parent await tx.hostParent.update({ where: { id: parentRecord.id }, data: { companyName: parentCompanyData.companyName, - address1: parentCompanyData.address1, + address1: parentCompanyData.address1 || null, address2: parentCompanyData.address2 || null, - cityXid: parentCompanyData.cityXid, - stateXid: parentCompanyData.stateXid, - countryXid: parentCompanyData.countryXid, - pinCode: parentCompanyData.pinCode, + cityXid: parentCompanyData.cityXid || null, + stateXid: parentCompanyData.stateXid || null, + countryXid: parentCompanyData.countryXid || null, + pinCode: parentCompanyData.pinCode || null, logoPath: parentCompanyData.logoPath || null, - registrationNumber: parentCompanyData.registrationNumber, - panNumber: parentCompanyData.panNumber, + registrationNumber: parentCompanyData.registrationNumber || null, + panNumber: parentCompanyData.panNumber || null, gstNumber: parentCompanyData.gstNumber || null, - formationDate: parentCompanyData.formationDate ? new Date(parentCompanyData.formationDate) : null, - companyType: parentCompanyData.companyType, + formationDate: parentCompanyData.formationDate ? new Date(parentCompanyData.formationDate as any) : null, + companyType: parentCompanyData.companyType || null, websiteUrl: parentCompanyData.websiteUrl || null, instagramUrl: parentCompanyData.instagramUrl || null, facebookUrl: parentCompanyData.facebookUrl || null, @@ -578,18 +641,35 @@ export class HostService { }, }); - // replace parent docs - await tx.hostParenetDocuments.deleteMany({ - where: { hostParentXid: parentRecord.id }, - }); + // replace / upsert parent docs by documentTypeXid (no deletes) 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 }); + for (const doc of parentDocuments) { + const existingParentDoc = await tx.hostParenetDocuments.findFirst({ + where: { + hostParentXid: parentRecord.id, + documentTypeXid: doc.documentTypeXid, + }, + }); + + if (existingParentDoc) { + await tx.hostParenetDocuments.update({ + where: { id: existingParentDoc.id }, + data: { + filePath: doc.filePath, + documentName: doc.documentName || existingParentDoc.documentName, + }, + }); + } else { + await tx.hostParenetDocuments.create({ + data: { + hostParentXid: parentRecord.id, + documentTypeXid: doc.documentTypeXid, + documentName: doc.documentName, + filePath: doc.filePath, + }, + }); + } + } } } } else { @@ -609,16 +689,17 @@ export class HostService { } } - const hostDetails = await this.prisma.hostHeader.findFirst({ + // Create a host track entry + const hostDetails = await tx.hostHeader.findFirst({ where: { userXid: user_xid }, }); - await this.prisma.hostTrack.create({ + await tx.hostTrack.create({ data: { - hostXid: hostDetails.id, + hostXid: hostDetails!.id, updatedByRole: ROLE_NAME.HOST, updatedByXid: user_xid, - trackStatus: hostDetails.hostStatusInternal, + trackStatus: hostDetails!.hostStatusInternal, }, }); diff --git a/src/modules/minglaradmin/services/minglar.service.ts b/src/modules/minglaradmin/services/minglar.service.ts index 8d1ec9d..d5d6b35 100644 --- a/src/modules/minglaradmin/services/minglar.service.ts +++ b/src/modules/minglaradmin/services/minglar.service.ts @@ -1324,7 +1324,19 @@ export class MinglarService { const host = await this.prisma.hostHeader.findFirst({ where: { id: host_xid }, include: { - hostParent: true, + hostParent: { + include: { + HostParenetDocuments: { + select: { + id: true, + filePath: true, + documentName: true, + documentTypeXid: true, + documentType: true + } + } + } + }, HostBankDetails: true, HostDocuments: { include: { @@ -1351,8 +1363,9 @@ export class MinglarService { }, }); + const bucket = config.aws.bucketName; + if (host.HostDocuments?.length) { - const bucket = config.aws.bucketName; for (const doc of host.HostDocuments) { if (doc.filePath) { @@ -1368,6 +1381,40 @@ export class MinglarService { } } + if (host.logoPath) { + const key = host.logoPath.startsWith('http') + ? host.logoPath.split('.com/')[1] + : host.logoPath; + + host.logoPath = await getPresignedUrl(bucket, key); + } + + if (host.hostParent?.length) { + const parent = host.hostParent[0]; // since you allow only 1 parent + + // Parent company logo + if (parent.logoPath) { + const key = parent.logoPath.startsWith("http") + ? parent.logoPath.split(".com/")[1] + : parent.logoPath; + + parent.logoPath = await getPresignedUrl(bucket, key); + } + + // Parent documents + if (parent.HostParenetDocuments?.length) { + for (const doc of parent.HostParenetDocuments) { + if (doc.filePath) { + const key = doc.filePath.startsWith("http") + ? doc.filePath.split(".com/")[1] + : doc.filePath; + + (doc as any).presignedUrl = await getPresignedUrl(bucket, key); + } + } + } + } + return host; } }