.
This commit is contained in:
parent
056e78548e
commit
821b4d3ff7
@ -93,7 +93,9 @@ export class DocumentGenerationService<TSnapshot> {
|
|||||||
properties,
|
properties,
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
return Result.fail(DocumentGenerationError.render(error));
|
const err = error as Error;
|
||||||
|
logger.error(err.message, err);
|
||||||
|
return Result.fail(DocumentGenerationError.render(err));
|
||||||
}
|
}
|
||||||
|
|
||||||
// 4. Post-processors (transformaciones)
|
// 4. Post-processors (transformaciones)
|
||||||
@ -101,8 +103,9 @@ export class DocumentGenerationService<TSnapshot> {
|
|||||||
try {
|
try {
|
||||||
document = await this.postProcessor.process(document, metadata);
|
document = await this.postProcessor.process(document, metadata);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(error);
|
const err = error as Error;
|
||||||
return Result.fail(DocumentGenerationError.postProcess(error));
|
logger.error(err.message, err);
|
||||||
|
return Result.fail(DocumentGenerationError.postProcess(err));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -4,6 +4,7 @@ import os from "node:os";
|
|||||||
import path from "node:path";
|
import path from "node:path";
|
||||||
|
|
||||||
import { Renderer } from "../../../../application";
|
import { Renderer } from "../../../../application";
|
||||||
|
import { logger } from "../../../logger";
|
||||||
|
|
||||||
import { FastReportExecutionError, FastReportIOError } from "./fastreport-errors";
|
import { FastReportExecutionError, FastReportIOError } from "./fastreport-errors";
|
||||||
import type { FastReportExecutableResolver } from "./fastreport-executable-resolver";
|
import type { FastReportExecutableResolver } from "./fastreport-executable-resolver";
|
||||||
@ -23,34 +24,79 @@ export class FastReportRenderer extends Renderer<unknown, FastReportRenderOutput
|
|||||||
}
|
}
|
||||||
|
|
||||||
async render(options: FastReportRenderOptions): Promise<FastReportRenderOutput> {
|
async render(options: FastReportRenderOptions): Promise<FastReportRenderOutput> {
|
||||||
|
if (!options.templatePath) {
|
||||||
|
const message = "Option 'templatePath' is required";
|
||||||
|
logger.error(message, {
|
||||||
|
label: "FastReportRenderer.render",
|
||||||
|
options,
|
||||||
|
});
|
||||||
|
throw new FastReportExecutionError(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!["PDF", "HTML"].includes(options.format)) {
|
||||||
|
const message = `Unsupported format: ${options.format}`;
|
||||||
|
logger.error(message, {
|
||||||
|
label: "FastReportRenderer.render",
|
||||||
|
options,
|
||||||
|
});
|
||||||
|
throw new FastReportExecutionError(message);
|
||||||
|
}
|
||||||
|
|
||||||
const workDir = path.join(os.tmpdir(), "fastreport", randomUUID());
|
const workDir = path.join(os.tmpdir(), "fastreport", randomUUID());
|
||||||
const inputPath = path.join(workDir, "input.json");
|
const inputPath = path.join(workDir, "input.json");
|
||||||
const outputPath = path.join(workDir, options.format === "PDF" ? "output.pdf" : "output.html");
|
const outputPath = path.join(workDir, options.format === "PDF" ? "output.pdf" : "output.html");
|
||||||
|
|
||||||
await mkdir(workDir, { recursive: true });
|
const params = {
|
||||||
|
templatePath: options.templatePath,
|
||||||
|
data: inputPath,
|
||||||
|
output: outputPath,
|
||||||
|
format: options.format,
|
||||||
|
properties: options.properties,
|
||||||
|
};
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await this.ensureWorkDir(workDir);
|
|
||||||
await writeFile(inputPath, JSON.stringify(options.inputData), "utf-8");
|
|
||||||
|
|
||||||
const executablePath = this.executableResolver.resolve();
|
const executablePath = this.executableResolver.resolve();
|
||||||
|
|
||||||
await this.processRunner.run(executablePath, {
|
await mkdir(workDir, { recursive: true });
|
||||||
templatePath: options.templatePath,
|
await this.ensureWorkDir(workDir);
|
||||||
data: inputPath,
|
|
||||||
output: outputPath,
|
await writeFile(inputPath, JSON.stringify(options.inputData), "utf-8");
|
||||||
format: options.format,
|
|
||||||
properties: options.properties,
|
const result = await this.processRunner.run(executablePath, params);
|
||||||
});
|
if (result.isFailure) {
|
||||||
|
logger.error(result.error.message, {
|
||||||
|
label: "FastReportRenderer.render",
|
||||||
|
params,
|
||||||
|
});
|
||||||
|
throw new FastReportExecutionError(result.error.message, { cause: result.error });
|
||||||
|
}
|
||||||
|
|
||||||
|
// comprobar salida
|
||||||
|
await access(outputPath);
|
||||||
|
|
||||||
const payload = await readFile(outputPath);
|
const payload = await readFile(outputPath);
|
||||||
|
|
||||||
|
if (!payload || payload.length === 0) {
|
||||||
|
logger.error("Output file is empty", {
|
||||||
|
label: "FastReportRenderer.render",
|
||||||
|
params,
|
||||||
|
});
|
||||||
|
throw new FastReportExecutionError("Output file is empty");
|
||||||
|
}
|
||||||
|
|
||||||
|
const checksum = await this.computeTemplateChecksum(options.templatePath);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
payload,
|
payload,
|
||||||
templateChecksum: await this.computeTemplateChecksum(options.templatePath),
|
templateChecksum: checksum,
|
||||||
};
|
};
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
throw new FastReportExecutionError((error as Error).message);
|
const err = error as Error;
|
||||||
|
logger.error(err.message, {
|
||||||
|
label: "FastReportRenderer.render",
|
||||||
|
params,
|
||||||
|
});
|
||||||
|
throw new FastReportExecutionError(err.message, { cause: err });
|
||||||
} finally {
|
} finally {
|
||||||
await this.safeCleanup(workDir);
|
await this.safeCleanup(workDir);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import type { ITransactionManager, RendererFormat } from "@erp/core/api";
|
import { type ITransactionManager, type RendererFormat, logger } from "@erp/core/api";
|
||||||
import { UniqueID } from "@repo/rdx-ddd";
|
import { UniqueID } from "@repo/rdx-ddd";
|
||||||
import { Result } from "@repo/rdx-utils";
|
import { Result } from "@repo/rdx-utils";
|
||||||
|
|
||||||
@ -9,7 +9,7 @@ import type { IProformaReportSnapshotBuilder } from "../snapshot-builders/report
|
|||||||
type ReportProformaUseCaseInput = {
|
type ReportProformaUseCaseInput = {
|
||||||
companyId: UniqueID;
|
companyId: UniqueID;
|
||||||
companySlug: string;
|
companySlug: string;
|
||||||
invoice_id: string;
|
proforma_id: string;
|
||||||
format: RendererFormat;
|
format: RendererFormat;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -23,25 +23,29 @@ export class ReportProformaUseCase {
|
|||||||
) {}
|
) {}
|
||||||
|
|
||||||
public async execute(params: ReportProformaUseCaseInput) {
|
public async execute(params: ReportProformaUseCaseInput) {
|
||||||
const { invoice_id, companyId } = params;
|
const { proforma_id, companyId } = params;
|
||||||
|
|
||||||
const idOrError = UniqueID.create(invoice_id);
|
const idOrError = UniqueID.create(proforma_id);
|
||||||
|
|
||||||
if (idOrError.isFailure) {
|
if (idOrError.isFailure) {
|
||||||
return Result.fail(idOrError.error);
|
return Result.fail(idOrError.error);
|
||||||
}
|
}
|
||||||
|
|
||||||
const invoiceId = idOrError.data;
|
const proformaId = idOrError.data;
|
||||||
|
|
||||||
return this.transactionManager.complete(async (transaction) => {
|
return this.transactionManager.complete(async (transaction) => {
|
||||||
try {
|
try {
|
||||||
const invoiceResult = await this.finder.findProformaById(companyId, invoiceId, transaction);
|
const proformaResult = await this.finder.findProformaById(
|
||||||
|
companyId,
|
||||||
|
proformaId,
|
||||||
|
transaction
|
||||||
|
);
|
||||||
|
|
||||||
if (invoiceResult.isFailure) {
|
if (proformaResult.isFailure) {
|
||||||
return Result.fail(invoiceResult.error);
|
return Result.fail(proformaResult.error);
|
||||||
}
|
}
|
||||||
|
|
||||||
const invoice = invoiceResult.data;
|
const invoice = proformaResult.data;
|
||||||
|
|
||||||
// Snapshot completo de la entidad
|
// Snapshot completo de la entidad
|
||||||
const fullSnapshot = this.fullSnapshotBuilder.toOutput(invoice);
|
const fullSnapshot = this.fullSnapshotBuilder.toOutput(invoice);
|
||||||
@ -67,7 +71,9 @@ export class ReportProformaUseCase {
|
|||||||
filename: documentResult.data.filename,
|
filename: documentResult.data.filename,
|
||||||
});
|
});
|
||||||
} catch (error: unknown) {
|
} catch (error: unknown) {
|
||||||
return Result.fail(error as Error);
|
const err = error as Error;
|
||||||
|
logger.error(err.message, { label: "ReportProformaUseCase.execure" });
|
||||||
|
return Result.fail(err);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2,11 +2,17 @@ import { mockUser, requireAuthenticated, requireCompanyContext } from "@erp/auth
|
|||||||
import { type ModuleParams, type RequestWithAuth, validateRequest } from "@erp/core/api";
|
import { type ModuleParams, type RequestWithAuth, validateRequest } from "@erp/core/api";
|
||||||
import { type NextFunction, type Request, type Response, Router } from "express";
|
import { type NextFunction, type Request, type Response, Router } from "express";
|
||||||
|
|
||||||
import { GetProformaByIdRequestSchema, ListProformasRequestSchema } from "../../../../common";
|
import {
|
||||||
|
GetProformaByIdRequestSchema,
|
||||||
|
ListProformasRequestSchema,
|
||||||
|
ReportProformaByIdParamsRequestSchema,
|
||||||
|
ReportProformaByIdQueryRequestSchema,
|
||||||
|
} from "../../../../common";
|
||||||
import {
|
import {
|
||||||
GetProformaController,
|
GetProformaController,
|
||||||
ListProformasController,
|
ListProformasController,
|
||||||
type ProformasInternalDeps,
|
type ProformasInternalDeps,
|
||||||
|
ReportProformaController,
|
||||||
} from "../../proformas";
|
} from "../../proformas";
|
||||||
|
|
||||||
export const proformasRouter = (params: ModuleParams, deps: ProformasInternalDeps) => {
|
export const proformasRouter = (params: ModuleParams, deps: ProformasInternalDeps) => {
|
||||||
@ -54,6 +60,19 @@ export const proformasRouter = (params: ModuleParams, deps: ProformasInternalDep
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
router.get(
|
||||||
|
"/:proforma_id/report",
|
||||||
|
//checkTabContext,
|
||||||
|
validateRequest(ReportProformaByIdParamsRequestSchema, "params"),
|
||||||
|
validateRequest(ReportProformaByIdQueryRequestSchema, "query"),
|
||||||
|
|
||||||
|
(req: Request, res: Response, next: NextFunction) => {
|
||||||
|
const useCase = deps.useCases.reportProforma();
|
||||||
|
const controller = new ReportProformaController(useCase);
|
||||||
|
return controller.execute(req, res, next);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
/*router.post(
|
/*router.post(
|
||||||
"/",
|
"/",
|
||||||
//checkTabContext,
|
//checkTabContext,
|
||||||
@ -89,19 +108,9 @@ export const proformasRouter = (params: ModuleParams, deps: ProformasInternalDep
|
|||||||
const controller = new DeleteProformaController(useCase);
|
const controller = new DeleteProformaController(useCase);
|
||||||
return controller.execute(req, res, next);
|
return controller.execute(req, res, next);
|
||||||
}
|
}
|
||||||
);
|
);*/
|
||||||
|
|
||||||
router.get(
|
/*
|
||||||
"/:proforma_id/report",
|
|
||||||
//checkTabContext,
|
|
||||||
validateRequest(ReportProformaByIdParamsRequestSchema, "params"),
|
|
||||||
validateRequest(ReportProformaByIdQueryRequestSchema, "query"),
|
|
||||||
(req: Request, res: Response, next: NextFunction) => {
|
|
||||||
const useCase = deps.useCases.reportIssuedInvoice();
|
|
||||||
const controller = new ReportProformaController(useCase);
|
|
||||||
return controller.execute(req, res, next);
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
router.patch(
|
router.patch(
|
||||||
"/:proforma_id/status",
|
"/:proforma_id/status",
|
||||||
|
|||||||
@ -5,8 +5,8 @@ import {
|
|||||||
requireAuthenticatedGuard,
|
requireAuthenticatedGuard,
|
||||||
requireCompanyContextGuard,
|
requireCompanyContextGuard,
|
||||||
} from "@erp/core/api";
|
} from "@erp/core/api";
|
||||||
import type { ReportIssueInvoiceByIdQueryRequestDTO } from "@erp/customer-invoices/common";
|
|
||||||
|
|
||||||
|
import type { ReportIssueInvoiceByIdQueryRequestDTO } from "../../../../../common";
|
||||||
import type { ReportIssuedInvoiceUseCase } from "../../../../application/index.ts";
|
import type { ReportIssuedInvoiceUseCase } from "../../../../application/index.ts";
|
||||||
import { customerInvoicesApiErrorMapper } from "../../../express/proformas/proformas-api-error-mapper.ts";
|
import { customerInvoicesApiErrorMapper } from "../../../express/proformas/proformas-api-error-mapper.ts";
|
||||||
|
|
||||||
|
|||||||
@ -45,7 +45,7 @@ export class ProformaDocumentRenderer implements IDocumentRenderer<ProformaRepor
|
|||||||
module: "customer-invoices",
|
module: "customer-invoices",
|
||||||
companySlug,
|
companySlug,
|
||||||
languageCode,
|
languageCode,
|
||||||
templateFilename: "issued-invoice.frx",
|
templateFilename: "proforma.frx",
|
||||||
});
|
});
|
||||||
|
|
||||||
const output = await this.fastReportRenderer.render({
|
const output = await this.fastReportRenderer.render({
|
||||||
|
|||||||
@ -1,10 +1,12 @@
|
|||||||
import {
|
import {
|
||||||
ExpressController,
|
ExpressController,
|
||||||
|
type RendererFormat,
|
||||||
forbidQueryFieldGuard,
|
forbidQueryFieldGuard,
|
||||||
requireAuthenticatedGuard,
|
requireAuthenticatedGuard,
|
||||||
requireCompanyContextGuard,
|
requireCompanyContextGuard,
|
||||||
} from "@erp/core/api";
|
} from "@erp/core/api";
|
||||||
|
|
||||||
|
import type { ReportProformaByIdQueryRequestDTO } from "../../../../../common";
|
||||||
import type { ReportProformaUseCase } from "../../../../application/index.ts";
|
import type { ReportProformaUseCase } from "../../../../application/index.ts";
|
||||||
import { customerInvoicesApiErrorMapper } from "../../../express/proformas/proformas-api-error-mapper.ts";
|
import { customerInvoicesApiErrorMapper } from "../../../express/proformas/proformas-api-error-mapper.ts";
|
||||||
|
|
||||||
@ -29,13 +31,26 @@ export class ReportProformaController extends ExpressController {
|
|||||||
|
|
||||||
const { companySlug } = this.getUser();
|
const { companySlug } = this.getUser();
|
||||||
const { proforma_id } = this.req.params;
|
const { proforma_id } = this.req.params;
|
||||||
const { format } = this.req.query as { format: "pdf" | "html" };
|
const { format } = this.req.query as ReportProformaByIdQueryRequestDTO;
|
||||||
|
|
||||||
const result = await this.useCase.execute({ proforma_id, companyId, companySlug, format });
|
const result = await this.useCase.execute({
|
||||||
|
proforma_id,
|
||||||
|
companyId,
|
||||||
|
companySlug,
|
||||||
|
format: format as RendererFormat,
|
||||||
|
});
|
||||||
|
|
||||||
return result.match(
|
return result.match(
|
||||||
({ data, filename }) =>
|
({ payload, filename }) => {
|
||||||
filename ? this.downloadPDF(data, filename) : this.downloadHTML(data as string),
|
if (format === "PDF") {
|
||||||
|
return this.downloadPDF(payload as Buffer<ArrayBuffer>, String(filename));
|
||||||
|
}
|
||||||
|
if (format === "HTML") {
|
||||||
|
return this.downloadHTML(payload as unknown as string);
|
||||||
|
}
|
||||||
|
// JSON
|
||||||
|
return this.json(payload);
|
||||||
|
},
|
||||||
(err) => this.handleError(err)
|
(err) => this.handleError(err)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -8,7 +8,12 @@ export type ReportProformaByIdParamsRequestDTO = z.infer<
|
|||||||
typeof ReportProformaByIdParamsRequestSchema
|
typeof ReportProformaByIdParamsRequestSchema
|
||||||
>;
|
>;
|
||||||
export const ReportProformaByIdQueryRequestSchema = z.object({
|
export const ReportProformaByIdQueryRequestSchema = z.object({
|
||||||
format: z.enum(["pdf", "html"]).default("pdf"),
|
format: z
|
||||||
|
.string()
|
||||||
|
.default("pdf")
|
||||||
|
.transform((v) => v.trim().toLowerCase())
|
||||||
|
.pipe(z.enum(["pdf", "html", "json"]))
|
||||||
|
.transform((v) => v.toUpperCase() as "PDF" | "HTML" | "JSON"),
|
||||||
});
|
});
|
||||||
|
|
||||||
export type ReportProformaByIdQueryRequestDTO = z.infer<
|
export type ReportProformaByIdQueryRequestDTO = z.infer<
|
||||||
|
|||||||
227
modules/customer-invoices/templates/rodax/es/proforma.frx
Executable file
227
modules/customer-invoices/templates/rodax/es/proforma.frx
Executable file
File diff suppressed because one or more lines are too long
Loading…
Reference in New Issue
Block a user