From 2254a13ad0a14c4a46ea68cf5cbd41b17b41df9a Mon Sep 17 00:00:00 2001 From: BOHA Date: Thu, 28 May 2026 13:35:36 +0200 Subject: [PATCH] v1.7.0: fix offer created_at override, invoice PDF translations, remove has_order filter MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add created_at to offer create/update schemas so user-selected date is stored instead of always defaulting to now() - Translate payment method in invoice PDF based on language (Příkazem → Bank transfer, etc.) - Add order_date and cnb_rate translation keys, replace hardcoded inline strings - Remove duplicate has_order filter from offers backend route Co-Authored-By: Claude Opus 4.7 --- package.json | 2 +- src/admin/lib/queries/offers.ts | 2 -- src/routes/admin/invoices-pdf.ts | 30 ++++++++++++++++++++++++++---- src/routes/admin/quotations.ts | 1 - src/schemas/offers.schema.ts | 2 ++ src/services/offers.service.ts | 25 +++++++++++-------------- 6 files changed, 40 insertions(+), 22 deletions(-) diff --git a/package.json b/package.json index e37eb00..2c0bb6b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "app-ts", - "version": "1.6.9", + "version": "1.7.0", "description": "", "main": "dist/server.js", "scripts": { diff --git a/src/admin/lib/queries/offers.ts b/src/admin/lib/queries/offers.ts index f95fed6..9fd7429 100644 --- a/src/admin/lib/queries/offers.ts +++ b/src/admin/lib/queries/offers.ts @@ -73,7 +73,6 @@ export const offerListOptions = (filters: { perPage?: number; status?: string; customer_id?: number; - has_order?: string; }) => queryOptions({ queryKey: ["offers", "list", filters], @@ -87,7 +86,6 @@ export const offerListOptions = (filters: { if (filters.status) params.set("status", filters.status); if (filters.customer_id) params.set("customer_id", String(filters.customer_id)); - if (filters.has_order) params.set("has_order", filters.has_order); const qs = params.toString(); return paginatedJsonQuery(`/api/admin/offers${qs ? `?${qs}` : ""}`); }, diff --git a/src/routes/admin/invoices-pdf.ts b/src/routes/admin/invoices-pdf.ts index 11a5fda..2bfc404 100644 --- a/src/routes/admin/invoices-pdf.ts +++ b/src/routes/admin/invoices-pdf.ts @@ -168,6 +168,24 @@ function buildAddressLines( /* ── Translations ────────────────────────────────────────────────── */ +const paymentMethodMap: Record> = { + cs: { + "Bank transfer": "Příkazem", + Cash: "Hotově", + "Cash on delivery": "Dobírka", + }, + en: { + Příkazem: "Bank transfer", + Hotově: "Cash", + Dobírka: "Cash on delivery", + }, +}; + +function translatePaymentMethod(value: string | null, lang: string): string { + if (!value) return ""; + return paymentMethodMap[lang]?.[value] || value; +} + const translations: Record> = { cs: { title: "Faktura", @@ -181,6 +199,7 @@ const translations: Record> = { var_symbol: "Variabilní s.:", const_symbol: "Konstantní s.:", order_no: "Objednávka č.:", + order_date: "Objednávka ze dne:", issue_date: "Datum vystavení:", due_date: "Datum splatnosti:", tax_date: "Datum uskutečnění plnění:", @@ -213,6 +232,7 @@ const translations: Record> = { stamp: "Razítko:", ico: "IČ: ", dic: "DIČ: ", + cnb_rate: "Přepočet kurzem ČNB ke dni", }, en: { title: "Invoice", @@ -226,6 +246,7 @@ const translations: Record> = { var_symbol: "Variable symbol:", const_symbol: "Constant symbol:", order_no: "Order No.:", + order_date: "Order date:", issue_date: "Issue date:", due_date: "Due date:", tax_date: "Tax point date:", @@ -257,6 +278,7 @@ const translations: Record> = { stamp: "Stamp:", ico: "Reg. No.: ", dic: "Tax ID: ", + cnb_rate: "CNB exchange rate as of", }, }; @@ -923,9 +945,9 @@ ${indentCSS}
${escapeHtml(t.issue_date)} ${escapeHtml(formatDate(invoice.issue_date))}
${escapeHtml(t.due_date)} ${escapeHtml(formatDate(invoice.due_date))}
${escapeHtml(t.tax_date)} ${escapeHtml(formatDate(invoice.tax_date))}
-
${escapeHtml(t.payment_method)} ${escapeHtml(invoice.payment_method)}
- ${orderNumber ? `
${lang === "cs" ? "Objednávka č.:" : "Order no.:"} ${orderNumber}
` : ""} - ${orderDate ? `
${lang === "cs" ? "Objednávka ze dne:" : "Order date:"} ${escapeHtml(orderDate)}
` : ""} +
${escapeHtml(t.payment_method)} ${escapeHtml(translatePaymentMethod(invoice.payment_method, lang))}
+ ${orderNumber ? `
${escapeHtml(t.order_no)} ${orderNumber}
` : ""} + ${orderDate ? `
${escapeHtml(t.order_date)} ${escapeHtml(orderDate)}
` : ""} @@ -1008,7 +1030,7 @@ ${indentCSS} ? ` - Přepočet kurzem ČNB ke dni ${formatDate(invoice.issue_date)}: 1 ${escapeHtml(currency)} = ${cnbRate.toFixed(3).replace(".", ",")} CZK + ${escapeHtml(t.cnb_rate)} ${formatDate(invoice.issue_date)}: 1 ${escapeHtml(currency)} = ${cnbRate.toFixed(3).replace(".", ",")} CZK ` diff --git a/src/routes/admin/quotations.ts b/src/routes/admin/quotations.ts index 9cfa6f0..33e73a1 100644 --- a/src/routes/admin/quotations.ts +++ b/src/routes/admin/quotations.ts @@ -42,7 +42,6 @@ export default async function quotationsRoutes( search, status: query.status ? String(query.status) : undefined, customer_id: query.customer_id ? Number(query.customer_id) : undefined, - has_order: query.has_order ? String(query.has_order) : undefined, }); return paginated( diff --git a/src/schemas/offers.schema.ts b/src/schemas/offers.schema.ts index e6be021..bd9f724 100644 --- a/src/schemas/offers.schema.ts +++ b/src/schemas/offers.schema.ts @@ -46,6 +46,7 @@ export const CreateQuotationSchema = z.object({ .transform((v) => Number(v)) .refine((v) => !Number.isNaN(v), { message: "Musí být platné číslo" }) .nullish(), + created_at: z.string().nullish(), valid_until: z.string().nullish(), currency: z.string().optional().default("CZK"), language: z.string().optional().default("cs"), @@ -76,6 +77,7 @@ export const UpdateQuotationSchema = z.object({ .transform((v) => Number(v)) .refine((v) => !Number.isNaN(v), { message: "Must be a valid number" }) .optional(), + created_at: z.union([z.string(), z.null()]).optional(), valid_until: z.union([z.string(), z.null()]).optional(), currency: z.string().optional(), language: z.string().optional(), diff --git a/src/services/offers.service.ts b/src/services/offers.service.ts index 9e5b1a0..9d5aa8f 100644 --- a/src/services/offers.service.ts +++ b/src/services/offers.service.ts @@ -44,7 +44,6 @@ interface ListOffersParams { search: string; status?: string; customer_id?: number; - has_order?: string; } function enrichQuotation(q: any) { @@ -73,24 +72,13 @@ function enrichQuotation(q: any) { } export async function listOffers(params: ListOffersParams) { - const { - page, - limit, - skip, - sort, - order, - search, - status, - customer_id, - has_order, - } = params; + const { page, limit, skip, sort, order, search, status, customer_id } = + params; const sortField = ALLOWED_SORT_FIELDS.includes(sort) ? sort : "id"; const where: Record = {}; if (status) where.status = status; if (customer_id) where.customer_id = customer_id; - if (has_order === "yes") where.order_id = { not: null }; - if (has_order === "no") where.order_id = null; if (search) { where.OR = [ { quotation_number: { contains: search } }, @@ -189,6 +177,9 @@ export async function createOffer(body: Record) { quotation_number: quotationNumber, project_code: body.project_code ? String(body.project_code) : null, customer_id: body.customer_id ? Number(body.customer_id) : null, + created_at: body.created_at + ? new Date(String(body.created_at)) + : undefined, valid_until: body.valid_until ? new Date(String(body.valid_until)) : null, @@ -251,6 +242,12 @@ export async function updateOffer(id: number, body: Record) { const data = { customer_id: body.customer_id !== undefined ? Number(body.customer_id) : undefined, + created_at: + body.created_at !== undefined + ? body.created_at + ? new Date(String(body.created_at)) + : null + : undefined, valid_until: body.valid_until !== undefined ? body.valid_until