From d6387f6e3e595e9e341d02eae0f7f1d72dce7ebc Mon Sep 17 00:00:00 2001 From: David Arranz Date: Thu, 8 Oct 2026 18:23:29 +0200 Subject: [PATCH] =?UTF-8?q?feat:=20implementar=20soporte=20para=20carga=20?= =?UTF-8?q?de=20etiquetas=20de=20informe=20multiling=C3=BCe=20y=20eliminar?= =?UTF-8?q?=20etiquetas=20obsoletas?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Dockerfile.server | 4 +- .../reportQuote/reporter/ReportQuote.i18n.ts | 53 ++++++++++++ .../reporter/ReportQuote.labels.ts | 82 ------------------- .../reporter/ReportQuote.reporter.test.ts | 6 +- .../reporter/ReportQuote.reporter.ts | 4 +- .../reporter/templates/quote/locales/en.json | 3 + .../reporter/templates/quote/locales/es.json | 3 + .../reporter/templates/quote/locales/fr.json | 3 + 8 files changed, 70 insertions(+), 88 deletions(-) create mode 100644 server/src/contexts/sales/infrastructure/express/controllers/quotes/reportQuote/reporter/ReportQuote.i18n.ts delete mode 100644 server/src/contexts/sales/infrastructure/express/controllers/quotes/reportQuote/reporter/ReportQuote.labels.ts create mode 100644 server/src/contexts/sales/infrastructure/express/controllers/quotes/reportQuote/reporter/templates/quote/locales/en.json create mode 100644 server/src/contexts/sales/infrastructure/express/controllers/quotes/reportQuote/reporter/templates/quote/locales/es.json create mode 100644 server/src/contexts/sales/infrastructure/express/controllers/quotes/reportQuote/reporter/templates/quote/locales/fr.json diff --git a/Dockerfile.server b/Dockerfile.server index 8c0cc84..20c7a1d 100644 --- a/Dockerfile.server +++ b/Dockerfile.server @@ -25,8 +25,7 @@ RUN cd ./server && yarn install RUN cd ./server && yarn run build || { echo 'Error during build'; exit 1; } # Copiar plantillas -RUN mkdir -p /api/dist/server/src/contexts/sales/infrastructure/express/controllers/quotes/reportQuote/reporter/templates/quote/ -COPY server/src/contexts/sales/infrastructure/express/controllers/quotes/reportQuote/reporter/templates/quote/* /api/dist/server/src/contexts/sales/infrastructure/express/controllers/quotes/reportQuote/reporter/templates/quote +COPY server/src/contexts/sales/infrastructure/express/controllers/quotes/reportQuote/reporter/templates/quote/ /api/dist/server/src/contexts/sales/infrastructure/express/controllers/quotes/reportQuote/reporter/templates/quote/ # Copiar logo por defecto RUN mkdir -p /api/dist/uploads/images/ @@ -75,4 +74,3 @@ EXPOSE 3001 # Comando para correr la aplicación CMD node -r ts-node/register/transpile-only -r tsconfig-paths/register ./server/src/index.js - diff --git a/server/src/contexts/sales/infrastructure/express/controllers/quotes/reportQuote/reporter/ReportQuote.i18n.ts b/server/src/contexts/sales/infrastructure/express/controllers/quotes/reportQuote/reporter/ReportQuote.i18n.ts new file mode 100644 index 0000000..243c29d --- /dev/null +++ b/server/src/contexts/sales/infrastructure/express/controllers/quotes/reportQuote/reporter/ReportQuote.i18n.ts @@ -0,0 +1,53 @@ +import { readFileSync } from "fs"; +import path from "path"; + +export const REPORT_LANGUAGES = ["es", "en", "fr"] as const; +export type ReportLanguage = (typeof REPORT_LANGUAGES)[number]; + +export interface ReportLabels { + official_distributor: string; + dealer_logo: string; + document_title: string; + browser_title: string; + quote_number: string; + date: string; + validity: string; + salesperson: string; + customer_reference: string; + page: string; + of: string; + quantity: string; + description: string; + unit_price: string; + subtotal: string; + discount: string; + total_amount: string; + payment_method: string; + notes: string; + net_amount: string; + discounts: string; + percentage_discount: string; + taxable_amount: string; +} + +export const resolveReportLanguage = (language: string): ReportLanguage => + REPORT_LANGUAGES.includes(language as ReportLanguage) ? (language as ReportLanguage) : "es"; + +const requiredKeys = [ + "official_distributor", "dealer_logo", "document_title", "browser_title", "quote_number", + "date", "validity", "salesperson", "customer_reference", "page", "of", "quantity", + "description", "unit_price", "subtotal", "discount", "total_amount", "payment_method", + "notes", "net_amount", "discounts", "percentage_discount", "taxable_amount", +] satisfies (keyof ReportLabels)[]; + +export const loadReportLabels = (language: ReportLanguage): ReportLabels => { + const filepath = path.join(__dirname, "./templates/quote/locales", `${language}.json`); + const labels = JSON.parse(readFileSync(filepath, "utf8")) as Partial; + const missingKeys = requiredKeys.filter((key) => !(key in labels)); + + if (missingKeys.length) { + throw new Error(`Missing report labels for ${language}: ${missingKeys.join(", ")}`); + } + + return labels as ReportLabels; +}; diff --git a/server/src/contexts/sales/infrastructure/express/controllers/quotes/reportQuote/reporter/ReportQuote.labels.ts b/server/src/contexts/sales/infrastructure/express/controllers/quotes/reportQuote/reporter/ReportQuote.labels.ts deleted file mode 100644 index deda910..0000000 --- a/server/src/contexts/sales/infrastructure/express/controllers/quotes/reportQuote/reporter/ReportQuote.labels.ts +++ /dev/null @@ -1,82 +0,0 @@ -export const reportLabels = { - es: { - official_distributor: "DISTRIBUIDOR OFICIAL", - dealer_logo: "Logo distribuidor", - document_title: "PRESUPUESTO", - browser_title: "Presupuesto", - quote_number: "Presupuesto nº:", - date: "Fecha:", - validity: "Validez:", - salesperson: "Vendedor:", - customer_reference: "Referencia cliente:", - page: "Página", - of: "de", - quantity: "Cant.", - description: "Descripción", - unit_price: "Prec. Unitario", - subtotal: "Subtotal", - discount: "Dto (%)", - total_amount: "Importe total", - payment_method: "Forma de pago:", - notes: "Notas:", - net_amount: "Importe neto", - discounts: "Descuentos", - percentage_discount: "% Descuento", - taxable_amount: "Base imponible", - }, - en: { - official_distributor: "OFFICIAL DISTRIBUTOR", - dealer_logo: "Dealer logo", - document_title: "QUOTE", - browser_title: "Quote", - quote_number: "Quote no.:", - date: "Date:", - validity: "Validity:", - salesperson: "Salesperson:", - customer_reference: "Customer reference:", - page: "Page", - of: "of", - quantity: "Qty.", - description: "Description", - unit_price: "Unit price", - subtotal: "Subtotal", - discount: "Discount (%)", - total_amount: "Total amount", - payment_method: "Payment method:", - notes: "Notes:", - net_amount: "Net amount", - discounts: "Discounts", - percentage_discount: "% Discount", - taxable_amount: "Taxable amount", - }, - fr: { - official_distributor: "DISTRIBUTEUR OFFICIEL", - dealer_logo: "Logo du distributeur", - document_title: "DEVIS", - browser_title: "Devis", - quote_number: "Devis n° :", - date: "Date :", - validity: "Validité :", - salesperson: "Commercial :", - customer_reference: "Référence client :", - page: "Page", - of: "sur", - quantity: "Qté", - description: "Description", - unit_price: "Prix unitaire", - subtotal: "Sous-total", - discount: "Remise (%)", - total_amount: "Montant total", - payment_method: "Mode de paiement :", - notes: "Notes :", - net_amount: "Montant net", - discounts: "Remises", - percentage_discount: "% de remise", - taxable_amount: "Base imposable", - }, -} as const; - -export type ReportLanguage = keyof typeof reportLabels; - -export const resolveReportLanguage = (language: string): ReportLanguage => - language === "en" || language === "fr" ? language : "es"; diff --git a/server/src/contexts/sales/infrastructure/express/controllers/quotes/reportQuote/reporter/ReportQuote.reporter.test.ts b/server/src/contexts/sales/infrastructure/express/controllers/quotes/reportQuote/reporter/ReportQuote.reporter.test.ts index d2f4089..a57bf26 100644 --- a/server/src/contexts/sales/infrastructure/express/controllers/quotes/reportQuote/reporter/ReportQuote.reporter.test.ts +++ b/server/src/contexts/sales/infrastructure/express/controllers/quotes/reportQuote/reporter/ReportQuote.reporter.test.ts @@ -1,6 +1,6 @@ import { ISalesContext } from "@/contexts/sales/infrastructure/Sales.context"; import { createQuoteMapper } from "@/contexts/sales/infrastructure/mappers/quote.mapper"; -import { resolveReportLanguage } from "./ReportQuote.labels"; +import { loadReportLabels, resolveReportLanguage } from "./ReportQuote.i18n"; import { ReportQuotePresenter } from "./ReportQuote.reporter"; const source = ( @@ -82,4 +82,8 @@ describe("ReportQuotePresenter", () => { it("falls back to Spanish labels and language for unsupported quote languages", async () => { expect(resolveReportLanguage("zz")).toBe("es"); }); + + it("loads the French labels from the template locale", () => { + expect(loadReportLabels("fr").document_title).toBe("DEVIS"); + }); }); diff --git a/server/src/contexts/sales/infrastructure/express/controllers/quotes/reportQuote/reporter/ReportQuote.reporter.ts b/server/src/contexts/sales/infrastructure/express/controllers/quotes/reportQuote/reporter/ReportQuote.reporter.ts index f544daa..2bf3fa7 100644 --- a/server/src/contexts/sales/infrastructure/express/controllers/quotes/reportQuote/reporter/ReportQuote.reporter.ts +++ b/server/src/contexts/sales/infrastructure/express/controllers/quotes/reportQuote/reporter/ReportQuote.reporter.ts @@ -7,7 +7,7 @@ import * as handlebars from "handlebars"; import path from "path"; import puppeteer from "puppeteer"; import report from "puppeteer-report"; -import { reportLabels, resolveReportLanguage } from "./ReportQuote.labels"; +import { loadReportLabels, resolveReportLanguage } from "./ReportQuote.i18n"; import { Language } from "@shared/contexts"; import { resolveLocalizedQuoteDescriptions } from "@/contexts/sales/application/services"; @@ -82,7 +82,7 @@ const map = async (quote: Quote, context: ISalesContext) => { customer_reference: quote.customerReference.toString(), customer_information: quote.customer.toString(), lang_code: langCode, - labels: reportLabels[langCode], + labels: loadReportLabels(langCode), currency_code: quote.currency.toString(), payment_method: quote.paymentMethod.toString(), diff --git a/server/src/contexts/sales/infrastructure/express/controllers/quotes/reportQuote/reporter/templates/quote/locales/en.json b/server/src/contexts/sales/infrastructure/express/controllers/quotes/reportQuote/reporter/templates/quote/locales/en.json new file mode 100644 index 0000000..962e0f6 --- /dev/null +++ b/server/src/contexts/sales/infrastructure/express/controllers/quotes/reportQuote/reporter/templates/quote/locales/en.json @@ -0,0 +1,3 @@ +{ + "official_distributor": "OFFICIAL DISTRIBUTOR", "dealer_logo": "Dealer logo", "document_title": "QUOTE", "browser_title": "Quote", "quote_number": "Quote no.:", "date": "Date:", "validity": "Validity:", "salesperson": "Salesperson:", "customer_reference": "Customer reference:", "page": "Page", "of": "of", "quantity": "Qty.", "description": "Description", "unit_price": "Unit price", "subtotal": "Subtotal", "discount": "Discount (%)", "total_amount": "Total amount", "payment_method": "Payment method:", "notes": "Notes:", "net_amount": "Net amount", "discounts": "Discounts", "percentage_discount": "% Discount", "taxable_amount": "Taxable amount" +} diff --git a/server/src/contexts/sales/infrastructure/express/controllers/quotes/reportQuote/reporter/templates/quote/locales/es.json b/server/src/contexts/sales/infrastructure/express/controllers/quotes/reportQuote/reporter/templates/quote/locales/es.json new file mode 100644 index 0000000..31a8c80 --- /dev/null +++ b/server/src/contexts/sales/infrastructure/express/controllers/quotes/reportQuote/reporter/templates/quote/locales/es.json @@ -0,0 +1,3 @@ +{ + "official_distributor": "DISTRIBUIDOR OFICIAL", "dealer_logo": "Logo distribuidor", "document_title": "PRESUPUESTO", "browser_title": "Presupuesto", "quote_number": "Presupuesto nº:", "date": "Fecha:", "validity": "Validez:", "salesperson": "Vendedor:", "customer_reference": "Referencia cliente:", "page": "Página", "of": "de", "quantity": "Cant.", "description": "Descripción", "unit_price": "Prec. Unitario", "subtotal": "Subtotal", "discount": "Dto (%)", "total_amount": "Importe total", "payment_method": "Forma de pago:", "notes": "Notas:", "net_amount": "Importe neto", "discounts": "Descuentos", "percentage_discount": "% Descuento", "taxable_amount": "Base imponible" +} diff --git a/server/src/contexts/sales/infrastructure/express/controllers/quotes/reportQuote/reporter/templates/quote/locales/fr.json b/server/src/contexts/sales/infrastructure/express/controllers/quotes/reportQuote/reporter/templates/quote/locales/fr.json new file mode 100644 index 0000000..6e1e183 --- /dev/null +++ b/server/src/contexts/sales/infrastructure/express/controllers/quotes/reportQuote/reporter/templates/quote/locales/fr.json @@ -0,0 +1,3 @@ +{ + "official_distributor": "DISTRIBUTEUR OFFICIEL", "dealer_logo": "Logo du distributeur", "document_title": "DEVIS", "browser_title": "Devis", "quote_number": "Devis n° :", "date": "Date :", "validity": "Validité :", "salesperson": "Commercial :", "customer_reference": "Référence client :", "page": "Page", "of": "sur", "quantity": "Qté", "description": "Description", "unit_price": "Prix unitaire", "subtotal": "Sous-total", "discount": "Remise (%)", "total_amount": "Montant total", "payment_method": "Mode de paiement :", "notes": "Notes :", "net_amount": "Montant net", "discounts": "Remises", "percentage_discount": "% de remise", "taxable_amount": "Base imposable" +}