feat(proformas): agregar contexto de generación de documentos y actualizar fábricas y servicios relacionados
This commit is contained in:
parent
b9143d8878
commit
fc8c7fea99
@ -21,26 +21,35 @@ import type { IDocumentSideEffect } from "./document-side-effect.interface";
|
||||
* 5. Side-effects (persistencia, métricas) [best-effort]
|
||||
*/
|
||||
|
||||
export type DocumentGenerationServiceDeps<TSnapshot> = {
|
||||
renderer: IDocumentRenderer<TSnapshot>;
|
||||
metadataFactory: IDocumentMetadataFactory<TSnapshot>;
|
||||
propertiesFactory: IDocumentPropertiesFactory<TSnapshot>;
|
||||
export type DocumentGenerationServiceDeps<TSnapshot, TContext = Record<string, unknown>> = {
|
||||
renderer: IDocumentRenderer<TSnapshot, TContext & DocumentGenerationServiceRendererParams>;
|
||||
metadataFactory: IDocumentMetadataFactory<TSnapshot, TContext>;
|
||||
propertiesFactory: IDocumentPropertiesFactory<TSnapshot, TContext>;
|
||||
preProcessors: readonly IDocumentPreProcessor[];
|
||||
postProcessor: IDocumentPostProcessor;
|
||||
sideEffects: readonly IDocumentSideEffect[];
|
||||
};
|
||||
|
||||
export type DocumentGenerationServiceRenderParams = Record<string, unknown>;
|
||||
export type DocumentGenerationServiceRendererParams = {
|
||||
format: "PDF" | "HTML";
|
||||
languageCode: string;
|
||||
filename: string;
|
||||
mimeType: string;
|
||||
properties: IDocumentProperties;
|
||||
};
|
||||
|
||||
export class DocumentGenerationService<TSnapshot> {
|
||||
private readonly renderer: IDocumentRenderer<TSnapshot>;
|
||||
private readonly metadataFactory: IDocumentMetadataFactory<TSnapshot>;
|
||||
private readonly propertiesFactory: IDocumentPropertiesFactory<TSnapshot>;
|
||||
export class DocumentGenerationService<TSnapshot, TContext = Record<string, unknown>> {
|
||||
private readonly renderer: IDocumentRenderer<
|
||||
TSnapshot,
|
||||
TContext & DocumentGenerationServiceRendererParams
|
||||
>;
|
||||
private readonly metadataFactory: IDocumentMetadataFactory<TSnapshot, TContext>;
|
||||
private readonly propertiesFactory: IDocumentPropertiesFactory<TSnapshot, TContext>;
|
||||
private readonly preProcessors: readonly IDocumentPreProcessor[];
|
||||
private readonly postProcessor: IDocumentPostProcessor;
|
||||
private readonly sideEffects: readonly IDocumentSideEffect[];
|
||||
|
||||
constructor(deps: DocumentGenerationServiceDeps<TSnapshot>) {
|
||||
constructor(deps: DocumentGenerationServiceDeps<TSnapshot, TContext>) {
|
||||
this.renderer = deps.renderer;
|
||||
this.metadataFactory = deps.metadataFactory;
|
||||
this.propertiesFactory = deps.propertiesFactory;
|
||||
@ -51,7 +60,7 @@ export class DocumentGenerationService<TSnapshot> {
|
||||
|
||||
async generate(
|
||||
snapshot: TSnapshot,
|
||||
params: DocumentGenerationServiceRenderParams
|
||||
params: TContext
|
||||
): Promise<Result<IDocument, DocumentGenerationError>> {
|
||||
let metadata: IDocumentMetadata;
|
||||
let properties: IDocumentProperties;
|
||||
@ -64,7 +73,7 @@ export class DocumentGenerationService<TSnapshot> {
|
||||
}
|
||||
|
||||
try {
|
||||
properties = this.propertiesFactory.build(snapshot);
|
||||
properties = this.propertiesFactory.build(snapshot, params);
|
||||
} catch (error) {
|
||||
return Result.fail(DocumentGenerationError.properties(error));
|
||||
}
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import type { IDocumentMetadata } from "../application-models";
|
||||
|
||||
export interface IDocumentMetadataFactory<TSnapshot, TSnapshotContext = Record<string, string>> {
|
||||
export interface IDocumentMetadataFactory<TSnapshot, TSnapshotContext = Record<string, unknown>> {
|
||||
build(snapshot: TSnapshot, context: TSnapshotContext): IDocumentMetadata;
|
||||
}
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import type { IDocumentProperties } from "../application-models";
|
||||
|
||||
export interface IDocumentPropertiesFactory<TSnapshot> {
|
||||
build(snapshot: TSnapshot): IDocumentProperties;
|
||||
export interface IDocumentPropertiesFactory<TSnapshot, TContext = Record<string, unknown>> {
|
||||
build(snapshot: TSnapshot, context: TContext): IDocumentProperties;
|
||||
}
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import type { IDocument } from "../application-models";
|
||||
|
||||
export interface IDocumentRenderer<TSource> {
|
||||
render(source: TSource, params?: Record<string, unknown>): Promise<IDocument>;
|
||||
export interface IDocumentRenderer<TSource, TRenderParams = Record<string, unknown>> {
|
||||
render(source: TSource, params: TRenderParams): Promise<IDocument>;
|
||||
}
|
||||
|
||||
@ -18,6 +18,7 @@ import type {
|
||||
IProformaStatusChanger,
|
||||
IProformaUpdater,
|
||||
ProformaDocumentGeneratorService,
|
||||
ProformaPdfDocumentGenerationContext,
|
||||
} from "../services";
|
||||
import type {
|
||||
IIssueProformaResponseSnapshotBuilder,
|
||||
@ -88,7 +89,7 @@ export function buildReportProformaPdfUseCase(deps: {
|
||||
fullReadModelAssembler: IProformaFullReadModelAssembler;
|
||||
fullSnapshotBuilder: IProformaFullSnapshotBuilder;
|
||||
reportSnapshotBuilder: IProformaReportSnapshotBuilder;
|
||||
documentService: ProformaDocumentGeneratorService;
|
||||
documentService: ProformaDocumentGeneratorService<ProformaPdfDocumentGenerationContext>;
|
||||
transactionManager: ITransactionManager;
|
||||
}) {
|
||||
return new ReportProformaPdfUseCase({
|
||||
|
||||
@ -4,6 +4,7 @@ export * from "./company-report-profile-finder";
|
||||
export * from "./proforma-archiver";
|
||||
export * from "./proforma-creator";
|
||||
export * from "./proforma-deleter";
|
||||
export * from "./proforma-document-generation-context.interface";
|
||||
export * from "./proforma-document-generator.interface";
|
||||
export * from "./proforma-document-properties-factory";
|
||||
export * from "./proforma-duplicator";
|
||||
|
||||
@ -0,0 +1,17 @@
|
||||
/**
|
||||
* Datos externos al snapshot necesarios para resolver y generar una proforma.
|
||||
*
|
||||
* Los identificadores funcionales permanecen en el snapshot para evitar
|
||||
* duplicar fuentes de verdad entre el caso de uso y el pipeline.
|
||||
*/
|
||||
export interface ProformaDocumentGenerationContext {
|
||||
readonly companySlug: string;
|
||||
readonly languageCode: string;
|
||||
readonly companyLegalName: string;
|
||||
}
|
||||
|
||||
/** Datos adicionales requeridos exclusivamente por el pipeline PDF y su cache. */
|
||||
export interface ProformaPdfDocumentGenerationContext extends ProformaDocumentGenerationContext {
|
||||
readonly currencyCode: string;
|
||||
readonly companyVersion?: string;
|
||||
}
|
||||
@ -2,5 +2,8 @@ import type { DocumentGenerationService } from "@erp/core/api";
|
||||
|
||||
import type { ProformaReportSnapshot } from "../snapshot-builders";
|
||||
|
||||
export interface ProformaDocumentGeneratorService
|
||||
extends DocumentGenerationService<ProformaReportSnapshot> {}
|
||||
import type { ProformaDocumentGenerationContext } from "./proforma-document-generation-context.interface";
|
||||
|
||||
export interface ProformaDocumentGeneratorService<
|
||||
TContext extends ProformaDocumentGenerationContext = ProformaDocumentGenerationContext,
|
||||
> extends DocumentGenerationService<ProformaReportSnapshot, TContext> {}
|
||||
|
||||
@ -2,6 +2,8 @@ import type { IDocumentProperties, IDocumentPropertiesFactory } from "@erp/core/
|
||||
|
||||
import type { ProformaReportSnapshot } from "../snapshot-builders";
|
||||
|
||||
import type { ProformaDocumentGenerationContext } from "./proforma-document-generation-context.interface";
|
||||
|
||||
/**
|
||||
* Construye los metadatos del documento PDF de una factura emitida.
|
||||
*
|
||||
@ -10,13 +12,16 @@ import type { ProformaReportSnapshot } from "../snapshot-builders";
|
||||
* - Sin IO
|
||||
*/
|
||||
export class ProformaDocumentPropertiesFactory
|
||||
implements IDocumentPropertiesFactory<ProformaReportSnapshot>
|
||||
implements IDocumentPropertiesFactory<ProformaReportSnapshot, ProformaDocumentGenerationContext>
|
||||
{
|
||||
build(snapshot: ProformaReportSnapshot): IDocumentProperties {
|
||||
build(
|
||||
snapshot: ProformaReportSnapshot,
|
||||
context: ProformaDocumentGenerationContext
|
||||
): IDocumentProperties {
|
||||
return {
|
||||
title: snapshot.reference ?? snapshot.proforma_reference,
|
||||
subject: "proforma",
|
||||
author: snapshot.company.legalName,
|
||||
author: context.companyLegalName,
|
||||
creator: "FactuGES ERP",
|
||||
};
|
||||
}
|
||||
|
||||
@ -2,7 +2,7 @@ import assert from "node:assert/strict";
|
||||
import test from "node:test";
|
||||
|
||||
import { UniqueID } from "@repo/rdx-ddd";
|
||||
import { Result } from "@repo/rdx-utils";
|
||||
import { Maybe, Result } from "@repo/rdx-utils";
|
||||
|
||||
import { PreviewProformaReportUseCase } from "./preview-proforma-report.use-case";
|
||||
|
||||
@ -41,3 +41,49 @@ test("busca proformas archivadas al generar la vista previa", async () => {
|
||||
assert.equal(activeFinderCalls, 0);
|
||||
assert.equal(includingArchivedFinderCalls, 1);
|
||||
});
|
||||
|
||||
test("genera la vista previa con el contexto documental mínimo", async () => {
|
||||
const companyId = UniqueID.generateNewID();
|
||||
const proformaId = UniqueID.generateNewID();
|
||||
let receivedContext: unknown;
|
||||
|
||||
const useCase = new PreviewProformaReportUseCase({
|
||||
finder: { findProformaByIdIncludingArchived: async () => Result.ok({} as never) },
|
||||
companyReportProfileFinder: {
|
||||
findByCompanyId: async () =>
|
||||
Result.ok(
|
||||
Maybe.some({
|
||||
slug: "rodax",
|
||||
legalName: "Rodax Software",
|
||||
locale: Maybe.some("es-ES"),
|
||||
currencyCode: Maybe.some("EUR"),
|
||||
} as never)
|
||||
),
|
||||
},
|
||||
fullReadModelAssembler: { assemble: async () => Result.ok({} as never) },
|
||||
fullSnapshotBuilder: {
|
||||
toOutput: () => ({ language_code: "es", currency_code: "EUR" }) as never,
|
||||
},
|
||||
reportSnapshotBuilder: { toOutput: () => ({}) as never },
|
||||
documentService: {
|
||||
generate: async (_snapshot, context) => {
|
||||
receivedContext = context;
|
||||
return Result.ok({
|
||||
payload: Buffer.from("<html />"),
|
||||
filename: "proforma-P-1.html",
|
||||
mimeType: "text/html",
|
||||
});
|
||||
},
|
||||
} as never,
|
||||
transactionManager: { complete: async (work) => work({} as never) },
|
||||
});
|
||||
|
||||
const result = await useCase.execute({ companyId, proforma_id: proformaId.toString() });
|
||||
|
||||
assert.equal(result.isSuccess, true);
|
||||
assert.deepEqual(receivedContext, {
|
||||
companySlug: "rodax",
|
||||
languageCode: "es",
|
||||
companyLegalName: "Rodax Software",
|
||||
});
|
||||
});
|
||||
|
||||
@ -91,7 +91,9 @@ export class PreviewProformaReportUseCase {
|
||||
});
|
||||
|
||||
const documentResult = await this.deps.documentService.generate(reportSnapshot, {
|
||||
companySlug: reportSnapshot.company.slug,
|
||||
companySlug: companyProfileResult.data.unwrap().slug,
|
||||
languageCode: localization.languageCode,
|
||||
companyLegalName: companyProfileResult.data.unwrap().legalName,
|
||||
});
|
||||
|
||||
if (documentResult.isFailure) {
|
||||
|
||||
@ -2,7 +2,7 @@ import assert from "node:assert/strict";
|
||||
import test from "node:test";
|
||||
|
||||
import { UniqueID } from "@repo/rdx-ddd";
|
||||
import { Result } from "@repo/rdx-utils";
|
||||
import { Maybe, Result } from "@repo/rdx-utils";
|
||||
|
||||
import { ReportProformaPdfUseCase } from "./report-proforma-pdf.use-case";
|
||||
|
||||
@ -41,3 +41,59 @@ test("busca proformas archivadas al generar el PDF", async () => {
|
||||
assert.equal(activeFinderCalls, 0);
|
||||
assert.equal(includingArchivedFinderCalls, 1);
|
||||
});
|
||||
|
||||
test("genera el PDF con el contexto documental mínimo", async () => {
|
||||
const companyId = UniqueID.generateNewID();
|
||||
const proformaId = UniqueID.generateNewID();
|
||||
const reportSnapshot = {
|
||||
id: proformaId.toString(),
|
||||
company_id: companyId.toString(),
|
||||
proforma_reference: "P-1",
|
||||
} as never;
|
||||
let receivedContext: unknown;
|
||||
|
||||
const useCase = new ReportProformaPdfUseCase({
|
||||
finder: {
|
||||
findProformaByIdIncludingArchived: async () => Result.ok({} as never),
|
||||
},
|
||||
companyReportProfileFinder: {
|
||||
findByCompanyId: async () =>
|
||||
Result.ok(
|
||||
Maybe.some({
|
||||
slug: "rodax",
|
||||
legalName: "Rodax Software",
|
||||
locale: Maybe.some("es-ES"),
|
||||
currencyCode: Maybe.some("EUR"),
|
||||
version: Maybe.some("3"),
|
||||
} as never)
|
||||
),
|
||||
},
|
||||
fullReadModelAssembler: { assemble: async () => Result.ok({} as never) },
|
||||
fullSnapshotBuilder: {
|
||||
toOutput: () => ({ language_code: "es", currency_code: "EUR" }) as never,
|
||||
},
|
||||
reportSnapshotBuilder: { toOutput: () => reportSnapshot },
|
||||
documentService: {
|
||||
generate: async (_snapshot, context) => {
|
||||
receivedContext = context;
|
||||
return Result.ok({
|
||||
payload: Buffer.from("pdf"),
|
||||
filename: "proforma-P-1.pdf",
|
||||
mimeType: "application/pdf",
|
||||
});
|
||||
},
|
||||
} as never,
|
||||
transactionManager: { complete: async (work) => work({} as never) },
|
||||
});
|
||||
|
||||
const result = await useCase.execute({ companyId, proforma_id: proformaId.toString() });
|
||||
|
||||
assert.equal(result.isSuccess, true);
|
||||
assert.deepEqual(receivedContext, {
|
||||
companySlug: "rodax",
|
||||
languageCode: "es",
|
||||
companyLegalName: "Rodax Software",
|
||||
currencyCode: "EUR",
|
||||
companyVersion: "3",
|
||||
});
|
||||
});
|
||||
|
||||
@ -9,6 +9,7 @@ import type {
|
||||
IProformaFinder,
|
||||
IProformaFullReadModelAssembler,
|
||||
ProformaDocumentGeneratorService,
|
||||
ProformaPdfDocumentGenerationContext,
|
||||
} from "../services";
|
||||
import type { IProformaFullSnapshotBuilder } from "../snapshot-builders";
|
||||
import type { IProformaReportSnapshotBuilder } from "../snapshot-builders/report";
|
||||
@ -26,7 +27,7 @@ export class ReportProformaPdfUseCase {
|
||||
fullReadModelAssembler: IProformaFullReadModelAssembler;
|
||||
fullSnapshotBuilder: IProformaFullSnapshotBuilder;
|
||||
reportSnapshotBuilder: IProformaReportSnapshotBuilder;
|
||||
documentService: ProformaDocumentGeneratorService;
|
||||
documentService: ProformaDocumentGeneratorService<ProformaPdfDocumentGenerationContext>;
|
||||
transactionManager: ITransactionManager;
|
||||
}
|
||||
) {}
|
||||
@ -93,8 +94,11 @@ export class ReportProformaPdfUseCase {
|
||||
});
|
||||
|
||||
const documentResult = await this.deps.documentService.generate(reportSnapshot, {
|
||||
//localization,
|
||||
company: companyProfile,
|
||||
companySlug: companyProfile.slug,
|
||||
languageCode: localization.languageCode,
|
||||
companyLegalName: companyProfile.legalName,
|
||||
currencyCode: localization.currencyCode,
|
||||
companyVersion: companyProfile.version.getOrUndefined(),
|
||||
});
|
||||
|
||||
if (documentResult.isFailure) {
|
||||
|
||||
@ -6,6 +6,7 @@ import {
|
||||
} from "@erp/core/api";
|
||||
|
||||
import {
|
||||
type ProformaDocumentGenerationContext,
|
||||
type ProformaDocumentGeneratorService,
|
||||
ProformaDocumentPropertiesFactory,
|
||||
type ProformaReportSnapshot,
|
||||
@ -28,13 +29,15 @@ export class ProformaHtmlPreviewPipelineFactory {
|
||||
const propertiesFactory = new ProformaDocumentPropertiesFactory();
|
||||
const postProcessor = new DocumentPostProcessorChain([]);
|
||||
|
||||
return new DocumentGenerationService<ProformaReportSnapshot>({
|
||||
renderer,
|
||||
preProcessors: [],
|
||||
postProcessor,
|
||||
sideEffects: [],
|
||||
metadataFactory,
|
||||
propertiesFactory,
|
||||
});
|
||||
return new DocumentGenerationService<ProformaReportSnapshot, ProformaDocumentGenerationContext>(
|
||||
{
|
||||
renderer,
|
||||
preProcessors: [],
|
||||
postProcessor,
|
||||
sideEffects: [],
|
||||
metadataFactory,
|
||||
propertiesFactory,
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -10,6 +10,7 @@ import {
|
||||
import {
|
||||
type ProformaDocumentGeneratorService,
|
||||
ProformaDocumentPropertiesFactory,
|
||||
type ProformaPdfDocumentGenerationContext,
|
||||
type ProformaReportSnapshot,
|
||||
} from "../../../../application";
|
||||
import { ProformaPdfCachePreProcessor } from "../pre-processors";
|
||||
@ -24,7 +25,9 @@ export interface ProformaPdfDocumentPipelineFactoryDeps {
|
||||
}
|
||||
|
||||
export class ProformaPdfDocumentPipelineFactory {
|
||||
static create(deps: ProformaPdfDocumentPipelineFactoryDeps): ProformaDocumentGeneratorService {
|
||||
static create(
|
||||
deps: ProformaPdfDocumentPipelineFactoryDeps
|
||||
): ProformaDocumentGeneratorService<ProformaPdfDocumentGenerationContext> {
|
||||
const preProcessors = [new ProformaPdfCachePreProcessor(deps.documentStorage)];
|
||||
const renderer = new ProformaPdfDocumentRenderer(
|
||||
deps.fastReportRenderer,
|
||||
@ -37,7 +40,10 @@ export class ProformaPdfDocumentPipelineFactory {
|
||||
new PersistProformaPdfSideEffect(deps.documentStorage),
|
||||
];
|
||||
|
||||
return new DocumentGenerationService<ProformaReportSnapshot>({
|
||||
return new DocumentGenerationService<
|
||||
ProformaReportSnapshot,
|
||||
ProformaPdfDocumentGenerationContext
|
||||
>({
|
||||
renderer,
|
||||
preProcessors,
|
||||
postProcessor,
|
||||
|
||||
@ -6,18 +6,21 @@ import type {
|
||||
IDocumentRenderer,
|
||||
} from "@erp/core/api";
|
||||
|
||||
import type { ProformaReportSnapshot } from "../../../../../application";
|
||||
import type {
|
||||
ProformaDocumentGenerationContext,
|
||||
ProformaReportSnapshot,
|
||||
} from "../../../../../application";
|
||||
|
||||
type ProformaHtmlPreviewRenderParams = {
|
||||
companySlug: string;
|
||||
format: string;
|
||||
languageCode: string;
|
||||
type ProformaHtmlPreviewRenderParams = ProformaDocumentGenerationContext & {
|
||||
format: "PDF" | "HTML";
|
||||
filename: string;
|
||||
mimeType: string;
|
||||
properties: IDocumentProperties;
|
||||
};
|
||||
|
||||
export class ProformaHtmlPreviewRenderer implements IDocumentRenderer<ProformaReportSnapshot> {
|
||||
export class ProformaHtmlPreviewRenderer
|
||||
implements IDocumentRenderer<ProformaReportSnapshot, ProformaHtmlPreviewRenderParams>
|
||||
{
|
||||
public constructor(
|
||||
private readonly fastReportRenderer: FastReportRenderer,
|
||||
private readonly templateResolver: FastReportTemplateResolver
|
||||
|
||||
@ -6,18 +6,21 @@ import type {
|
||||
IDocumentRenderer,
|
||||
} from "@erp/core/api";
|
||||
|
||||
import type { ProformaReportSnapshot } from "../../../../../application";
|
||||
import type {
|
||||
ProformaPdfDocumentGenerationContext,
|
||||
ProformaReportSnapshot,
|
||||
} from "../../../../../application";
|
||||
|
||||
type ProformaPdfDocumentRenderParams = {
|
||||
companySlug: string;
|
||||
format: string;
|
||||
languageCode: string;
|
||||
type ProformaPdfDocumentRenderParams = ProformaPdfDocumentGenerationContext & {
|
||||
format: "PDF" | "HTML";
|
||||
filename: string;
|
||||
mimeType: string;
|
||||
properties: IDocumentProperties;
|
||||
};
|
||||
|
||||
export class ProformaPdfDocumentRenderer implements IDocumentRenderer<ProformaReportSnapshot> {
|
||||
export class ProformaPdfDocumentRenderer
|
||||
implements IDocumentRenderer<ProformaReportSnapshot, ProformaPdfDocumentRenderParams>
|
||||
{
|
||||
public constructor(
|
||||
private readonly fastReportRenderer: FastReportRenderer,
|
||||
private readonly templateResolver: FastReportTemplateResolver
|
||||
|
||||
@ -0,0 +1,117 @@
|
||||
import assert from "node:assert/strict";
|
||||
import test from "node:test";
|
||||
|
||||
import { ProformaDocumentPropertiesFactory } from "../../../../application";
|
||||
import { ProformaHtmlPreviewRenderer } from "../renderers/fastreport/proforma-html-preview-renderer";
|
||||
import { ProformaPdfDocumentRenderer } from "../renderers/fastreport/proforma-pdf-document-renderer";
|
||||
|
||||
import { ProformaHtmlPreviewDocumentMetadataFactory } from "./proforma-html-preview-document-metadata-factory";
|
||||
import { ProformaPdfDocumentMetadataFactory } from "./proforma-pdf-document-metadata-factory";
|
||||
|
||||
const snapshot = {
|
||||
id: "proforma-id",
|
||||
company_id: "company-id",
|
||||
proforma_reference: "P-1",
|
||||
status: "issued",
|
||||
} as never;
|
||||
|
||||
test("construye metadata PDF desde snapshot y contexto tipado", () => {
|
||||
const metadata = new ProformaPdfDocumentMetadataFactory().build(snapshot, {
|
||||
companySlug: "rodax",
|
||||
languageCode: "es",
|
||||
companyLegalName: "Rodax Software",
|
||||
currencyCode: "EUR",
|
||||
companyVersion: "3",
|
||||
});
|
||||
|
||||
assert.deepEqual(metadata, {
|
||||
documentType: "proforma",
|
||||
documentId: "proforma-id",
|
||||
companyId: "company-id",
|
||||
companySlug: "rodax",
|
||||
format: "PDF",
|
||||
languageCode: "es",
|
||||
filename: "proforma-P-1.pdf",
|
||||
storageKey: "proforma:company-id:proforma-id:issued:pdf:es:EUR:rodax:3",
|
||||
});
|
||||
});
|
||||
|
||||
test("construye metadata HTML sin propiedades inexistentes del snapshot", () => {
|
||||
const metadata = new ProformaHtmlPreviewDocumentMetadataFactory().build(snapshot, {
|
||||
companySlug: "rodax",
|
||||
languageCode: "es",
|
||||
companyLegalName: "Rodax Software",
|
||||
});
|
||||
|
||||
assert.deepEqual(metadata, {
|
||||
documentType: "proforma",
|
||||
documentId: "proforma-id",
|
||||
companyId: "company-id",
|
||||
companySlug: "rodax",
|
||||
format: "HTML",
|
||||
languageCode: "es",
|
||||
filename: "proforma-P-1.html",
|
||||
});
|
||||
});
|
||||
|
||||
test("construye las propiedades desde el contexto sin leer company del snapshot", () => {
|
||||
const properties = new ProformaDocumentPropertiesFactory().build(snapshot, {
|
||||
companySlug: "rodax",
|
||||
languageCode: "es",
|
||||
companyLegalName: "Rodax Software",
|
||||
});
|
||||
|
||||
assert.deepEqual(properties, {
|
||||
title: "P-1",
|
||||
subject: "proforma",
|
||||
author: "Rodax Software",
|
||||
creator: "FactuGES ERP",
|
||||
});
|
||||
});
|
||||
|
||||
test("entrega companySlug y languageCode al resolver la plantilla en ambos renderers", async () => {
|
||||
const resolvedTemplates: unknown[] = [];
|
||||
const templateResolver = {
|
||||
resolveTemplatePath: (params: unknown) => {
|
||||
resolvedTemplates.push(params);
|
||||
return "/tmp/proforma.frx";
|
||||
},
|
||||
};
|
||||
const fastReportRenderer = {
|
||||
render: async () => ({ payload: Buffer.from("document") }),
|
||||
};
|
||||
const renderParams = {
|
||||
companySlug: "rodax",
|
||||
languageCode: "es",
|
||||
companyLegalName: "Rodax Software",
|
||||
currencyCode: "EUR",
|
||||
format: "PDF" as const,
|
||||
filename: "proforma-P-1.pdf",
|
||||
mimeType: "application/pdf",
|
||||
properties: {},
|
||||
};
|
||||
|
||||
await new ProformaPdfDocumentRenderer(
|
||||
fastReportRenderer as never,
|
||||
templateResolver as never
|
||||
).render(snapshot, renderParams);
|
||||
await new ProformaHtmlPreviewRenderer(
|
||||
fastReportRenderer as never,
|
||||
templateResolver as never
|
||||
).render(snapshot, renderParams);
|
||||
|
||||
assert.deepEqual(resolvedTemplates, [
|
||||
{
|
||||
module: "customer-invoices",
|
||||
companySlug: "rodax",
|
||||
languageCode: "es",
|
||||
templateFilename: "proforma.frx",
|
||||
},
|
||||
{
|
||||
module: "customer-invoices",
|
||||
companySlug: "rodax",
|
||||
languageCode: "es",
|
||||
templateFilename: "proforma.frx",
|
||||
},
|
||||
]);
|
||||
});
|
||||
@ -1,18 +1,24 @@
|
||||
import type { IDocumentMetadata, IDocumentMetadataFactory } from "@erp/core/api";
|
||||
|
||||
import type { ProformaReportSnapshot } from "../../../../application";
|
||||
import type {
|
||||
ProformaDocumentGenerationContext,
|
||||
ProformaReportSnapshot,
|
||||
} from "../../../../application";
|
||||
|
||||
export class ProformaHtmlPreviewDocumentMetadataFactory
|
||||
implements IDocumentMetadataFactory<ProformaReportSnapshot>
|
||||
implements IDocumentMetadataFactory<ProformaReportSnapshot, ProformaDocumentGenerationContext>
|
||||
{
|
||||
build(snapshot: ProformaReportSnapshot): IDocumentMetadata {
|
||||
build(
|
||||
snapshot: ProformaReportSnapshot,
|
||||
context: ProformaDocumentGenerationContext
|
||||
): IDocumentMetadata {
|
||||
return {
|
||||
documentType: "proforma",
|
||||
documentId: snapshot.id,
|
||||
companyId: snapshot.company_id,
|
||||
companySlug: snapshot.company.slug,
|
||||
companySlug: context.companySlug,
|
||||
format: "HTML",
|
||||
languageCode: snapshot.localization.languageCode,
|
||||
languageCode: context.languageCode,
|
||||
filename: `proforma-${snapshot.proforma_reference}.html`,
|
||||
};
|
||||
}
|
||||
|
||||
@ -1,26 +1,32 @@
|
||||
import type { IDocumentMetadata, IDocumentMetadataFactory } from "@erp/core/api";
|
||||
|
||||
import type { ProformaReportSnapshot } from "../../../../application";
|
||||
import type {
|
||||
ProformaPdfDocumentGenerationContext,
|
||||
ProformaReportSnapshot,
|
||||
} from "../../../../application";
|
||||
|
||||
export class ProformaPdfDocumentMetadataFactory
|
||||
implements IDocumentMetadataFactory<ProformaReportSnapshot>
|
||||
implements IDocumentMetadataFactory<ProformaReportSnapshot, ProformaPdfDocumentGenerationContext>
|
||||
{
|
||||
build(snapshot: ProformaReportSnapshot, context: Record<string, string>): IDocumentMetadata {
|
||||
build(
|
||||
snapshot: ProformaReportSnapshot,
|
||||
context: ProformaPdfDocumentGenerationContext
|
||||
): IDocumentMetadata {
|
||||
return {
|
||||
documentType: "proforma",
|
||||
documentId: snapshot.id,
|
||||
companyId: context.company_id,
|
||||
companySlug: context.company.slug,
|
||||
companyId: snapshot.company_id,
|
||||
companySlug: context.companySlug,
|
||||
format: "PDF",
|
||||
languageCode: context.localization.languageCode,
|
||||
languageCode: context.languageCode,
|
||||
filename: `proforma-${snapshot.proforma_reference}.pdf`,
|
||||
storageKey: this.buildStorageKey(snapshot),
|
||||
storageKey: this.buildStorageKey(snapshot, context),
|
||||
};
|
||||
}
|
||||
|
||||
private buildStorageKey(
|
||||
snapshot: ProformaReportSnapshot,
|
||||
context: Record<string, string>
|
||||
context: ProformaPdfDocumentGenerationContext
|
||||
): string | undefined {
|
||||
if (snapshot.status === "draft") {
|
||||
return undefined;
|
||||
@ -35,15 +41,13 @@ export class ProformaPdfDocumentMetadataFactory
|
||||
snapshot.id,
|
||||
snapshot.status,
|
||||
"pdf",
|
||||
context.localization.languageCode,
|
||||
context.localization.currencyCode,
|
||||
context.company.slug,
|
||||
context.languageCode,
|
||||
context.currencyCode,
|
||||
context.companySlug,
|
||||
];
|
||||
|
||||
const companyVersion = context.company.version.getOrUndefined();
|
||||
|
||||
if (companyVersion) {
|
||||
keyParts.push(companyVersion);
|
||||
if (context.companyVersion) {
|
||||
keyParts.push(context.companyVersion);
|
||||
}
|
||||
|
||||
return keyParts.join(":");
|
||||
|
||||
Loading…
Reference in New Issue
Block a user