From d33c2b34161dc46cd51b889d597d7a4a32007277 Mon Sep 17 00:00:00 2001 From: BOHA Date: Mon, 23 Mar 2026 19:25:16 +0100 Subject: [PATCH] =?UTF-8?q?fix:=20invoice=20numbering=20=E2=80=94=20use=20?= =?UTF-8?q?MAX=20from=20invoices=20table=20instead=20of=20sequence=20count?= =?UTF-8?q?er?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/services/invoices.service.ts | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/src/services/invoices.service.ts b/src/services/invoices.service.ts index 247ac83..278c923 100644 --- a/src/services/invoices.service.ts +++ b/src/services/invoices.service.ts @@ -1,8 +1,6 @@ import prisma from '../config/database'; -import { generateInvoiceNumber } from './numbering.service'; // Re-export for convenience -export { generateInvoiceNumber as getNextInvoiceNumber } from './numbering.service'; // Status transition rules matching PHP const VALID_TRANSITIONS: Record = { @@ -100,11 +98,18 @@ export async function listInvoices(params: ListInvoicesParams) { export async function getNextInvoiceNumberFormatted() { const settings = await prisma.company_settings.findFirst({ select: { invoice_type_code: true } }); const typeCode = settings?.invoice_type_code || '81'; - const year = new Date().getFullYear(); - const yy = String(year).slice(-2); + const yy = String(new Date().getFullYear()).slice(-2); const prefix = `${yy}${typeCode}`; + const prefixLen = prefix.length; + const likePattern = `${prefix}%`; - const nextNum = await generateInvoiceNumber(year); + // MAX from existing invoices — same approach as offers/orders + const result = await prisma.$queryRaw<[{ max_num: bigint | null }]>` + SELECT COALESCE(MAX(CAST(SUBSTRING(invoice_number, ${prefixLen} + 1) AS UNSIGNED)), 0) as max_num + FROM invoices + WHERE invoice_number LIKE ${likePattern} + `; + const nextNum = Number(result[0]?.max_num ?? 0) + 1; const number = `${prefix}${String(nextNum).padStart(4, '0')}`; return { number, next_number: number }; }