import { displayValue } from './display-value.ts'; import { fiscalQaRows } from './fiscal-qa-preview.ts'; import { pathStates } from './investor-path.ts'; import { buildProjectDossierRows, type ProjectDossierRow, } from './project-dossier.ts'; export const dossierReportSections = [ { id: 'summary', title: 'Resumo executivo', private: true }, { id: 'project', title: 'Identificação e descrição do projecto', private: true, }, { id: 'market', title: 'Mercado e hipóteses', private: true }, { id: 'location', title: 'Localização', private: true }, { id: 'finance', title: 'Plano financeiro e cálculos', private: true }, { id: 'team', title: 'Equipa, competências e lacunas', private: true }, { id: 'resources', title: 'Recursos e financiamento', private: true }, { id: 'risk', title: 'O que precisa de esclarecer: riscos e respostas', private: true, }, { id: 'execution', title: 'Marcos e execução', private: true }, { id: 'sources', title: 'Fontes, pendências e próximos passos', private: true, }, { id: 'annexes', title: 'Anexos seleccionados', private: true }, ] as const; export type DossierReportSectionId = (typeof dossierReportSections)[number]['id']; export type DossierReference = { version: number; date: string; checksum: string; }; export type DossierCurrentCalculation = { status: string; version: number; updatedAt?: string; snapshot: Record | null; reportChecksum?: string; }; export type DossierAttachment = { file: File; include: boolean; caption: string; }; export type DossierAttachmentKind = 'image' | 'csv' | 'text' | 'original-only'; export type DossierReportRow = { field: string; value: string; origin: string; classification: string; }; export type DossierReportSection = { id: DossierReportSectionId; title: string; private: boolean; paragraphs: string[]; rows: DossierReportRow[]; }; export type DossierReportModel = { title: string; generatedAt: string; version: string; sections: DossierReportSection[]; references: DossierReference[]; limitations: string[]; }; export const MAX_DOSSIER_ATTACHMENT_COUNT = 10; export const MAX_DOSSIER_ATTACHMENT_BYTES = 15 * 1024 * 1024; export const MAX_DOSSIER_ATTACHMENT_TOTAL_BYTES = 50 * 1024 * 1024; export const MAX_EMBEDDED_TEXT_BYTES = 2 * 1024 * 1024; function text(draft: Record, key: string): string { const value = draft[key]; if (Array.isArray(value)) return value.filter(Boolean).join(', '); return typeof value === 'string' ? value.trim() : ''; } function absent(value: string): string { return value.trim() === '' ? 'Não informado' : value; } function classification(origin: string): string { const normal = origin.toLocaleLowerCase('pt-MZ'); if (normal.includes('não verificada') || normal.includes('nao verificada')) return 'Informação declarada, não verificada'; if (normal.includes('estimativa') || normal.includes('estimado')) return 'Estimativa'; if ( normal.includes('indeterminado') || normal.includes('insuficiente') || normal.includes('não informado') ) return 'Indeterminado'; if (normal.includes('catálogo') || normal.includes('catalogo')) return 'Referência de catálogo; não equivale a verificação documental'; if ( normal.includes('utilizador') || normal.includes('declarada') || normal.includes('declarado') || normal.includes('pressuposto') ) return 'Informação declarada ou pressuposto'; return 'Referência/estado indicado'; } export function formatDossierReportValue( field: string, value: string, currency = '', ): string { if (field === 'Estado do plano detalhado') { return value.replace( 'Snapshot versionado disponível; usar também a exportação do plano financeiro', 'Plano guardado disponível; consulte também a exportação financeira', ); } if (field === 'Reconciliação do financiamento') { const states: Record = { shortfall: 'Financiamento insuficiente', surplus: 'Fontes superiores aos usos previstos', balanced: 'Fontes e usos equilibrados', unknown: 'Informação insuficiente', incomplete: 'Informação insuficiente', }; return states[value] ?? value; } if (/^Necessidade \d+$/.test(field)) { value = value .replace('; gap;', '; Competência em falta;') .replace('; unknown;', '; Por confirmar;') .replace('; available;', '; Disponível (declarado);'); } if (/^Marco \d+$/.test(field)) { value = value .replace('; planned;', '; Planeado;') .replace('; in_progress;', '; Em curso;') .replace('; completed;', '; Concluído (declarado);'); } if (/^(Necessidade|Marco) \d+$/.test(field)) { value = value .replace( /(custo|planeado|realizado): ([^;]*)(?=;|$)/g, (_, label: string, amount: string) => { const raw = amount.trim(); return `${label}: ${raw ? (/^\d+(?:\.\d{1,2})?$/.test(raw) ? `${raw} ${currency || '(moeda não informada)'}` : raw) : 'Não informado'}`; }, ) .replace(/prazo: (?=;|$)/g, 'prazo: Não informado'); } // Presentation only: group the decimal string without converting the amount // to a JavaScript float or changing the saved financial snapshot. return value.replace( /^(-?)(\d+)\.(\d{2}) ([A-Z]{3})$/, (_, sign: string, whole: string, cents: string, currency: string) => `${sign}${whole.replace(/\B(?=(\d{3})+(?!\d))/g, ' ')},${cents} ${currency}`, ); } const rowSection: Record = { Projecto: 'project', Mercado: 'market', Localização: 'location', 'Plano financeiro': 'finance', 'Empréstimo educativo': 'finance', 'Ponto de equilíbrio': 'finance', Orçamento: 'finance', Tesouraria: 'finance', 'IVA mensal': 'finance', ISPC: 'finance', 'IRPC — retenções': 'finance', 'IRPC normal — L6': 'finance', 'IRPC — L7': 'finance', 'IRPC — L8': 'finance', Equipa: 'team', 'Necessidade de negócio': 'resources', Financiamento: 'resources', Risco: 'risk', Execução: 'execution', }; function makeSections( draft: Record, rows: ProjectDossierRow[], selectedSections: ReadonlySet, references: DossierReference[], currentCalculation: DossierCurrentCalculation | null, generatedAt: string, version: string, ): DossierReportSection[] { const sections = new Map(); for (const section of dossierReportSections) { if (!selectedSections.has(section.id)) continue; sections.set(section.id, { id: section.id, title: section.title, private: section.private, paragraphs: [], rows: [], }); } const summary = sections.get('summary'); if (summary) { const suppliedSummary = text(draft, 'project_summary'); const summaryParts = [ suppliedSummary, !suppliedSummary && text(draft, 'project_title') ? `Projecto: ${text(draft, 'project_title')}.` : '', !suppliedSummary && text(draft, 'project_problem') ? `Necessidade abordada: ${text(draft, 'project_problem')}.` : '', !suppliedSummary && text(draft, 'value_proposition') ? `Proposta de valor declarada: ${text(draft, 'value_proposition')}.` : '', !suppliedSummary && text(draft, 'target_customers') ? `Clientes/beneficiários indicados: ${text(draft, 'target_customers')}.` : '', ].filter(Boolean); summary.paragraphs.push( summaryParts.length ? summaryParts.join(' ') : 'Resumo executivo pendente: ainda não foram introduzidas informações suficientes no dossier.', ); summary.rows.push( { field: 'Projecto', value: absent(text(draft, 'project_title')), origin: rows.find((row) => row[1] === 'Título')?.[3] ?? 'Informação introduzida no dossier', classification: 'Origem indicada no dossier', }, { field: 'Data de geração', value: new Date(generatedAt).toLocaleString('pt-MZ'), origin: 'Emissão no momento da exportação', classification: 'Data de emissão', }, { field: 'Versão/revisão do dossier', value: version, origin: 'Revisão do dossier exportado', classification: 'Referência da revisão', }, ); const highlights = new Set([ 'Saldo operacional estimado', 'Diferença por financiar', 'Prestação do primeiro período', 'Saldo final total', 'Caixa conhecida antes de imposto em falta', ]); const budgetPeriod = rows.find( (row) => row[1] === 'Periodicidade do orçamento', )?.[2]; for (const [, field, value, origin] of rows.slice(1)) { if (!highlights.has(field)) continue; summary.rows.push({ field: field === 'Saldo operacional estimado' && budgetPeriod ? `${field} — ${budgetPeriod}` : field === 'Saldo final total' ? 'Saldo final de tesouraria — 12 meses' : field, value: absent( formatDossierReportValue( field, value, text(draft, 'currency'), ), ), origin, classification: classification(origin), }); } } if (draft.context_applicability_snapshot) { try { const context = JSON.parse( String(draft.context_applicability_snapshot), ); if (context.version === 1) { const section = sections.get('sources'); if (context.portfolio_origin) section?.rows.push({ field: 'Projecto ou carteira de origem', value: context.portfolio_origin.title, origin: `${context.portfolio_origin.key}; versão ${context.portfolio_origin.version}; ${context.portfolio_origin.href}`, classification: 'Origem documental; localização e oferta não presumidas', }); if (context.consumer_opportunity) section?.rows.push({ field: 'Oportunidade em tratamento', value: context.consumer_opportunity, origin: context.opportunity_choices?.[0]?.opportunity ?.title ?? 'Escolha guardada no dossier', classification: 'Âmbito desta análise', }); for (const step of context.path?.steps ?? []) { if (step.applicability === 'not_needed_for_preparation') section?.paragraphs.push( `${step.code} — preparação não necessária neste âmbito: ${step.reason || 'Decisão guardada do promotor'}. Histórico conservado; não constitui dispensa legal.`, ); else section?.rows.push({ field: `Etapa ${step.code}: ${step.title}`, value: step.applicability === 'undetermined' ? 'Aplicabilidade por determinar' : (pathStates[step.state] ?? 'Estado por determinar'), origin: context.consumer_opportunity ?? 'Contexto comum do dossier', classification: 'Preparação no âmbito seleccionado', }); } const dispensed = new Set( (context.path?.steps ?? []) .filter( (step: { applicability: string }) => step.applicability === 'not_needed_for_preparation', ) .map((step: { code: string }) => step.code), ); const requirementStates: Record = { selected: 'Localização escolhida; direito não inferido', missing: 'Informação em falta', conditional: 'Conforme as condições do projecto', declared: 'Actividades declaradas; enquadrar modalidade', undetermined: 'Procedimento por determinar', }; for (const requirement of context.requirements_assessment ?.requirements ?? []) { if ( requirement.opportunity_key !== context.consumer_opportunity || dispensed.has(requirement.step) ) continue; section?.rows.push({ field: `Requisito: ${requirement.title}`, value: `${requirementStates[requirement.state] ?? 'Por determinar'}. ${(requirement.documents ?? []).join('; ')}`, origin: `${requirement.opportunity_title}; ${requirement.opportunity_key}; ${requirement.basis ?? ''} ${requirement.source_url ?? ''}`, classification: 'Preparação específica; não constitui licença ou aprovação', }); } const zone = context.zone; section?.rows.push({ field: 'Contexto do investimento', value: `Revisão ${context.context_revision}; ${zone ? `${zone.title}, versão ${zone.version}` : 'sem ZEE, ZFI ou parque seleccionado'}`, origin: 'Referência do mesmo dossier no momento da gravação', classification: 'Contexto versionado; não é reconhecimento jurídico', }); section?.paragraphs.push( zone ? 'Benefício fiscal da zona indeterminado: falta regra executável ligada às condições e reconhecimento. O IVA apresentado é apenas cenário geral sem benefício; não representa o regime especial nem o apuramento final.' : 'Cenário geral de preparação; sem confirmação automática de elegibilidade fiscal.', ); const preparation = context.preparation; if (preparation?.enabled) { const modes: Record = { authorization: 'Pedido de autorização', registration: 'Pedido de mero registo', indeterminate: 'Enquadramento por determinar', }; section?.rows.push({ field: 'Preparação do investimento privado', value: modes[preparation.mode] ?? 'Por determinar', origin: `Contexto congelado v${context.context_revision}; ${preparation.version}`, classification: 'Declarações e requisitos de preparação; sem aprovação jurídica', }); section?.paragraphs.push( preparation.next, preparation.calculation_notice, preparation.notice, ); const omitted = (preparation.requirements ?? []).filter( (requirement: { state: string }) => requirement.state === 'not_applicable', ); if (omitted.length) section?.paragraphs.push( `Não exigido neste contexto: ${omitted.map((requirement: { label: string; reason: string }) => `${requirement.label} (${requirement.reason})`).join('; ')}.`, ); for (const requirement of preparation.requirements ?? []) { // Keep an explicitly associated historical document visible without requiring it again. if ( requirement.state === 'not_applicable' && !requirement.document_id && !requirement.document ) continue; section?.rows.push({ field: requirement.label, value: requirement.state === 'not_applicable' ? 'Não aplicável; histórico preservado' : requirement.state === 'associated' ? `Associado: ${requirement.document?.file_name ?? 'documento do dossier'}; conteúdo por conferir` : requirement.state === 'missing' ? 'Documento por associar' : 'Informação em falta para decidir', origin: `${requirement.reason} ${requirement.source?.reference ?? ''}; ${requirement.source?.url ?? ''}`, classification: 'Preparação informativa — simulação, sem submissão à Administração', }); } } if (zone?.href) section?.rows.push({ field: 'Ficha e fundamentos da zona', value: zone.href, origin: `Versão consultada ${zone.version}`, classification: 'Referência consultável; condições por verificar', }); } } catch { sections .get('sources') ?.paragraphs.push( 'Não foi possível recuperar o contexto guardado; o enquadramento territorial permanece indeterminado.', ); } } for (const [sectionName, field, value, origin] of rows.slice(1)) { // Questionnaire schema identifiers remain stored; they do not help the // reader assess the project and are not a business report field. if (field === 'Versão do questionário') continue; const id = rowSection[sectionName]; if (!id) continue; const section = sections.get(id); if (!section) continue; section.rows.push({ field, value: absent( formatDossierReportValue(field, value, text(draft, 'currency')), ), origin, classification: value.trim() === '' ? 'Informação em falta' : classification(origin), }); } const project = sections.get('project'); if (project) { const extra = [ ['Data prevista de início', text(draft, 'expected_start_date')], ['Duração prevista', text(draft, 'project_duration_years')], ]; for (const [field, value] of extra) { if (!project.rows.some((row) => row.field === field)) project.rows.push({ field, value: absent(value), origin: 'Informação do dossier', classification: value ? 'Declarada pelo utilizador' : 'Indeterminado', }); } } const location = sections.get('location'); if (location) { for (const [field, key] of [ ['País', 'country_id_label'], ['Província', 'province_id_label'], ['Distrito', 'district_id_label'], ['Posto administrativo', 'admin_post_id_label'], ['Localidade', 'locality_id_label'], ]) { if (!location.rows.some((row) => row.field === field)) { const value = text(draft, key) || (key === 'country_id_label' && text(draft, 'country_id') === 'MZ' ? 'Moçambique' : ''); location.rows.push({ field, value: absent(value), origin: 'Selecção/informação territorial do dossier', classification: value ? 'Selecção declarada; não constitui verificação do local' : 'Indeterminado', }); } } } const finance = sections.get('finance'); if ( finance && ['fiscal_rule_qa_preview', 'fiscal_rule_published'].includes( String(currentCalculation?.snapshot?.scenario_kind), ) && currentCalculation?.snapshot ) { for (const [field, value] of fiscalQaRows( currentCalculation.snapshot, )) { finance.rows.push({ field, value, origin: 'Snapshot fiscal preservado; sem recálculo', classification: 'Cálculo exacto; ver estado da regra no snapshot; não é saldo a pagar', }); } } if (finance && currentCalculation) { const snapshot = currentCalculation.snapshot; const result = snapshot && typeof snapshot.result === 'object' && snapshot.result !== null && !Array.isArray(snapshot.result) ? (snapshot.result as Record) : null; const context = snapshot && typeof snapshot.context === 'object' && snapshot.context !== null && !Array.isArray(snapshot.context) ? (snapshot.context as Record) : null; const rawValue = result?.value ?? snapshot?.indicative_total; const knownValue = typeof rawValue === 'string' && /^(?:\d+)(?:\.\d+)?$/.test(rawValue) ? rawValue : ''; finance.rows.push( { field: 'Estado da simulação fiscal/aritmética anexada', value: currentCalculation.status === 'completed' ? result?.status === 'indeterminate' ? 'Indeterminado' : 'Concluída' : 'Não concluída; resultado não incluído como confirmado', origin: 'Execução existente no simulador', classification: currentCalculation.status === 'completed' ? 'Estado da execução; verificar a explicação e o âmbito abaixo' : 'Indeterminado', }, { field: 'Resultado indicativo', value: currentCalculation.status === 'completed' && knownValue ? `${knownValue}${typeof result?.unit === 'string' ? ` ${result.unit}` : ''}` : 'Indeterminado/não disponível', origin: 'Snapshot da execução existente; valor reproduzido sem recalcular', classification: 'Resultado do simulador; não é decisão fiscal nem aconselhamento', }, { field: 'Explicação e dados em falta', value: typeof result?.explanation === 'string' ? result.explanation : 'Não disponível no snapshot', origin: 'Explicação guardada pela execução', classification: 'Informação do resultado do simulador', }, { field: 'Data de referência', value: typeof context?.reference_date === 'string' ? context.reference_date : 'Não identificada', origin: 'Contexto guardado na execução', classification: 'Metadado da simulação', }, { field: 'Versão do motor/cenário', value: `${typeof context?.engine_version === 'string' ? context.engine_version : 'não identificada'} / ${typeof context?.scenario_version === 'number' ? context.scenario_version : currentCalculation.version}`, origin: 'Contexto guardado na execução', classification: 'Referência da revisão', }, { field: 'Fórmula e valores usados', value: [ 'fiscal_rule_qa_preview', 'fiscal_rule_published', ].includes(String(snapshot?.scenario_kind)) ? `Base tributável declarada × taxa cadastrada ÷ 100; cálculo decimal exacto, sem arredondamento. Ver regra e proveniência acima.` : snapshot && typeof snapshot.formula_version === 'string' ? `Fórmula ${snapshot.formula_version}; base ${displayValue(snapshot.base, 'não disponível')}; taxa ${displayValue(snapshot.rate_percent, 'não disponível')}%; redução ${displayValue(snapshot.reduction_percent, 'não disponível')}%; moeda ${displayValue(snapshot.currency, 'não indicada')}` : 'A fórmula não foi identificada no snapshot; ver explicação da execução', origin: [ 'fiscal_rule_qa_preview', 'fiscal_rule_published', ].includes(String(snapshot?.scenario_kind)) ? snapshot?.scenario_kind === 'fiscal_rule_published' ? 'Versão publicada na admissão; sem validação jurídica da operação' : 'Snapshot QA de regra em rascunho; sem activação ou validação jurídica da operação' : 'Snapshot guardado; sem aplicação de regra legal', classification: 'Reutilização do resultado existente', }, { field: 'Revisão do resultado', value: String(currentCalculation.version), origin: currentCalculation.updatedAt ?? 'Data não identificada', classification: 'Versão imutável da execução existente', }, ...(currentCalculation.reportChecksum ? [ { field: 'Hash SHA-256 do relatório associado', value: currentCalculation.reportChecksum, origin: 'Referência imutável do relatório', classification: 'Integridade técnica; não é fonte legal', }, ] : []), ); } if (finance && !text(draft, 'business_plan_snapshot')) { finance.paragraphs.push( 'Não existe plano empresarial detalhado guardado neste dossier. Os campos sem valor são indicados como “Não informado”; não se substituem por zero.', ); } const resources = sections.get('resources'); if (resources) { resources.rows.push({ field: 'Pré-elegibilidade de financiamento', value: 'Indeterminada; não há catálogo de produtos e critérios documentados para esta consulta.', origin: 'Limite funcional conhecido', classification: 'Não verificado/não decidido', }); const needs = text(draft, 'infrastructure_needs'); resources.rows.push({ field: 'Outros recursos declarados', value: absent(needs), origin: 'Informação do dossier', classification: needs ? 'Declarada pelo utilizador' : 'Indeterminado', }); } const sources = sections.get('sources'); if (sources) { const marketSource = text(draft, 'market_source'); sources.rows.push({ field: 'Origem de mercado', value: absent(marketSource), origin: 'Declarada pelo utilizador; não verificada pelo portal', classification: marketSource ? 'Informação declarada, não verificada' : 'Indeterminado', }); if (!marketSource) sources.paragraphs.push( 'Se disponível, registar origem e data da informação de mercado e validar a fonte antes de a usar numa decisão.', ); if (references.length) { for (const [index, ref] of references.entries()) { sources.rows.push({ field: `Referência de cálculo anexada ${index + 1}`, value: `Versão ${ref.version}; ${new Date(ref.date).toLocaleString('pt-MZ')}; SHA-256 ${ref.checksum}`, origin: 'Simulação matemática anexada ao dossier com consentimento', classification: 'Rasto de cálculo; não é fonte oficial externa', }); } } else { sources.rows.push({ field: 'Cálculos de simulação anexados', value: 'Nenhum cálculo externo/anterior anexado ao dossier.', origin: 'Estado actual do dossier', classification: 'Não aplicável', }); } const nextSteps = [ !text(draft, 'project_summary') && 'Completar o resumo do projecto ou negócio.', !text(draft, 'market_assumption') && 'Descrever, se conhecido, a hipótese de mercado e a sua origem/data.', !text(draft, 'business_plan_snapshot') && 'Registar pressupostos financeiros se forem necessários à decisão.', !text(draft, 'project_team_snapshot') && 'Identificar competências/equipa em falta, caso sejam relevantes.', 'Rever os pressupostos e confirmar as fontes antes de tomar decisões.', 'Verificar critérios de financiamento em produtos documentados; esta exportação não determina elegibilidade.', ].filter((item): item is string => Boolean(item)); sources.paragraphs.push( `Próximos passos sugeridos a partir dos campos em falta: ${nextSteps.join(' ')}`, ); sources.paragraphs.push( 'Informação não carregada ou não fornecida não é inferida. A exportação não publica nem partilha o dossier.', ); } return [...sections.values()]; } export function createProjectDossierReport( draft: Record, options: { selectedSections: ReadonlySet; version?: string; references?: DossierReference[]; currentCalculation?: DossierCurrentCalculation | null; generatedAt?: string; }, ): DossierReportModel { const generatedAt = options.generatedAt ?? new Date().toISOString(); const references = options.references ?? []; const reportRows = buildProjectDossierRows(draft); const title = absent(text(draft, 'project_title')); return { title, generatedAt, version: options.version?.trim() || 'Rascunho sem revisão guardada', sections: makeSections( draft, reportRows, options.selectedSections, references, options.currentCalculation ?? null, generatedAt, options.version?.trim() || 'Rascunho sem revisão guardada', ), references, limitations: [ 'Conteúdo fornecido pelo utilizador, estimativas matemáticas, selecções de catálogo e referências documentadas estão rotulados separadamente.', 'A origem de mercado introduzida pelo utilizador não foi verificada automaticamente.', 'Não há pré-aprovação nem confirmação de financiamento nesta exportação.', 'O relatório é gerado localmente e não é publicado nem partilhado pelo portal.', ], }; } export function classifyDossierAttachment( file: Pick, ): DossierAttachmentKind { const name = file.name.toLocaleLowerCase('en'); const type = file.type.toLocaleLowerCase('en'); if ( (type === 'image/jpeg' || type === 'image/png') && file.size <= MAX_DOSSIER_ATTACHMENT_BYTES ) return 'image'; if ( (type === 'text/csv' || name.endsWith('.csv') || name.endsWith('.tsv')) && file.size <= MAX_EMBEDDED_TEXT_BYTES ) return 'csv'; if ( (type === 'text/plain' || name.endsWith('.txt')) && file.size <= MAX_EMBEDDED_TEXT_BYTES ) return 'text'; return 'original-only'; } export function validateDossierAttachments( files: readonly Pick[], ): string | null { if (files.length > MAX_DOSSIER_ATTACHMENT_COUNT) return `Seleccione no máximo ${MAX_DOSSIER_ATTACHMENT_COUNT} anexos.`; if (files.some((file) => file.size > MAX_DOSSIER_ATTACHMENT_BYTES)) return 'Cada anexo deve ter até 15 MB.'; if ( files.reduce((total, file) => total + file.size, 0) > MAX_DOSSIER_ATTACHMENT_TOTAL_BYTES ) return 'O conjunto de anexos deve ter até 50 MB.'; return null; } export async function generateProjectDossierDocx( ...args: Parameters< typeof import('./project-dossier-document.ts').generateProjectDossierDocx > ): ReturnType< typeof import('./project-dossier-document.ts').generateProjectDossierDocx > { const document = await import('./project-dossier-document.ts'); return document.generateProjectDossierDocx(...args); } export async function createProjectDossierPrintHtml( ...args: Parameters< typeof import('./project-dossier-document.ts').createProjectDossierPrintHtml > ): ReturnType< typeof import('./project-dossier-document.ts').createProjectDossierPrintHtml > { const document = await import('./project-dossier-document.ts'); return document.createProjectDossierPrintHtml(...args); } export async function generateProjectDossierPackage( ...args: Parameters< typeof import('./project-dossier-document.ts').generateProjectDossierPackage > ): ReturnType< typeof import('./project-dossier-document.ts').generateProjectDossierPackage > { const document = await import('./project-dossier-document.ts'); return document.generateProjectDossierPackage(...args); }