.
This commit is contained in:
parent
056e78548e
commit
821b4d3ff7
@ -93,7 +93,9 @@ export class DocumentGenerationService<TSnapshot> {
|
||||
properties,
|
||||
});
|
||||
} 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)
|
||||
@ -101,8 +103,9 @@ export class DocumentGenerationService<TSnapshot> {
|
||||
try {
|
||||
document = await this.postProcessor.process(document, metadata);
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
return Result.fail(DocumentGenerationError.postProcess(error));
|
||||
const err = error as 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 { Renderer } from "../../../../application";
|
||||
import { logger } from "../../../logger";
|
||||
|
||||
import { FastReportExecutionError, FastReportIOError } from "./fastreport-errors";
|
||||
import type { FastReportExecutableResolver } from "./fastreport-executable-resolver";
|
||||
@ -23,34 +24,79 @@ export class FastReportRenderer extends Renderer<unknown, 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 inputPath = path.join(workDir, "input.json");
|
||||
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 {
|
||||
await this.ensureWorkDir(workDir);
|
||||
await writeFile(inputPath, JSON.stringify(options.inputData), "utf-8");
|
||||
|
||||
const executablePath = this.executableResolver.resolve();
|
||||
|
||||
await this.processRunner.run(executablePath, {
|
||||
templatePath: options.templatePath,
|
||||
data: inputPath,
|
||||
output: outputPath,
|
||||
format: options.format,
|
||||
properties: options.properties,
|
||||
});
|
||||
await mkdir(workDir, { recursive: true });
|
||||
await this.ensureWorkDir(workDir);
|
||||
|
||||
await writeFile(inputPath, JSON.stringify(options.inputData), "utf-8");
|
||||
|
||||
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);
|
||||
|
||||
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 {
|
||||
payload,
|
||||
templateChecksum: await this.computeTemplateChecksum(options.templatePath),
|
||||
templateChecksum: checksum,
|
||||
};
|
||||
} 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 {
|
||||
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 { Result } from "@repo/rdx-utils";
|
||||
|
||||
@ -9,7 +9,7 @@ import type { IProformaReportSnapshotBuilder } from "../snapshot-builders/report
|
||||
type ReportProformaUseCaseInput = {
|
||||
companyId: UniqueID;
|
||||
companySlug: string;
|
||||
invoice_id: string;
|
||||
proforma_id: string;
|
||||
format: RendererFormat;
|
||||
};
|
||||
|
||||
@ -23,25 +23,29 @@ export class ReportProformaUseCase {
|
||||
) {}
|
||||
|
||||
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) {
|
||||
return Result.fail(idOrError.error);
|
||||
}
|
||||
|
||||
const invoiceId = idOrError.data;
|
||||
const proformaId = idOrError.data;
|
||||
|
||||
return this.transactionManager.complete(async (transaction) => {
|
||||
try {
|
||||
const invoiceResult = await this.finder.findProformaById(companyId, invoiceId, transaction);
|
||||
const proformaResult = await this.finder.findProformaById(
|
||||
companyId,
|
||||
proformaId,
|
||||
transaction
|
||||
);
|
||||
|
||||
if (invoiceResult.isFailure) {
|
||||
return Result.fail(invoiceResult.error);
|
||||
if (proformaResult.isFailure) {
|
||||
return Result.fail(proformaResult.error);
|
||||
}
|
||||
|
||||
const invoice = invoiceResult.data;
|
||||
const invoice = proformaResult.data;
|
||||
|
||||
// Snapshot completo de la entidad
|
||||
const fullSnapshot = this.fullSnapshotBuilder.toOutput(invoice);
|
||||
@ -67,7 +71,9 @@ export class ReportProformaUseCase {
|
||||
filename: documentResult.data.filename,
|
||||
});
|
||||
} 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 NextFunction, type Request, type Response, Router } from "express";
|
||||
|
||||
import { GetProformaByIdRequestSchema, ListProformasRequestSchema } from "../../../../common";
|
||||
import {
|
||||
GetProformaByIdRequestSchema,
|
||||
ListProformasRequestSchema,
|
||||
ReportProformaByIdParamsRequestSchema,
|
||||
ReportProformaByIdQueryRequestSchema,
|
||||
} from "../../../../common";
|
||||
import {
|
||||
GetProformaController,
|
||||
ListProformasController,
|
||||
type ProformasInternalDeps,
|
||||
ReportProformaController,
|
||||
} from "../../proformas";
|
||||
|
||||
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(
|
||||
"/",
|
||||
//checkTabContext,
|
||||
@ -89,19 +108,9 @@ export const proformasRouter = (params: ModuleParams, deps: ProformasInternalDep
|
||||
const controller = new DeleteProformaController(useCase);
|
||||
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(
|
||||
"/:proforma_id/status",
|
||||
|
||||
@ -5,8 +5,8 @@ import {
|
||||
requireAuthenticatedGuard,
|
||||
requireCompanyContextGuard,
|
||||
} 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 { customerInvoicesApiErrorMapper } from "../../../express/proformas/proformas-api-error-mapper.ts";
|
||||
|
||||
|
||||
@ -45,7 +45,7 @@ export class ProformaDocumentRenderer implements IDocumentRenderer<ProformaRepor
|
||||
module: "customer-invoices",
|
||||
companySlug,
|
||||
languageCode,
|
||||
templateFilename: "issued-invoice.frx",
|
||||
templateFilename: "proforma.frx",
|
||||
});
|
||||
|
||||
const output = await this.fastReportRenderer.render({
|
||||
|
||||
@ -1,10 +1,12 @@
|
||||
import {
|
||||
ExpressController,
|
||||
type RendererFormat,
|
||||
forbidQueryFieldGuard,
|
||||
requireAuthenticatedGuard,
|
||||
requireCompanyContextGuard,
|
||||
} from "@erp/core/api";
|
||||
|
||||
import type { ReportProformaByIdQueryRequestDTO } from "../../../../../common";
|
||||
import type { ReportProformaUseCase } from "../../../../application/index.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 { 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(
|
||||
({ data, filename }) =>
|
||||
filename ? this.downloadPDF(data, filename) : this.downloadHTML(data as string),
|
||||
({ payload, filename }) => {
|
||||
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)
|
||||
);
|
||||
}
|
||||
|
||||
@ -8,7 +8,12 @@ export type ReportProformaByIdParamsRequestDTO = z.infer<
|
||||
typeof ReportProformaByIdParamsRequestSchema
|
||||
>;
|
||||
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<
|
||||
|
||||
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