import prisma from "../config/database"; /** * Shared number generator for orders and projects. * Format: YYtypeCode + 4-digit sequence (e.g., 26710003) * Queries MAX from both orders and projects tables. */ export async function generateSharedNumber(): Promise { const settings = await prisma.company_settings.findFirst({ select: { order_type_code: true }, }); const typeCode = settings?.order_type_code || "71"; const yy = String(new Date().getFullYear()).slice(-2); const prefix = `${yy}${typeCode}`; const prefixLen = prefix.length; const likePattern = `${prefix}%`; const result = await prisma.$queryRaw<[{ max_seq: bigint | null }]>` SELECT COALESCE(MAX(seq), 0) as max_seq FROM ( SELECT CAST(SUBSTRING(order_number, ${prefixLen} + 1) AS UNSIGNED) AS seq FROM orders WHERE order_number LIKE ${likePattern} UNION ALL SELECT CAST(SUBSTRING(project_number, ${prefixLen} + 1) AS UNSIGNED) AS seq FROM projects WHERE project_number LIKE ${likePattern} ) combined `; const nextNum = Number(result[0]?.max_seq ?? 0) + 1; return `${prefix}${String(nextNum).padStart(4, "0")}`; } /** * Next offer number. Queries MAX from quotations table. * Format: YEAR/PREFIX/NNN (e.g., 2026/NA/008) */ export async function generateOfferNumber(): Promise { const settings = await prisma.company_settings.findFirst({ select: { quotation_prefix: true }, }); const prefix = settings?.quotation_prefix || "NA"; const year = new Date().getFullYear(); const likePattern = `${year}/${prefix}/%`; const result = await prisma.$queryRaw<[{ max_num: bigint | null }]>` SELECT COALESCE(MAX(CAST(SUBSTRING_INDEX(quotation_number, '/', -1) AS UNSIGNED)), 0) as max_num FROM quotations WHERE quotation_number LIKE ${likePattern} `; const nextNum = Number(result[0]?.max_num ?? 0) + 1; return `${year}/${prefix}/${String(nextNum).padStart(3, "0")}`; } /** * Next invoice number via atomic sequence table. */ export async function generateInvoiceNumber(year: number): Promise { return prisma.$transaction(async (tx) => { const existing = await tx.number_sequences.findFirst({ where: { type: "invoice", year }, }); if (existing) { const nextNum = (existing.last_number ?? 0) + 1; await tx.number_sequences.update({ where: { id: existing.id }, data: { last_number: nextNum }, }); return nextNum; } await tx.number_sequences.create({ data: { type: "invoice", year, last_number: 1 }, }); return 1; }); }