pelagia-portal/App/lib/po-number.ts
Hardik 6a3b371acc fix(po-number): FY format changed to 2024-25 style
PO numbers now end with e.g. PMS/HNR1/200/2025-26

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-31 01:57:48 +05:30

72 lines
2.3 KiB
TypeScript
Raw 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 } });
let maxId = 199;
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] };
}