task-h.1 complate
This commit is contained in:
@@ -35,7 +35,8 @@ const DOCUMENT_OPTION_CATEGORIES = {
|
||||
discountType: 'crm_discount_type',
|
||||
unit: 'crm_unit',
|
||||
customerRole: 'crm_quotation_customer_role',
|
||||
productType: 'crm_product_type'
|
||||
productType: 'crm_product_type',
|
||||
topicType: 'crm_quotation_topic_type'
|
||||
} as const;
|
||||
|
||||
function toRevisionLabel(revision: number) {
|
||||
@@ -76,6 +77,72 @@ function findOptionCode(
|
||||
return id ? options.find((option) => option.id === id)?.code ?? null : null;
|
||||
}
|
||||
|
||||
function extractSinglePlaceholder(value: string) {
|
||||
const matches = [...value.matchAll(/\{([^}]+)\}/g)].map((match) => match[1]);
|
||||
const uniqueMatches = [...new Set(matches)];
|
||||
|
||||
return uniqueMatches.length === 1 ? uniqueMatches[0] : null;
|
||||
}
|
||||
|
||||
function normalizePdfmeTemplateInput(
|
||||
schemaJson: unknown,
|
||||
templateInput: Record<string, unknown>
|
||||
) {
|
||||
const pages =
|
||||
schemaJson && typeof schemaJson === 'object' && 'schemas' in schemaJson
|
||||
? (schemaJson as { schemas?: unknown }).schemas
|
||||
: null;
|
||||
|
||||
if (!Array.isArray(pages)) {
|
||||
return templateInput;
|
||||
}
|
||||
|
||||
const normalized = { ...templateInput };
|
||||
|
||||
for (const page of pages) {
|
||||
if (!Array.isArray(page)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (const field of page) {
|
||||
if (!field || typeof field !== 'object') {
|
||||
continue;
|
||||
}
|
||||
|
||||
const tableField = field as { type?: unknown; content?: unknown };
|
||||
|
||||
if (tableField.type !== 'table' || typeof tableField.content !== 'string') {
|
||||
continue;
|
||||
}
|
||||
|
||||
const placeholderKey = extractSinglePlaceholder(tableField.content);
|
||||
|
||||
if (!placeholderKey) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const value = normalized[placeholderKey];
|
||||
|
||||
if (typeof value !== 'string') {
|
||||
continue;
|
||||
}
|
||||
|
||||
const lines = value
|
||||
.split('\n')
|
||||
.map((line) => line.trim())
|
||||
.filter(Boolean);
|
||||
|
||||
if (!lines.length) {
|
||||
continue;
|
||||
}
|
||||
|
||||
normalized[placeholderKey] = JSON.stringify(lines.map((line) => [line]));
|
||||
}
|
||||
}
|
||||
|
||||
return normalized;
|
||||
}
|
||||
|
||||
export async function buildQuotationDocumentData(
|
||||
quotationId: string,
|
||||
organizationId: string
|
||||
@@ -95,6 +162,7 @@ export async function buildQuotationDocumentData(
|
||||
unitOptions,
|
||||
customerRoleOptions,
|
||||
productTypeOptions,
|
||||
topicTypeOptions,
|
||||
approvalRequests
|
||||
] = await Promise.all([
|
||||
db
|
||||
@@ -114,6 +182,7 @@ export async function buildQuotationDocumentData(
|
||||
getActiveOptionsByCategory(DOCUMENT_OPTION_CATEGORIES.unit, { organizationId }),
|
||||
getActiveOptionsByCategory(DOCUMENT_OPTION_CATEGORIES.customerRole, { organizationId }),
|
||||
getActiveOptionsByCategory(DOCUMENT_OPTION_CATEGORIES.productType, { organizationId }),
|
||||
getActiveOptionsByCategory(DOCUMENT_OPTION_CATEGORIES.topicType, { organizationId }),
|
||||
listApprovalRequests(organizationId, { entityType: 'quotation', entityId: quotationId, limit: 20 })
|
||||
]);
|
||||
|
||||
@@ -180,6 +249,7 @@ export async function buildQuotationDocumentData(
|
||||
const relatedCustomerMap = new Map(relatedCustomers.map((item) => [item.id, item]));
|
||||
const statusCode = findOptionCode(statusOptions, quotation.status);
|
||||
const statusLabel = findOptionLabel(statusOptions, quotation.status);
|
||||
const topicCodeById = new Map(topicTypeOptions.map((option) => [option.id, option.code]));
|
||||
const approvalApprovers: QuotationDocumentApprovalStep[] =
|
||||
approvalDetail?.timeline
|
||||
.filter((item) => ['approve', 'reject', 'return'].includes(item.action))
|
||||
@@ -284,16 +354,19 @@ export async function buildQuotationDocumentData(
|
||||
})),
|
||||
topics: {
|
||||
scope: topics
|
||||
.filter((item) => item.topicType === 'scope')
|
||||
.filter((item) => topicCodeById.get(item.topicType) === 'scope')
|
||||
.map((item) => ({ title: item.title, items: item.items.map((child) => child.content) })),
|
||||
exclusion: topics
|
||||
.filter((item) => item.topicType === 'exclusion')
|
||||
.filter((item) => topicCodeById.get(item.topicType) === 'exclusion')
|
||||
.map((item) => ({ title: item.title, items: item.items.map((child) => child.content) })),
|
||||
payment: topics
|
||||
.filter((item) => item.topicType === 'payment')
|
||||
.filter((item) => topicCodeById.get(item.topicType) === 'payment')
|
||||
.map((item) => ({ title: item.title, items: item.items.map((child) => child.content) })),
|
||||
other: topics
|
||||
.filter((item) => !['scope', 'exclusion', 'payment'].includes(item.topicType))
|
||||
.filter((item) => {
|
||||
const topicCode = topicCodeById.get(item.topicType);
|
||||
return !['scope', 'exclusion', 'payment'].includes(topicCode ?? '');
|
||||
})
|
||||
.map((item) => ({ title: item.title, items: item.items.map((child) => child.content) }))
|
||||
},
|
||||
approval: {
|
||||
@@ -330,9 +403,14 @@ export async function getQuotationDocumentPreviewData(
|
||||
fileType: 'pdfme'
|
||||
});
|
||||
const mappings = await resolveTemplateMappings(template.version.id, organizationId);
|
||||
const templateInput = mapDocumentDataToTemplateInput(
|
||||
documentData as unknown as Record<string, unknown>,
|
||||
mappings
|
||||
|
||||
if (!mappings.length) {
|
||||
throw new AuthError('Document template mappings are not configured for this template version', 409);
|
||||
}
|
||||
|
||||
const templateInput = normalizePdfmeTemplateInput(
|
||||
template.version.schemaJson,
|
||||
mapDocumentDataToTemplateInput(documentData as unknown as Record<string, unknown>, mappings)
|
||||
);
|
||||
|
||||
return {
|
||||
|
||||
Reference in New Issue
Block a user