diff --git a/modules/core/src/api/application/documents/services/document-generation-service.ts b/modules/core/src/api/application/documents/services/document-generation-service.ts index 551fdcb4..edf0ff9e 100644 --- a/modules/core/src/api/application/documents/services/document-generation-service.ts +++ b/modules/core/src/api/application/documents/services/document-generation-service.ts @@ -93,7 +93,9 @@ export class DocumentGenerationService { 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 { 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)); } } diff --git a/modules/core/src/api/infrastructure/documents/renderers/fastreport/fastreport-renderer.ts b/modules/core/src/api/infrastructure/documents/renderers/fastreport/fastreport-renderer.ts index 83a890a3..46482a95 100644 --- a/modules/core/src/api/infrastructure/documents/renderers/fastreport/fastreport-renderer.ts +++ b/modules/core/src/api/infrastructure/documents/renderers/fastreport/fastreport-renderer.ts @@ -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 { + 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); } diff --git a/modules/customer-invoices/src/api/application/proformas/use-cases/report-proforma.use-case.ts b/modules/customer-invoices/src/api/application/proformas/use-cases/report-proforma.use-case.ts index 285ea945..a4e5f820 100644 --- a/modules/customer-invoices/src/api/application/proformas/use-cases/report-proforma.use-case.ts +++ b/modules/customer-invoices/src/api/application/proformas/use-cases/report-proforma.use-case.ts @@ -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); } }); } diff --git a/modules/customer-invoices/src/api/infrastructure/express/proformas/proformas.routes.ts b/modules/customer-invoices/src/api/infrastructure/express/proformas/proformas.routes.ts index a2edb415..be9aae6f 100644 --- a/modules/customer-invoices/src/api/infrastructure/express/proformas/proformas.routes.ts +++ b/modules/customer-invoices/src/api/infrastructure/express/proformas/proformas.routes.ts @@ -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", diff --git a/modules/customer-invoices/src/api/infrastructure/issued-invoices/express/controllers/report-issued-invoice.controller.ts b/modules/customer-invoices/src/api/infrastructure/issued-invoices/express/controllers/report-issued-invoice.controller.ts index 6c91437c..e223c717 100644 --- a/modules/customer-invoices/src/api/infrastructure/issued-invoices/express/controllers/report-issued-invoice.controller.ts +++ b/modules/customer-invoices/src/api/infrastructure/issued-invoices/express/controllers/report-issued-invoice.controller.ts @@ -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"; diff --git a/modules/customer-invoices/src/api/infrastructure/proformas/documents/renderers/fastreport/proforma-document-renderer.ts b/modules/customer-invoices/src/api/infrastructure/proformas/documents/renderers/fastreport/proforma-document-renderer.ts index fd715298..900f0a66 100644 --- a/modules/customer-invoices/src/api/infrastructure/proformas/documents/renderers/fastreport/proforma-document-renderer.ts +++ b/modules/customer-invoices/src/api/infrastructure/proformas/documents/renderers/fastreport/proforma-document-renderer.ts @@ -45,7 +45,7 @@ export class ProformaDocumentRenderer implements IDocumentRenderer - filename ? this.downloadPDF(data, filename) : this.downloadHTML(data as string), + ({ payload, filename }) => { + if (format === "PDF") { + return this.downloadPDF(payload as Buffer, String(filename)); + } + if (format === "HTML") { + return this.downloadHTML(payload as unknown as string); + } + // JSON + return this.json(payload); + }, (err) => this.handleError(err) ); } diff --git a/modules/customer-invoices/src/common/dto/request/proformas/report-proforma-by-id.request.dto.ts b/modules/customer-invoices/src/common/dto/request/proformas/report-proforma-by-id.request.dto.ts index 9ea45e5a..a6711949 100644 --- a/modules/customer-invoices/src/common/dto/request/proformas/report-proforma-by-id.request.dto.ts +++ b/modules/customer-invoices/src/common/dto/request/proformas/report-proforma-by-id.request.dto.ts @@ -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< diff --git a/modules/customer-invoices/templates/rodax/es/proforma.frx b/modules/customer-invoices/templates/rodax/es/proforma.frx new file mode 100755 index 00000000..d4653e10 --- /dev/null +++ b/modules/customer-invoices/templates/rodax/es/proforma.frx @@ -0,0 +1,227 @@ + + + using System; +using System.Collections; +using System.Collections.Generic; +using System.ComponentModel; +using System.Windows.Forms; +using System.Drawing; +using System.Data; +using FastReport; +using FastReport.Data; +using FastReport.Dialog; +using FastReport.Barcode; +using FastReport.Table; +using FastReport.Utils; + +namespace FastReport +{ + public class ReportScript + { + + } +} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +