pelagia-portal/App/lib/po-number.ts
Hardik ccc93d40f3 fix(po-number): floor at 9000, imported POs keep original PO number
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-31 02:33:42 +05:30

74 lines
2.5 KiB
TypeScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* Structured PO number generator.
* Format: COMPANY_CODE/VESSEL_CODE/PO_ID/FY
* - COMPANY_CODE: company.code (fallback "PMS")
* - VESSEL_CODE: vessel.code (fallback "GEN")
* - PO_ID: globally sequential integer, starting from 200
* - FY: Indian financial year "XXYY" e.g. "2526" for Apr 2025Mar 2026
*
* Example: PMS/HNR1/200/2526
*/
import { db } from "@/lib/db";
/** Indian financial year string. AprilMarch cycle. */
function currentFY(): string {
const now = new Date();
const month = now.getMonth() + 1; // 1-indexed
const year = now.getFullYear();
const fyStart = month >= 4 ? year : year - 1;
const fyEnd = fyStart + 1;
return `${fyStart}-${String(fyEnd).slice(-2)}`;
}
/** Find the next sequential PO ID (min 200) by scanning existing structured PO numbers. */
async function nextPoId(): Promise<number> {
const pos = await db.purchaseOrder.findMany({ select: { poNumber: true } });
// Floor at 8999 so the first generated ID is 9000, avoiding clashes with
// imported POs that retain their original IDs (which typically start from 1).
let maxId = 8999;
for (const { poNumber } of pos) {
const parts = poNumber.split("/");
if (parts.length === 4) {
const n = parseInt(parts[2], 10);
if (!isNaN(n) && n > maxId) maxId = n;
}
}
return maxId + 1;
}
/**
* Generate a structured PO number.
* Pass vesselId and companyId so we can resolve their codes from the DB.
* Either may be null — sensible defaults are used.
*/
export async function generatePoNumber(
vesselId?: string | null,
companyId?: string | null,
): Promise<string> {
const [vessel, company, id] = await Promise.all([
vesselId ? db.vessel .findUnique({ where: { id: vesselId }, select: { code: true } }) : null,
companyId ? db.company.findUnique({ where: { id: companyId }, select: { code: true } }) : null,
nextPoId(),
]);
const companyCode = company?.code ?? "PMS";
const vesselCode = vessel?.code ?? "GEN";
const fy = currentFY();
return `${companyCode}/${vesselCode}/${id}/${fy}`;
}
/** Parse a structured PO number into its parts. Returns null for old-format numbers. */
export function parsePoNumber(poNumber: string): {
companyCode: string;
vesselCode: string;
poId: number;
fy: string;
} | null {
const parts = poNumber.split("/");
if (parts.length !== 4) return null;
const poId = parseInt(parts[2], 10);
if (isNaN(poId)) return null;
return { companyCode: parts[0], vesselCode: parts[1], poId, fy: parts[3] };
}