|
|
|
|
@@ -11,11 +11,9 @@ import {
|
|
|
|
|
hostCompanyDetailsSchema,
|
|
|
|
|
hostDocumentsSchema,
|
|
|
|
|
parentCompanySchema,
|
|
|
|
|
// REQUIRED_DOC_TYPES
|
|
|
|
|
} from '../../../../../common/utils/validation/host/hostCompanyDetails.validation';
|
|
|
|
|
import { HostService } from '../../../services/host.service';
|
|
|
|
|
import { sendEmailToAM, sendEmailToMinglarAdmin } from '../../../services/sendHostResubmitEmailToAM.service';
|
|
|
|
|
import { HOST_STATUS_INTERNAL } from '@/common/utils/constants/host.constant';
|
|
|
|
|
|
|
|
|
|
const prisma = new PrismaService();
|
|
|
|
|
const hostService = new HostService(prisma);
|
|
|
|
|
@@ -26,7 +24,6 @@ const s3 = new AWS.S3({
|
|
|
|
|
|
|
|
|
|
function normalizeJsonField(fields: any, key: string) {
|
|
|
|
|
if (!fields[key]) return undefined;
|
|
|
|
|
|
|
|
|
|
const val = fields[key];
|
|
|
|
|
|
|
|
|
|
if (typeof val === "object") return val;
|
|
|
|
|
@@ -38,47 +35,51 @@ function normalizeJsonField(fields: any, key: string) {
|
|
|
|
|
throw new ApiError(400, `Invalid JSON in field: ${key}`);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
throw new ApiError(400, `Invalid input: ${key} must be object or JSON string.`);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function cleanEmptyStrings(obj: any) {
|
|
|
|
|
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]);
|
|
|
|
|
else cleaned[key] = obj[key];
|
|
|
|
|
}
|
|
|
|
|
return cleaned;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export const handler = safeHandler(async (event: APIGatewayProxyEvent): Promise<APIGatewayProxyResult> => {
|
|
|
|
|
try {
|
|
|
|
|
// 1) Auth
|
|
|
|
|
|
|
|
|
|
/** 1) AUTH */
|
|
|
|
|
const token = event.headers['x-auth-token'] || event.headers['X-Auth-Token'];
|
|
|
|
|
if (!token) throw new ApiError(400, 'This is a protected route. Please provide a valid token.');
|
|
|
|
|
const userInfo = await verifyHostToken(token);
|
|
|
|
|
|
|
|
|
|
// 2) multipart check
|
|
|
|
|
/** 2) CHECK CONTENT TYPE */
|
|
|
|
|
const contentType = event.headers['content-type'] || event.headers['Content-Type'];
|
|
|
|
|
if (!contentType?.includes('multipart/form-data')) {
|
|
|
|
|
throw new ApiError(400, 'Content-Type must be multipart/form-data.');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Handle both base64 and non-base64 encoded bodies
|
|
|
|
|
let bodyBuffer: Buffer;
|
|
|
|
|
if (event.isBase64Encoded) {
|
|
|
|
|
bodyBuffer = Buffer.from(event.body as string, 'base64');
|
|
|
|
|
} else {
|
|
|
|
|
bodyBuffer = Buffer.from(event.body as string, 'binary');
|
|
|
|
|
}
|
|
|
|
|
/** 3) HANDLE BODY */
|
|
|
|
|
const bodyBuffer = event.isBase64Encoded
|
|
|
|
|
? Buffer.from(event.body as string, 'base64')
|
|
|
|
|
: Buffer.from(event.body as string, 'binary');
|
|
|
|
|
|
|
|
|
|
const fields: Record<string, any> = {};
|
|
|
|
|
const files: Array<{ buffer: Buffer; mimeType: string; fileName: string; fieldName: string }> = [];
|
|
|
|
|
|
|
|
|
|
// 3) parse with Busboy
|
|
|
|
|
await new Promise<void>((resolve, reject) => {
|
|
|
|
|
const bb = Busboy({
|
|
|
|
|
headers: {
|
|
|
|
|
'content-type': contentType
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
const bb = Busboy({ headers: { 'content-type': contentType } });
|
|
|
|
|
|
|
|
|
|
bb.on('file', (fieldname, file, info) => {
|
|
|
|
|
const { filename, mimeType } = info;
|
|
|
|
|
const chunks: Buffer[] = [];
|
|
|
|
|
let totalSize = 0;
|
|
|
|
|
const MAX_SIZE = 5 * 1024 * 1024; // 5 MB
|
|
|
|
|
const MAX_SIZE = 5 * 1024 * 1024;
|
|
|
|
|
|
|
|
|
|
file.on('data', (chunk) => {
|
|
|
|
|
totalSize += chunk.length;
|
|
|
|
|
@@ -100,143 +101,72 @@ export const handler = safeHandler(async (event: APIGatewayProxyEvent): Promise<
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
file.on('error', (error) => {
|
|
|
|
|
reject(new ApiError(400, `File upload error: ${error.message}`));
|
|
|
|
|
});
|
|
|
|
|
file.on('error', (error) => reject(new ApiError(400, `File upload error: ${error.message}`)));
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
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}`));
|
|
|
|
|
});
|
|
|
|
|
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}`)));
|
|
|
|
|
|
|
|
|
|
bb.write(bodyBuffer);
|
|
|
|
|
bb.end();
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// Extract isDraft flag from fields (default to false if not provided)
|
|
|
|
|
const isDraft = fields.isDraft === 'true' || fields.isDraft === true;
|
|
|
|
|
/** 4) Extract and clean isDraft flag */
|
|
|
|
|
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.");
|
|
|
|
|
|
|
|
|
|
if (isDraft) {
|
|
|
|
|
companyDetailsRaw = cleanEmptyStrings(companyDetailsRaw);
|
|
|
|
|
|
|
|
|
|
// IMPORTANT: also clean parent company nested fields
|
|
|
|
|
if (companyDetailsRaw.parentCompany) {
|
|
|
|
|
companyDetailsRaw.parentCompany = cleanEmptyStrings(companyDetailsRaw.parentCompany);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** 6) Profile update if provided */
|
|
|
|
|
if (fields.userProfile) {
|
|
|
|
|
const userProfileRaw = normalizeJsonField(fields, "userProfile");
|
|
|
|
|
if (userProfileRaw) {
|
|
|
|
|
const { firstName, lastName, mobileNumber } = userProfileRaw;
|
|
|
|
|
|
|
|
|
|
// Update user profile if provided
|
|
|
|
|
if (firstName || lastName || mobileNumber) {
|
|
|
|
|
await prisma.user.update({
|
|
|
|
|
where: { id: userInfo.id },
|
|
|
|
|
data: {
|
|
|
|
|
...(firstName && { firstName }),
|
|
|
|
|
...(lastName && { lastName }),
|
|
|
|
|
...(mobileNumber && { mobileNumber }),
|
|
|
|
|
},
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
await prisma.user.update({
|
|
|
|
|
where: { id: userInfo.id },
|
|
|
|
|
data: {
|
|
|
|
|
...(firstName && { firstName }),
|
|
|
|
|
...(lastName && { lastName }),
|
|
|
|
|
...(mobileNumber && { mobileNumber }),
|
|
|
|
|
},
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 4) Validate required root fields
|
|
|
|
|
if (!fields.companyDetails) throw new ApiError(400, 'Missing companyDetails field.');
|
|
|
|
|
if (!fields.documents) throw new ApiError(400, 'Missing documents field.');
|
|
|
|
|
|
|
|
|
|
// 5) Parse companyDetails
|
|
|
|
|
const companyDetailsRaw = normalizeJsonField(fields, "companyDetails");
|
|
|
|
|
if (!companyDetailsRaw) throw new ApiError(400, "companyDetails is required.");
|
|
|
|
|
// --- FIXED HOST ID INITIALIZATION ---
|
|
|
|
|
let hostId: number;
|
|
|
|
|
|
|
|
|
|
// Check if host already exists
|
|
|
|
|
let existingHost = await prisma.hostHeader.findFirst({
|
|
|
|
|
where: { userXid: userInfo.id },
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// Define uploadToS3 function (same as before)
|
|
|
|
|
async function uploadToS3(buffer: Buffer, mimeType: string, originalName: string, folderType: 'logo' | 'documents' | 'parent_company', documentTypeXid?: number, fieldName?: string) {
|
|
|
|
|
let s3Key: string;
|
|
|
|
|
|
|
|
|
|
const sanitizeFileName = (name: string) => {
|
|
|
|
|
return name
|
|
|
|
|
.toLowerCase()
|
|
|
|
|
.replace(/[^a-z0-9.]/g, '_')
|
|
|
|
|
.replace(/_+/g, '_')
|
|
|
|
|
.replace(/^_+|_+$/g, '');
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const fileExtension = originalName.split('.').pop() || 'pdf';
|
|
|
|
|
|
|
|
|
|
if (folderType === 'logo') {
|
|
|
|
|
const sanitizedFileName = sanitizeFileName(originalName);
|
|
|
|
|
s3Key = `Documents/Host/${userInfo.id}/logo/${sanitizedFileName}`;
|
|
|
|
|
} else if (folderType === 'documents' && documentTypeXid && fieldName) {
|
|
|
|
|
const fileName = `${documentTypeXid}_${fieldName}.${fileExtension}`;
|
|
|
|
|
const sanitizedFileName = sanitizeFileName(fileName);
|
|
|
|
|
s3Key = `Documents/Host/${userInfo.id}/documents/${sanitizedFileName}`;
|
|
|
|
|
} else if (folderType === 'parent_company' && documentTypeXid && fieldName) {
|
|
|
|
|
const fileName = `${documentTypeXid}_${fieldName}.${fileExtension}`;
|
|
|
|
|
const sanitizedFileName = sanitizeFileName(fileName);
|
|
|
|
|
s3Key = `Documents/Host/${userInfo.id}/parent_company/${sanitizedFileName}`;
|
|
|
|
|
} else {
|
|
|
|
|
throw new ApiError(400, 'Invalid folder type or missing documentTypeXid/fieldName');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
await s3
|
|
|
|
|
.upload({
|
|
|
|
|
Bucket: config.aws.bucketName,
|
|
|
|
|
Key: s3Key,
|
|
|
|
|
Body: buffer,
|
|
|
|
|
ContentType: mimeType,
|
|
|
|
|
ACL: 'private',
|
|
|
|
|
})
|
|
|
|
|
.promise();
|
|
|
|
|
|
|
|
|
|
console.log(`File uploaded successfully: ${s3Key}`);
|
|
|
|
|
return `https://${config.aws.bucketName}.s3.${config.aws.region}.amazonaws.com/${s3Key}`;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 5.5) Handle company logo upload
|
|
|
|
|
const logoFile = files.find((f) => f.fieldName === 'companyLogo');
|
|
|
|
|
if (logoFile) {
|
|
|
|
|
const logoPath = await uploadToS3(
|
|
|
|
|
logoFile.buffer,
|
|
|
|
|
logoFile.mimeType,
|
|
|
|
|
logoFile.fileName,
|
|
|
|
|
'logo'
|
|
|
|
|
);
|
|
|
|
|
companyDetailsRaw.logoPath = logoPath;
|
|
|
|
|
console.log('Company logo uploaded:', logoPath);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 6) Zod validation for companyDetails
|
|
|
|
|
/** 7) VALIDATION - SKIPPED IF DRAFT */
|
|
|
|
|
let parsedCompany: any = companyDetailsRaw;
|
|
|
|
|
|
|
|
|
|
if (!isDraft) {
|
|
|
|
|
const companyValidation = hostCompanyDetailsSchema.safeParse(companyDetailsRaw);
|
|
|
|
|
if (!companyValidation.success) {
|
|
|
|
|
const message = companyValidation.error.issues.map((i) => i.message).join(', ');
|
|
|
|
|
const validate = hostCompanyDetailsSchema.safeParse(companyDetailsRaw);
|
|
|
|
|
if (!validate.success) {
|
|
|
|
|
const message = validate.error.issues.map(i => i.message).join(', ');
|
|
|
|
|
throw new ApiError(400, `Validation failed: ${message}`);
|
|
|
|
|
}
|
|
|
|
|
parsedCompany = companyValidation.data;
|
|
|
|
|
parsedCompany = validate.data;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 7) Parse documents metadata
|
|
|
|
|
/** 8) DOCUMENT METADATA */
|
|
|
|
|
const documentsMetadataRaw = normalizeJsonField(fields, "documents");
|
|
|
|
|
if (!Array.isArray(documentsMetadataRaw)) throw new ApiError(400, "documents must be an array.");
|
|
|
|
|
if (!documentsMetadataRaw.length) throw new ApiError(400, 'Documents must be a non-empty array.');
|
|
|
|
|
|
|
|
|
|
// Validate documents metadata shape
|
|
|
|
|
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}`);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
@@ -246,42 +176,79 @@ export const handler = safeHandler(async (event: APIGatewayProxyEvent): Promise<
|
|
|
|
|
owner: d.owner || 'host',
|
|
|
|
|
}));
|
|
|
|
|
|
|
|
|
|
// 8) Map uploaded files to metadata
|
|
|
|
|
const documentMetadata = documentsMetadata.map((doc: any) => {
|
|
|
|
|
const file = files.find((f) => f.fieldName === doc.fieldName);
|
|
|
|
|
if (!file) throw new ApiError(400, `File not found for field: ${doc.fieldName}`);
|
|
|
|
|
const file = files.find(f => f.fieldName === doc.fieldName);
|
|
|
|
|
|
|
|
|
|
// In DRAFT mode → allow missing documents
|
|
|
|
|
if (isDraft && !file) {
|
|
|
|
|
return { ...doc, file: null };
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// In FINAL mode → file must exist
|
|
|
|
|
if (!file) {
|
|
|
|
|
throw new ApiError(400, `File not found for field: ${doc.fieldName}`);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return { ...doc, file };
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// 9) Split host vs parent docs
|
|
|
|
|
const hostDocs = documentMetadata.filter((d) => d.owner === 'host');
|
|
|
|
|
const parentDocs = documentMetadata.filter((d) => d.owner === 'parent');
|
|
|
|
|
|
|
|
|
|
// 11) If isSubsidairy === true and parentCompany provided -> validate parent company & docs
|
|
|
|
|
/** 9) SPLIT host & parent docs */
|
|
|
|
|
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;
|
|
|
|
|
|
|
|
|
|
if (!isDraft && parsedCompany.isSubsidairy) {
|
|
|
|
|
if (!parsedCompany.parentCompany) {
|
|
|
|
|
throw new ApiError(400, 'isSubsidairy is true but parentCompany object is missing inside companyDetails.');
|
|
|
|
|
throw new ApiError(400, 'isSubsidairy is true but parentCompany object is missing.');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const parentValidation = parentCompanySchema.safeParse(parsedCompany.parentCompany);
|
|
|
|
|
if (!parentValidation.success) {
|
|
|
|
|
const message = parentValidation.error.issues.map((i) => i.message).join(', ');
|
|
|
|
|
const parentCheck = parentCompanySchema.safeParse(parsedCompany.parentCompany);
|
|
|
|
|
if (!parentCheck.success) {
|
|
|
|
|
const message = parentCheck.error.issues.map(i => i.message).join(', ');
|
|
|
|
|
throw new ApiError(400, `Parent company validation failed: ${message}`);
|
|
|
|
|
}
|
|
|
|
|
parsedParentCompany = parsedCompany.parentCompany;
|
|
|
|
|
|
|
|
|
|
parsedParentCompany = parentCheck.data;
|
|
|
|
|
} else {
|
|
|
|
|
parsedParentCompany = parsedCompany.parentCompany || null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** 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, '_');
|
|
|
|
|
|
|
|
|
|
// 12) Upload files to S3 (same for both draft and final submission)
|
|
|
|
|
const uploadedHostDocs: Array<{ documentTypeXid: number; documentName: string; filePath: string }> = [];
|
|
|
|
|
const uploadedParentDocs: Array<{ documentTypeXid: number; documentName: string; filePath: string }> = [];
|
|
|
|
|
const ext = originalName.split('.').pop() || 'pdf';
|
|
|
|
|
|
|
|
|
|
// Upload host documents
|
|
|
|
|
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}`)}`;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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 = [];
|
|
|
|
|
for (const doc of hostDocs) {
|
|
|
|
|
const filePath = await uploadToS3(
|
|
|
|
|
if (isDraft && !doc.file) continue;
|
|
|
|
|
|
|
|
|
|
const path = await uploadToS3(
|
|
|
|
|
doc.file.buffer,
|
|
|
|
|
doc.file.mimeType,
|
|
|
|
|
doc.file.fileName,
|
|
|
|
|
@@ -289,72 +256,71 @@ export const handler = safeHandler(async (event: APIGatewayProxyEvent): Promise<
|
|
|
|
|
doc.documentTypeXid,
|
|
|
|
|
doc.fieldName
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
uploadedHostDocs.push({
|
|
|
|
|
documentTypeXid: doc.documentTypeXid,
|
|
|
|
|
documentName: doc.fieldName,
|
|
|
|
|
filePath,
|
|
|
|
|
filePath: path,
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Upload parent company documents
|
|
|
|
|
if (parentDocs.length > 0) {
|
|
|
|
|
for (const doc of parentDocs) {
|
|
|
|
|
const filePath = await uploadToS3(
|
|
|
|
|
doc.file.buffer,
|
|
|
|
|
doc.file.mimeType,
|
|
|
|
|
doc.file.fileName,
|
|
|
|
|
'parent_company',
|
|
|
|
|
doc.documentTypeXid,
|
|
|
|
|
doc.fieldName
|
|
|
|
|
);
|
|
|
|
|
uploadedParentDocs.push({
|
|
|
|
|
documentTypeXid: doc.documentTypeXid,
|
|
|
|
|
documentName: doc.documentName,
|
|
|
|
|
filePath,
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** Upload parent docs */
|
|
|
|
|
const uploadedParentDocs = [];
|
|
|
|
|
for (const doc of parentDocs) {
|
|
|
|
|
if (!doc.file && isDraft) continue; // skip missing files in draft mode
|
|
|
|
|
|
|
|
|
|
const path = await uploadToS3(
|
|
|
|
|
doc.file.buffer,
|
|
|
|
|
doc.file.mimeType,
|
|
|
|
|
doc.file.fileName,
|
|
|
|
|
'parent_company',
|
|
|
|
|
doc.documentTypeXid,
|
|
|
|
|
doc.fieldName
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
uploadedParentDocs.push({
|
|
|
|
|
documentTypeXid: doc.documentTypeXid,
|
|
|
|
|
documentName: doc.documentName,
|
|
|
|
|
filePath: path,
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 13) Persist using hostService - PASS isDraft flag
|
|
|
|
|
|
|
|
|
|
/** 12) SAVE / UPDATE HOST ENTRY */
|
|
|
|
|
const createdOrUpdated = await hostService.addOrUpdateCompanyDetails(
|
|
|
|
|
userInfo.id,
|
|
|
|
|
parsedCompany,
|
|
|
|
|
uploadedHostDocs,
|
|
|
|
|
parsedParentCompany,
|
|
|
|
|
uploadedParentDocs,
|
|
|
|
|
isDraft // Pass the isDraft flag
|
|
|
|
|
isDraft
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
if (!createdOrUpdated) throw new ApiError(400, 'Failed to add/update company details.');
|
|
|
|
|
|
|
|
|
|
// Update hostId if it was a new creation
|
|
|
|
|
if (!existingHost) {
|
|
|
|
|
hostId = createdOrUpdated.id;
|
|
|
|
|
console.log(`Host created with ID: ${hostId}`);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 14) Send emails only for FINAL submission (not draft)
|
|
|
|
|
/** 13) SEND EMAIL ONLY IN FINAL SUBMISSION */
|
|
|
|
|
if (!isDraft) {
|
|
|
|
|
const getSuggestionDetails = await hostService.getSuggestionDetails(userInfo.id)
|
|
|
|
|
const details = await hostService.getSuggestionDetails(userInfo.id);
|
|
|
|
|
|
|
|
|
|
if (getSuggestionDetails.hostDetails.accountManagerXid !== null) {
|
|
|
|
|
if (details.hostDetails.accountManagerXid) {
|
|
|
|
|
await sendEmailToAM(
|
|
|
|
|
getSuggestionDetails.hostDetails.accountManager.emailAddress,
|
|
|
|
|
getSuggestionDetails.hostDetails.accountManager.firstName,
|
|
|
|
|
getSuggestionDetails.hostDetails.companyName,
|
|
|
|
|
getSuggestionDetails.hostDetails.hostRefNumber
|
|
|
|
|
details.hostDetails.accountManager.emailAddress,
|
|
|
|
|
details.hostDetails.accountManager.firstName,
|
|
|
|
|
details.hostDetails.companyName,
|
|
|
|
|
details.hostDetails.hostRefNumber
|
|
|
|
|
);
|
|
|
|
|
} else {
|
|
|
|
|
await sendEmailToMinglarAdmin(
|
|
|
|
|
config.MinglarAdminEmail,
|
|
|
|
|
config.MinglarAdminName,
|
|
|
|
|
getSuggestionDetails.hostDetails.companyName,
|
|
|
|
|
getSuggestionDetails.hostDetails.hostRefNumber
|
|
|
|
|
)
|
|
|
|
|
details.hostDetails.companyName,
|
|
|
|
|
details.hostDetails.hostRefNumber
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 15) Success response
|
|
|
|
|
/** RESPONSE */
|
|
|
|
|
return {
|
|
|
|
|
statusCode: 200,
|
|
|
|
|
headers: {
|
|
|
|
|
@@ -363,18 +329,17 @@ export const handler = safeHandler(async (event: APIGatewayProxyEvent): Promise<
|
|
|
|
|
},
|
|
|
|
|
body: JSON.stringify({
|
|
|
|
|
success: true,
|
|
|
|
|
message: isDraft
|
|
|
|
|
? 'Company details saved as draft successfully.'
|
|
|
|
|
: 'Company (and parent if provided) details and documents uploaded successfully.',
|
|
|
|
|
message: isDraft ? "Company details saved as draft successfully." : "Company details uploaded successfully.",
|
|
|
|
|
data: {
|
|
|
|
|
id: createdOrUpdated.id,
|
|
|
|
|
hostRefNumber: (createdOrUpdated as any).hostRefNumber,
|
|
|
|
|
hostRefNumber: createdOrUpdated.hostRefNumber,
|
|
|
|
|
isDraft
|
|
|
|
|
}
|
|
|
|
|
}),
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
} catch (error: any) {
|
|
|
|
|
console.error('❌ Error in addCompanyDetails:', error);
|
|
|
|
|
console.error("❌ Error in addCompanyDetails:", error);
|
|
|
|
|
throw error;
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|