();
+ for (const a of accounts) {
+ const subLabel = a.parent ? `${a.parent.code} — ${a.parent.name}` : "Uncategorised";
+ const topLabel = a.parent?.parent ? `${a.parent.parent.name} › ` : "";
+ const groupKey = `${topLabel}${subLabel}`;
+ if (!accountGroupMap.has(groupKey)) accountGroupMap.set(groupKey, []);
+ accountGroupMap.get(groupKey)!.push(a);
+ }
+ const accountGroups: AccountGroup[] = Array.from(accountGroupMap.entries()).map(([group, items]) => ({ group, items }));
+
const costCentres: CostCentreOption[] = [
...vessels.map((v) => ({ ref: `v:${v.id}`, label: `${v.code} — ${v.name}`, group: "Vessels" as const })),
...sites.map((s) => ({ ref: `s:${s.id}`, label: `${s.code} — ${s.name}`, group: "Sites" as const })),
@@ -35,7 +49,7 @@ export default async function ImportPoPage() {
You then select the cost centre, accounting code, and confirm before saving as a draft.
-
+
);
}
diff --git a/App/app/(portal)/po/new/new-po-form.tsx b/App/app/(portal)/po/new/new-po-form.tsx
index eae1c8f..b28bdeb 100644
--- a/App/app/(portal)/po/new/new-po-form.tsx
+++ b/App/app/(portal)/po/new/new-po-form.tsx
@@ -3,7 +3,7 @@
import { useState } from "react";
import { useRouter } from "next/navigation";
import { createPo } from "./actions";
-import type { Account, Vendor } from "@prisma/client";
+import type { Vendor } from "@prisma/client";
import { LineItemsEditor } from "@/components/po/po-line-items-editor";
import { FileUploader } from "@/components/po/file-uploader";
import { uploadAndLinkFiles } from "@/lib/upload-files";
@@ -11,6 +11,7 @@ import type { LineItemInput } from "@/lib/validations/po";
import { TC_DEFAULTS, TC_FIXED_LINE, TC_FIXED_LINE_2 } from "@/lib/validations/po";
export type CostCentreOption = { ref: string; label: string; group: "Vessels" | "Sites" };
+export type AccountGroup = { group: string; items: { id: string; code: string; name: string }[] };
const INPUT_CLS =
"w-full rounded-lg border border-neutral-300 px-3 py-2.5 text-sm focus:border-primary-500 focus:outline-none focus:ring-2 focus:ring-primary-500/20";
@@ -19,7 +20,7 @@ const EMPTY_LINE: LineItemInput = { name: "", description: "", quantity: 1, unit
interface Props {
costCentres: CostCentreOption[];
- accounts: Account[];
+ accounts: AccountGroup[];
vendors: Vendor[];
initialLineItems?: LineItemInput[];
initialVendorId?: string;
@@ -125,8 +126,12 @@ export function NewPoForm({ costCentres, accounts, vendors, initialLineItems, in
onChange={(e) => setDefaultAccountId(e.target.value)}
className={INPUT_CLS}
>
-
- {accounts.map((a) => )}
+
+ {accounts.map(({ group, items }) => (
+
+ ))}
@@ -191,7 +196,7 @@ export function NewPoForm({ costCentres, accounts, vendors, initialLineItems, in
items={lineItems}
onChange={setLineItems}
multiAccount={multiAccount}
- accounts={accounts.map((a) => ({ id: a.id, name: a.name, code: a.code }))}
+ accounts={accounts.flatMap((g) => g.items.map((a) => ({ id: a.id, name: a.name, code: a.code })))}
defaultAccountId={defaultAccountId || undefined}
/>
diff --git a/App/app/(portal)/po/new/page.tsx b/App/app/(portal)/po/new/page.tsx
index 214fe02..7f43ac9 100644
--- a/App/app/(portal)/po/new/page.tsx
+++ b/App/app/(portal)/po/new/page.tsx
@@ -49,13 +49,36 @@ export default async function NewPoPage({ searchParams }: Props) {
}
}
- const [vessels, sites, accounts, vendors] = await Promise.all([
+ const [vessels, sites, leafAccounts, vendors] = await Promise.all([
db.vessel.findMany({ where: { isActive: true }, orderBy: { name: "asc" }, select: { id: true, name: true, code: true } }),
db.site.findMany({ where: { isActive: true }, orderBy: { name: "asc" }, select: { id: true, name: true, code: true } }),
- db.account.findMany({ where: { isActive: true }, orderBy: { name: "asc" } }),
+ db.account.findMany({
+ where: { isActive: true, children: { none: {} } },
+ orderBy: { code: "asc" },
+ select: {
+ id: true, code: true, name: true,
+ parent: {
+ select: {
+ name: true, code: true,
+ parent: { select: { name: true, code: true } },
+ },
+ },
+ },
+ }),
db.vendor.findMany({ where: { isActive: true }, orderBy: { name: "asc" } }),
]);
+ // Build grouped account list for optgroups: "TOP CAT > Sub Cat" → items
+ const accountGroupMap = new Map();
+ for (const a of leafAccounts) {
+ const subLabel = a.parent ? `${a.parent.code} — ${a.parent.name}` : "Uncategorised";
+ const topLabel = a.parent?.parent ? `${a.parent.parent.name} › ` : "";
+ const groupKey = `${topLabel}${subLabel}`;
+ if (!accountGroupMap.has(groupKey)) accountGroupMap.set(groupKey, []);
+ accountGroupMap.get(groupKey)!.push(a);
+ }
+ const accounts = Array.from(accountGroupMap.entries()).map(([group, items]) => ({ group, items }));
+
const costCentres: CostCentreOption[] = [
...vessels.map((v) => ({ ref: `v:${v.id}` as const, label: `${v.code} — ${v.name}`, group: "Vessels" as const })),
...sites.map((s) => ({ ref: `s:${s.id}` as const, label: `${s.code} — ${s.name}`, group: "Sites" as const })),
diff --git a/App/prisma/accounting-codes-data.ts b/App/prisma/accounting-codes-data.ts
new file mode 100644
index 0000000..1d0e93f
--- /dev/null
+++ b/App/prisma/accounting-codes-data.ts
@@ -0,0 +1,387 @@
+// Accounting Codes (Rev. 01/251227) — hierarchical structure
+// Each entry: [code, name, parentCode | null]
+// parentCode null = top-level category
+// parentCode ending in 0000 = top-level → sub-category
+// parentCode ending in 00 = sub-category → leaf item
+
+export type ACEntry = { code: string; name: string; parentCode: string | null };
+
+export const ACCOUNTING_CODES: ACEntry[] = [
+ // ── CAPITAL EXPENSES ─────────────────────────────────────────────────────────
+ { code: "100000", name: "CAPITAL EXPENSES", parentCode: null },
+ { code: "100100", name: "DREDGER COST", parentCode: "100000" },
+ { code: "100101", name: "Dredger cost-Shipyard", parentCode: "100100" },
+ { code: "100102", name: "Custom duty", parentCode: "100100" },
+ { code: "100103", name: "Stamp duty", parentCode: "100100" },
+ { code: "100104", name: "CHA Charges", parentCode: "100100" },
+ { code: "100105", name: "Transportation by sea- Dredger", parentCode: "100100" },
+ { code: "100106", name: "Transportation by road- Dredger", parentCode: "100100" },
+ { code: "100107", name: "Dredger cost- Others", parentCode: "100100" },
+ { code: "100200", name: "PIPELINE COST", parentCode: "100000" },
+ { code: "100201", name: "Pipeline Cost", parentCode: "100200" },
+ { code: "100202", name: "Pipeline- Nuts & Bolts", parentCode: "100200" },
+ { code: "100203", name: "Pipeline- Packing", parentCode: "100200" },
+ { code: "100204", name: "Pipeline- Repairs", parentCode: "100200" },
+ { code: "100205", name: "Transportation by sea- Pipeline", parentCode: "100200" },
+ { code: "100206", name: "Transportation by road- Pipeline", parentCode: "100200" },
+ { code: "100207", name: "Pipeline cost- Others", parentCode: "100200" },
+ { code: "100300", name: "HOSES COST", parentCode: "100000" },
+ { code: "100301", name: "Hoses Cost", parentCode: "100300" },
+ { code: "100302", name: "Hoses- Nuts & Bolts", parentCode: "100300" },
+ { code: "100303", name: "Hoses- Packing", parentCode: "100300" },
+ { code: "100304", name: "Hoses- Repairs", parentCode: "100300" },
+ { code: "100305", name: "Transportation by sea- Hoses", parentCode: "100300" },
+ { code: "100306", name: "Transportation by road- Hoses", parentCode: "100300" },
+ { code: "100307", name: "Hoses cost- Others", parentCode: "100300" },
+ { code: "100400", name: "FLOATS COST", parentCode: "100000" },
+ { code: "100401", name: "Floats Cost", parentCode: "100400" },
+ { code: "100402", name: "Floats- Nuts & Bolts", parentCode: "100400" },
+ { code: "100403", name: "Floats- Packing", parentCode: "100400" },
+ { code: "100404", name: "Floats- Repairs", parentCode: "100400" },
+ { code: "100405", name: "Transportation by sea- Floats", parentCode: "100400" },
+ { code: "100406", name: "Transportation by road- Floats", parentCode: "100400" },
+ { code: "100407", name: "Floats cost- Others", parentCode: "100400" },
+ { code: "100500", name: "DOCKING EXPENSES (CAPITAL)", parentCode: "100000" },
+ { code: "100501", name: "Dock charges", parentCode: "100500" },
+ { code: "100502", name: "Towing charges", parentCode: "100500" },
+ { code: "100503", name: "Drydock- Agency charges", parentCode: "100500" },
+ { code: "100504", name: "Drydock- Port charges", parentCode: "100500" },
+ { code: "100505", name: "Drydock- Manpower", parentCode: "100500" },
+ { code: "100506", name: "Drydock- Paints", parentCode: "100500" },
+ { code: "100507", name: "Drydock- Steel", parentCode: "100500" },
+ { code: "100508", name: "Drydock- Pipelines", parentCode: "100500" },
+ { code: "100509", name: "Drydock- Welding consumables", parentCode: "100500" },
+ { code: "100510", name: "Drydock- Gases", parentCode: "100500" },
+ { code: "100511", name: "Drydock- Cranage", parentCode: "100500" },
+ { code: "100512", name: "Drydock- Workshop charges", parentCode: "100500" },
+ { code: "100513", name: "Drydock- Technician visit charges", parentCode: "100500" },
+ { code: "100514", name: "Drydock- Transportation", parentCode: "100500" },
+ { code: "100515", name: "Drydock- Supdt. Travel Expenses", parentCode: "100500" },
+ { code: "100516", name: "Drydock- Supdt. Boarding & Lodging", parentCode: "100500" },
+ { code: "100517", name: "Drydock- Stores", parentCode: "100500" },
+ { code: "100600", name: "MAJOR REPAIRS", parentCode: "100000" },
+ { code: "100601", name: "Spares- DP Engine", parentCode: "100600" },
+ { code: "100602", name: "Spares- Hyd. Engine", parentCode: "100600" },
+ { code: "100603", name: "Spares- Dredge Pump", parentCode: "100600" },
+ { code: "100604", name: "Spares- Deck generator", parentCode: "100600" },
+ { code: "100605", name: "Spares- Cutter", parentCode: "100600" },
+ { code: "100606", name: "Spares- Winches", parentCode: "100600" },
+ { code: "100607", name: "Spares- Hydraulics", parentCode: "100600" },
+ { code: "100608", name: "Spares- Gear box", parentCode: "100600" },
+ { code: "100609", name: "Spares- Transmission system", parentCode: "100600" },
+ { code: "100610", name: "Spares- Attached pumps & Coolers", parentCode: "100600" },
+ { code: "100611", name: "Spares- Safety Equipment", parentCode: "100600" },
+ { code: "100612", name: "Spares- Others", parentCode: "100600" },
+ { code: "100613", name: "Spares- Steel Wires", parentCode: "100600" },
+ { code: "100614", name: "Spares- Electricals", parentCode: "100600" },
+ { code: "100615", name: "Spares- Electronics", parentCode: "100600" },
+ { code: "100616", name: "Spares- Gauges & Instrumentation", parentCode: "100600" },
+ { code: "100617", name: "Spares- Controls and Automation", parentCode: "100600" },
+ { code: "100618", name: "Technician expenses", parentCode: "100600" },
+ { code: "100619", name: "Workshop charges", parentCode: "100600" },
+ { code: "100620", name: "Transportation", parentCode: "100600" },
+ { code: "100621", name: "Other cost", parentCode: "100600" },
+ { code: "100622", name: "Equipment rent", parentCode: "100600" },
+ { code: "100623", name: "Equipment diesel", parentCode: "100600" },
+ { code: "100700", name: "OTHER EQUIPMENT COST", parentCode: "100000" },
+ { code: "100701", name: "Dredger equipment", parentCode: "100700" },
+ { code: "100702", name: "Office equipment", parentCode: "100700" },
+ { code: "100703", name: "Site equipment", parentCode: "100700" },
+ { code: "100704", name: "Transportation- Other Equipment", parentCode: "100700" },
+ { code: "100800", name: "OTHER INVESTMENT EXPENSES", parentCode: "100000" },
+ { code: "100801", name: "Purchase cost", parentCode: "100800" },
+ { code: "100802", name: "Upgradation cost", parentCode: "100800" },
+
+ // ── BUSINESS DEVELOPMENT EXP ─────────────────────────────────────────────────
+ { code: "200000", name: "BUSINESS DEVELOPMENT EXP", parentCode: null },
+ { code: "200100", name: "TENDER FEE", parentCode: "200000" },
+ { code: "200101", name: "Tender fee payment", parentCode: "200100" },
+ { code: "200200", name: "EMD", parentCode: "200000" },
+ { code: "200201", name: "Earnest Money Deposit", parentCode: "200200" },
+ { code: "200300", name: "DOCUMENTATION FEE", parentCode: "200000" },
+ { code: "200301", name: "Documentation fee payment", parentCode: "200300" },
+ { code: "200400", name: "BUSINESS TRAVEL", parentCode: "200000" },
+ { code: "200401", name: "Air/train ticket", parentCode: "200400" },
+ { code: "200402", name: "Boarding & Lodging", parentCode: "200400" },
+ { code: "200403", name: "Local travel", parentCode: "200400" },
+ { code: "200404", name: "Food expense", parentCode: "200400" },
+ { code: "200500", name: "ENTERTAINMENT EXPENSES", parentCode: "200000" },
+ { code: "200501", name: "Client entertainment expenses BD", parentCode: "200500" },
+
+ // ── OFFICE ADMINISTRATION ─────────────────────────────────────────────────────
+ { code: "300000", name: "OFFICE ADMINISTRATION", parentCode: null },
+ { code: "300100", name: "OFFICE RENT", parentCode: "300000" },
+ { code: "300101", name: "Premises rent", parentCode: "300100" },
+ { code: "300200", name: "OFFICE PROPERTY TAX", parentCode: "300000" },
+ { code: "300201", name: "Property tax payment", parentCode: "300200" },
+ { code: "300300", name: "OFFICE BILLS", parentCode: "300000" },
+ { code: "300301", name: "Electricity bill- Office", parentCode: "300300" },
+ { code: "300302", name: "Water bill- Office", parentCode: "300300" },
+ { code: "300400", name: "OFFICE MAINTENANCE EXPENSES", parentCode: "300000" },
+ { code: "300401", name: "Society dues", parentCode: "300400" },
+ { code: "300402", name: "AMC charges", parentCode: "300400" },
+ { code: "300403", name: "Other repairs- Office", parentCode: "300400" },
+ { code: "300404", name: "Refreshment/Pantry expenses", parentCode: "300400" },
+ { code: "300405", name: "Housekeeping expenses", parentCode: "300400" },
+ { code: "300406", name: "Other Expenses- Office", parentCode: "300400" },
+ { code: "300500", name: "OFFICE STATIONERY", parentCode: "300000" },
+ { code: "300501", name: "Stationery- Office", parentCode: "300500" },
+ { code: "300600", name: "COMPUTERS & PERIPHERALS", parentCode: "300000" },
+ { code: "300601", name: "Software expenses", parentCode: "300600" },
+ { code: "300602", name: "Hardware expenses", parentCode: "300600" },
+ { code: "300700", name: "OFFICE COMMUNICATION", parentCode: "300000" },
+ { code: "300701", name: "Courier & Postage", parentCode: "300700" },
+ { code: "300702", name: "Telephone and Data", parentCode: "300700" },
+ { code: "300703", name: "Mobile bills", parentCode: "300700" },
+ { code: "300800", name: "OFFICE VEHICLE EXPENSES", parentCode: "300000" },
+ { code: "300801", name: "Vehicle hire", parentCode: "300800" },
+ { code: "300802", name: "Fuel", parentCode: "300800" },
+ { code: "300803", name: "Repairs", parentCode: "300800" },
+ { code: "300804", name: "Insurance premium", parentCode: "300800" },
+ { code: "300805", name: "Tax & Tolls", parentCode: "300800" },
+ { code: "300900", name: "OFFICE STAFF TRAVEL", parentCode: "300000" },
+ { code: "300901", name: "Travel Expense- Official", parentCode: "300900" },
+ { code: "300902", name: "Travel allowance", parentCode: "300900" },
+ { code: "301000", name: "OFFICE STAFF SALARY", parentCode: "300000" },
+ { code: "301001", name: "Office Staff Wages", parentCode: "301000" },
+ { code: "301100", name: "OFFICE STAFF PF", parentCode: "300000" },
+ { code: "301101", name: "Office Staff EPF- Company Cost", parentCode: "301100" },
+ { code: "301200", name: "OFFICE STAFF BONUS", parentCode: "300000" },
+ { code: "301201", name: "Annual Bonus", parentCode: "301200" },
+ { code: "301202", name: "Performance Bonus", parentCode: "301200" },
+ { code: "301300", name: "OFFICE STAFF INSURANCE PREM", parentCode: "300000" },
+ { code: "301301", name: "Mediclaim Policy Premium", parentCode: "301300" },
+ { code: "301302", name: "Term Policy Premium", parentCode: "301300" },
+ { code: "301400", name: "CONSULTANT HIRE CHARGES", parentCode: "300000" },
+ { code: "301401", name: "Consultant service charges", parentCode: "301400" },
+ { code: "301402", name: "Consultant travel charges", parentCode: "301400" },
+ { code: "301403", name: "Consultant- Other charges", parentCode: "301400" },
+ { code: "301500", name: "BANKING CHARGES", parentCode: "300000" },
+ { code: "301501", name: "Bank charges- Regular", parentCode: "301500" },
+ { code: "301502", name: "Processing fee / Account renewal", parentCode: "301500" },
+ { code: "301503", name: "Bank charges- Remittance", parentCode: "301500" },
+ { code: "301504", name: "Bank charges- POD", parentCode: "301500" },
+ { code: "301505", name: "Bank charges- Others", parentCode: "301500" },
+ { code: "301600", name: "BANK INTEREST CHARGES", parentCode: "300000" },
+ { code: "301601", name: "Bank Interest on hypothecated loan", parentCode: "301600" },
+ { code: "301602", name: "Bank Interest on unsecured loan", parentCode: "301600" },
+ { code: "301603", name: "Bank penalty & other charges", parentCode: "301600" },
+ { code: "301604", name: "Bank Interest on CC/OD Loan", parentCode: "301600" },
+ { code: "301700", name: "MCA DUES", parentCode: "300000" },
+ { code: "301701", name: "MCA Fee", parentCode: "301700" },
+ { code: "301800", name: "OFFICE AUDIT EXPENSES", parentCode: "300000" },
+ { code: "301801", name: "Audit fee", parentCode: "301800" },
+ { code: "301802", name: "Surveyor travel expenses", parentCode: "301800" },
+ { code: "301803", name: "Surveyor entertainment expenses", parentCode: "301800" },
+ { code: "301900", name: "TRAINING & DEVELOPMENT", parentCode: "300000" },
+ { code: "301901", name: "Training fee", parentCode: "301900" },
+ { code: "301902", name: "Other expenses", parentCode: "301900" },
+ { code: "302000", name: "LEGAL EXP", parentCode: "300000" },
+ { code: "302001", name: "Advocate fee", parentCode: "302000" },
+ { code: "302002", name: "Arbitrator fee", parentCode: "302000" },
+ { code: "302003", name: "Court charges", parentCode: "302000" },
+ { code: "302004", name: "Fines & Penalties", parentCode: "302000" },
+ { code: "302005", name: "Stamp duty", parentCode: "302000" },
+ { code: "302006", name: "Legal- Travel & Logistics", parentCode: "302000" },
+ { code: "302007", name: "Legal cost- Others", parentCode: "302000" },
+ { code: "302100", name: "GUEST HOUSE EXPENSES", parentCode: "300000" },
+ { code: "302101", name: "Guest house rent", parentCode: "302100" },
+ { code: "302102", name: "Electricity bill", parentCode: "302100" },
+ { code: "302103", name: "Maintenance cost", parentCode: "302100" },
+ { code: "302104", name: "Other expenses", parentCode: "302100" },
+ { code: "302200", name: "MEMBERSHIP & SUBSCRIPTION CHARGES", parentCode: "300000" },
+ { code: "302201", name: "Subscription Fee", parentCode: "302200" },
+ { code: "302300", name: "LICENSE RENEWAL CHARGES", parentCode: "300000" },
+ { code: "302301", name: "License renewal fee", parentCode: "302300" },
+ { code: "302400", name: "OFFICE STAFF WELFARE EXPENSES", parentCode: "300000" },
+ { code: "302401", name: "Office staff Medical Expenses", parentCode: "302400" },
+ { code: "302402", name: "Office staff recreation expenses", parentCode: "302400" },
+ { code: "302500", name: "SECURITY DEPOSIT", parentCode: "300000" },
+ { code: "302501", name: "Security deposit for site equipment", parentCode: "302500" },
+ { code: "302502", name: "Security deposit for Office Equipment", parentCode: "302500" },
+
+ // ── PROJECT EXPENSES ──────────────────────────────────────────────────────────
+ { code: "400000", name: "PROJECT EXPENSES", parentCode: null },
+ { code: "400100", name: "CHARTER HIRE", parentCode: "400000" },
+ { code: "400101", name: "Charter hire payment", parentCode: "400100" },
+ { code: "400200", name: "MOB & DEMOB", parentCode: "400000" },
+ { code: "400201", name: "Manpower", parentCode: "400200" },
+ { code: "400202", name: "Cranes/Hydra", parentCode: "400200" },
+ { code: "400203", name: "Transportation", parentCode: "400200" },
+ { code: "400204", name: "Lashing material", parentCode: "400200" },
+ { code: "400205", name: "Towing", parentCode: "400200" },
+ { code: "400206", name: "Workshop expenses", parentCode: "400200" },
+ { code: "400207", name: "Welding & Cutting", parentCode: "400200" },
+ { code: "400208", name: "Berth rent", parentCode: "400200" },
+ { code: "400209", name: "Union dues", parentCode: "400200" },
+ { code: "400210", name: "Facilitation expenses", parentCode: "400200" },
+ { code: "400211", name: "Mob-demob Agency charges", parentCode: "400200" },
+ { code: "400212", name: "Mob-demob lumpsum cost", parentCode: "400200" },
+ { code: "400213", name: "Equipment Insurance Cost", parentCode: "400200" },
+ { code: "400300", name: "SITE EXPENSES", parentCode: "400000" },
+ { code: "400301", name: "Victualling expense", parentCode: "400300" },
+ { code: "400302", name: "Saloon Stores", parentCode: "400300" },
+ { code: "400303", name: "Crew welfare", parentCode: "400300" },
+ { code: "400304", name: "Crew transport", parentCode: "400300" },
+ { code: "400305", name: "Transportation", parentCode: "400300" },
+ { code: "400306", name: "Medical", parentCode: "400300" },
+ { code: "400307", name: "Hotel stay and food", parentCode: "400300" },
+ { code: "400308", name: "Accommodation rent", parentCode: "400300" },
+ { code: "400309", name: "Electricity bill", parentCode: "400300" },
+ { code: "400310", name: "Union dues", parentCode: "400300" },
+ { code: "400311", name: "Contracted Labour Charges", parentCode: "400300" },
+ { code: "400312", name: "Boat expenses", parentCode: "400300" },
+ { code: "400313", name: "Vehicle hire", parentCode: "400300" },
+ { code: "400314", name: "Vehicle diesel", parentCode: "400300" },
+ { code: "400315", name: "Vehicle maintenance", parentCode: "400300" },
+ { code: "400316", name: "Vehicle tax/toll/parking", parentCode: "400300" },
+ { code: "400317", name: "Stationery", parentCode: "400300" },
+ { code: "400318", name: "Computers & Peripherals", parentCode: "400300" },
+ { code: "400319", name: "Postage & Courier", parentCode: "400300" },
+ { code: "400320", name: "Communication", parentCode: "400300" },
+ { code: "400321", name: "Agency charges", parentCode: "400300" },
+ { code: "400322", name: "Entertainment", parentCode: "400300" },
+ { code: "400323", name: "Sundries", parentCode: "400300" },
+ { code: "400324", name: "Covid expenses incl. insurance", parentCode: "400300" },
+ { code: "400400", name: "RECLAMATION EXPENSES", parentCode: "400000" },
+ { code: "400401", name: "Survey expenses", parentCode: "400400" },
+ { code: "400402", name: "Excavator / Hydra Hire", parentCode: "400400" },
+ { code: "400403", name: "Excavator Diesel", parentCode: "400400" },
+ { code: "400404", name: "Excavator Lubes", parentCode: "400400" },
+ { code: "400405", name: "Excavator Spares", parentCode: "400400" },
+ { code: "400406", name: "Excavator Repairs", parentCode: "400400" },
+ { code: "400407", name: "Pipeline repairs & maintenance", parentCode: "400400" },
+ { code: "400408", name: "Transportation (Reclaim)", parentCode: "400400" },
+ { code: "400409", name: "Reclamation stores & consumables", parentCode: "400400" },
+ { code: "400500", name: "PORT DUES", parentCode: "400000" },
+ { code: "400501", name: "Port dues charges", parentCode: "400500" },
+ { code: "400502", name: "Bank charges for PBG", parentCode: "400500" },
+ { code: "400600", name: "PERFORMANCE BANK GUARANTEE", parentCode: "400000" },
+ { code: "400601", name: "Value of PBG", parentCode: "400600" },
+ { code: "400602", name: "Banking charges", parentCode: "400600" },
+ { code: "400700", name: "BANKING CHARGES", parentCode: "400000" },
+ { code: "400800", name: "DOCUMENTATION CHARGES", parentCode: "400000" },
+ { code: "400900", name: "PROJECT RELATED TRAVEL", parentCode: "400000" },
+ { code: "400901", name: "Office staff travel", parentCode: "400900" },
+ { code: "400902", name: "Other staff travel", parentCode: "400900" },
+ { code: "400903", name: "Office staff hotel & food expense", parentCode: "400900" },
+ { code: "400904", name: "Other staff hotel & food expense", parentCode: "400900" },
+ { code: "401000", name: "REPAIRS & MAINTENANCE", parentCode: "400000" },
+ { code: "401001", name: "Spares- DP Engine", parentCode: "401000" },
+ { code: "401002", name: "Spares- Hyd. Engine", parentCode: "401000" },
+ { code: "401003", name: "Spares- Dredge Pump", parentCode: "401000" },
+ { code: "401004", name: "Spares- Deck generator", parentCode: "401000" },
+ { code: "401005", name: "Spares- Cutter", parentCode: "401000" },
+ { code: "401006", name: "Spares- Winches", parentCode: "401000" },
+ { code: "401007", name: "Spares- Hydraulics", parentCode: "401000" },
+ { code: "401008", name: "Spares- Gear box", parentCode: "401000" },
+ { code: "401009", name: "Spares- Transmission system", parentCode: "401000" },
+ { code: "401010", name: "Spares- Attached pumps & Coolers", parentCode: "401000" },
+ { code: "401011", name: "Spares- Safety Equipment", parentCode: "401000" },
+ { code: "401012", name: "Spares- Others", parentCode: "401000" },
+ { code: "401013", name: "Spares- Steel Wires", parentCode: "401000" },
+ { code: "401014", name: "Spares- Electricals", parentCode: "401000" },
+ { code: "401015", name: "Spares- Electronics", parentCode: "401000" },
+ { code: "401016", name: "Spares- Gauges & Instrumentation", parentCode: "401000" },
+ { code: "401017", name: "Spares- Controls and Automation", parentCode: "401000" },
+ { code: "401018", name: "Stores- Steel", parentCode: "401000" },
+ { code: "401019", name: "Stores- Pipelines", parentCode: "401000" },
+ { code: "401020", name: "Stores- Consumables", parentCode: "401000" },
+ { code: "401021", name: "Stores- Ropes", parentCode: "401000" },
+ { code: "401022", name: "Stores- Lifting gear", parentCode: "401000" },
+ { code: "401023", name: "Stores- Safety Equipment & PPE", parentCode: "401000" },
+ { code: "401024", name: "Stores- Cleaning gear", parentCode: "401000" },
+ { code: "401025", name: "Stores- Welding consumables", parentCode: "401000" },
+ { code: "401026", name: "Stores- Gases", parentCode: "401000" },
+ { code: "401027", name: "Stores- Paints", parentCode: "401000" },
+ { code: "401028", name: "Stores- Electrical", parentCode: "401000" },
+ { code: "401029", name: "Stores- Nuts & Bolts", parentCode: "401000" },
+ { code: "401030", name: "Stores- Tools", parentCode: "401000" },
+ { code: "401031", name: "Stores- Others", parentCode: "401000" },
+ { code: "401032", name: "Workshop charges", parentCode: "401000" },
+ { code: "401033", name: "Technician visit charges", parentCode: "401000" },
+ { code: "401034", name: "Travel & Transportation", parentCode: "401000" },
+ { code: "401035", name: "Crane/Hydra charges for repairs", parentCode: "401000" },
+ { code: "401100", name: "CLIENT ENTERTAINMENT", parentCode: "400000" },
+ { code: "401101", name: "Client entertainment expenses Project", parentCode: "401100" },
+
+ // ── MANNING ───────────────────────────────────────────────────────────────────
+ { code: "500000", name: "MANNING", parentCode: null },
+ { code: "500100", name: "CREW WAGES", parentCode: "500000" },
+ { code: "500101", name: "Salary", parentCode: "500100" },
+ { code: "500102", name: "Employee Provident Fund Contribution", parentCode: "500100" },
+ { code: "500103", name: "Seaman Provident Fund Contribution", parentCode: "500100" },
+ { code: "500104", name: "Levy to Seaman Employment Office", parentCode: "500100" },
+ { code: "500105", name: "Crew Welfare to Seaman Employment Office", parentCode: "500100" },
+ { code: "500106", name: "ESIC Contribution", parentCode: "500100" },
+ { code: "500107", name: "WC Premium", parentCode: "500100" },
+ { code: "500108", name: "Income Tax to Seafarer", parentCode: "500100" },
+ { code: "500109", name: "Professional Tax to Seafarer", parentCode: "500100" },
+ { code: "500110", name: "Bonus to Seafarer", parentCode: "500100" },
+ { code: "500111", name: "Labour Welfare Fund Contribution", parentCode: "500100" },
+ { code: "500200", name: "SIGN-ON/OFF", parentCode: "500000" },
+ { code: "500201", name: "Interstate travel", parentCode: "500200" },
+ { code: "500202", name: "Local Travel", parentCode: "500200" },
+ { code: "500203", name: "Medical", parentCode: "500200" },
+ { code: "500204", name: "Hotel", parentCode: "500200" },
+ { code: "500205", name: "Food allowance", parentCode: "500200" },
+ { code: "500206", name: "Personal Protective Equipment", parentCode: "500200" },
+ { code: "500207", name: "License fee etc.", parentCode: "500200" },
+ { code: "500208", name: "Course/ Training fee", parentCode: "500200" },
+ { code: "500209", name: "Travelling Allowance", parentCode: "500200" },
+ { code: "500210", name: "Covid related expenses", parentCode: "500200" },
+ { code: "500211", name: "International travel Air Fare", parentCode: "500200" },
+ { code: "500212", name: "Crew Agency charges", parentCode: "500200" },
+ { code: "500213", name: "Visa charges", parentCode: "500200" },
+ { code: "500214", name: "Meet n Greet Charges", parentCode: "500200" },
+ { code: "500215", name: "Change of Command Fee", parentCode: "500200" },
+
+ // ── TECHNICAL ─────────────────────────────────────────────────────────────────
+ { code: "600000", name: "TECHNICAL", parentCode: null },
+ { code: "600100", name: "DOCKING EXPENSES (TECHNICAL)", parentCode: "600000" },
+ { code: "600101", name: "Drydock- Stores n Consumables", parentCode: "600100" },
+ { code: "600102", name: "Drydock- Ropes n wires", parentCode: "600100" },
+ { code: "600103", name: "Drydock- Lifting gear", parentCode: "600100" },
+ { code: "600104", name: "Drydock- Safety Equipment & PPE", parentCode: "600100" },
+ { code: "600105", name: "Drydock- Cleaning gear", parentCode: "600100" },
+ { code: "600106", name: "Drydock- Nuts n bolts", parentCode: "600100" },
+ { code: "600107", name: "Drydock- Electrical stores", parentCode: "600100" },
+ { code: "600108", name: "Drydock- Tools", parentCode: "600100" },
+ { code: "600109", name: "Drydock- Cranage", parentCode: "600100" },
+ { code: "600110", name: "Drydock- Transportation", parentCode: "600100" },
+ { code: "600200", name: "SURVEY & CERTIFICATION", parentCode: "600000" },
+ { code: "600201", name: "Flag Survey fee", parentCode: "600200" },
+ { code: "600202", name: "Classification Society fee", parentCode: "600200" },
+ { code: "600203", name: "Survey travel expenses", parentCode: "600200" },
+ { code: "600204", name: "Entertainment expenses", parentCode: "600200" },
+ { code: "600300", name: "INSURANCE PREMIUM", parentCode: "600000" },
+ { code: "600301", name: "H&M Insurance premium", parentCode: "600300" },
+ { code: "600302", name: "Loss of Hire Insurance", parentCode: "600300" },
+ { code: "600303", name: "P&I Insurance", parentCode: "600300" },
+ { code: "600304", name: "War risk", parentCode: "600300" },
+ { code: "600305", name: "Other insurance", parentCode: "600300" },
+
+ // ── BUNKER, LUBES & WATER ─────────────────────────────────────────────────────
+ { code: "700000", name: "BUNKER, LUBES & WATER", parentCode: null },
+ { code: "700100", name: "FUEL OIL", parentCode: "700000" },
+ { code: "700101", name: "Diesel", parentCode: "700100" },
+ { code: "700102", name: "Bio-diesel", parentCode: "700100" },
+ { code: "700103", name: "CNG/LNG", parentCode: "700100" },
+ { code: "700104", name: "Other fuels", parentCode: "700100" },
+ { code: "700105", name: "Transportation (fuel oil)", parentCode: "700100" },
+ { code: "700200", name: "OTHER OILS ETC.", parentCode: "700000" },
+ { code: "700201", name: "Engine oil", parentCode: "700200" },
+ { code: "700202", name: "Hydraulic Oil", parentCode: "700200" },
+ { code: "700203", name: "Gear Oil", parentCode: "700200" },
+ { code: "700204", name: "Grease", parentCode: "700200" },
+ { code: "700205", name: "Coolant", parentCode: "700200" },
+ { code: "700206", name: "Distil Water", parentCode: "700200" },
+ { code: "700207", name: "Transportation (Other oils)", parentCode: "700200" },
+ { code: "700300", name: "ANALYSIS EXPENSE", parentCode: "700000" },
+ { code: "700301", name: "Fuel oil analysis", parentCode: "700300" },
+ { code: "700302", name: "Engine oil analysis", parentCode: "700300" },
+ { code: "700303", name: "Hydraulic oil analysis", parentCode: "700300" },
+ { code: "700304", name: "Coolant analysis", parentCode: "700300" },
+ { code: "700305", name: "Other oils analysis", parentCode: "700300" },
+ { code: "700306", name: "Courier charges", parentCode: "700300" },
+];
diff --git a/App/prisma/migrations/20260530000001_account_hierarchy/migration.sql b/App/prisma/migrations/20260530000001_account_hierarchy/migration.sql
new file mode 100644
index 0000000..0ddb492
--- /dev/null
+++ b/App/prisma/migrations/20260530000001_account_hierarchy/migration.sql
@@ -0,0 +1,5 @@
+-- AlterTable: add parentId for hierarchical accounting codes (3-level: TopCategory > SubCategory > Item)
+ALTER TABLE "Account" ADD COLUMN "parentId" TEXT;
+
+ALTER TABLE "Account" ADD CONSTRAINT "Account_parentId_fkey"
+ FOREIGN KEY ("parentId") REFERENCES "Account"("id") ON DELETE SET NULL ON UPDATE CASCADE;
diff --git a/App/prisma/schema.prisma b/App/prisma/schema.prisma
index 777b6ad..268c5a9 100644
--- a/App/prisma/schema.prisma
+++ b/App/prisma/schema.prisma
@@ -125,6 +125,10 @@ model Account {
description String?
isActive Boolean @default(true)
+ parentId String?
+ parent Account? @relation("AccountHierarchy", fields: [parentId], references: [id])
+ children Account[] @relation("AccountHierarchy")
+
purchaseOrders PurchaseOrder[]
lineItems POLineItem[]
}
diff --git a/App/prisma/seed.ts b/App/prisma/seed.ts
index a195cc9..edc4222 100644
--- a/App/prisma/seed.ts
+++ b/App/prisma/seed.ts
@@ -1,5 +1,6 @@
import { PrismaClient, Role } from "@prisma/client";
import bcrypt from "bcryptjs";
+import { ACCOUNTING_CODES } from "./accounting-codes-data";
const db = new PrismaClient();
@@ -177,78 +178,43 @@ async function main() {
const mvCallisto = await findOrCreateVessel("MV Callisto", siteGOA.id, "SITE-010");
await findOrCreateVessel("MV Doris", siteCHE.id, "SITE-011");
- // ─── Accounts ────────────────────────────────────────────────────────────────
- const accTechOps = await db.account.upsert({
- where: { code: "TECH-OPS" },
- update: {},
- create: { code: "TECH-OPS", name: "Technical Operations", description: "Engine, deck equipment and spare parts" },
- });
+ // ─── Accounting Codes (hierarchical) ─────────────────────────────────────────
+ // Seed in two passes: first create all entries without parentId, then link parents
+ const codeIdMap = new Map();
- const accCrewMgt = await db.account.upsert({
- where: { code: "CREW-MGT" },
- update: {},
- create: { code: "CREW-MGT", name: "Crew Management", description: "Manning and crew welfare" },
- });
+ // Pass 1: upsert all entries without parentId to get their IDs
+ for (const entry of ACCOUNTING_CODES) {
+ const rec = await db.account.upsert({
+ where: { code: entry.code },
+ update: { name: entry.name },
+ create: { code: entry.code, name: entry.name },
+ });
+ codeIdMap.set(entry.code, rec.id);
+ }
- const accFuel = await db.account.upsert({
- where: { code: "FUEL-BNK" },
- update: {},
- create: { code: "FUEL-BNK", name: "Fuel & Bunkers", description: "Fuel procurement and bunkering" },
- });
+ // Pass 2: link parent relationships
+ for (const entry of ACCOUNTING_CODES) {
+ if (entry.parentCode) {
+ const parentId = codeIdMap.get(entry.parentCode);
+ if (parentId) {
+ await db.account.update({
+ where: { code: entry.code },
+ data: { parentId },
+ });
+ }
+ }
+ }
- const accSafety = await db.account.upsert({
- where: { code: "SAFETY" },
- update: {},
- create: { code: "SAFETY", name: "Safety & Lifesaving", description: "LSA, firefighting, immersion suits, EPIRBs" },
- });
-
- const accPaint = await db.account.upsert({
- where: { code: "PAINT-MAINT" },
- update: {},
- create: { code: "PAINT-MAINT", name: "Paint & Maintenance", description: "Hull painting, surface prep, coatings" },
- });
-
- const accElect = await db.account.upsert({
- where: { code: "ELECT" },
- update: {},
- create: { code: "ELECT", name: "Electrical Systems", description: "Navigation lights, batteries, marine cable" },
- });
-
- const accNavig = await db.account.upsert({
- where: { code: "NAVIG" },
- update: {},
- create: { code: "NAVIG", name: "Navigation & Charts", description: "ECDIS updates, chart folios, publications" },
- });
-
- await db.account.upsert({
- where: { code: "PROVISION" },
- update: {},
- create: { code: "PROVISION", name: "Crew Provisions", description: "Food, water and domestic supplies" },
- });
-
- const accStores = await db.account.upsert({
- where: { code: "GEN-STORES" },
- update: {},
- create: { code: "GEN-STORES", name: "General Stores", description: "Consumables, cleaning materials, PPE" },
- });
-
- await db.account.upsert({
- where: { code: "CHEM-TREAT" },
- update: {},
- create: { code: "CHEM-TREAT", name: "Chemicals & Treatments", description: "Boiler water treatment, biocides, cleaners" },
- });
-
- const accDeck = await db.account.upsert({
- where: { code: "DECK-EQUIP" },
- update: {},
- create: { code: "DECK-EQUIP", name: "Deck Equipment", description: "Mooring, anchor, deck machinery" },
- });
-
- await db.account.upsert({
- where: { code: "ROPE-RIGG" },
- update: {},
- create: { code: "ROPE-RIGG", name: "Rope & Rigging", description: "Mooring ropes, pilot ladders, wire rope" },
- });
+ // Convenience variables for PO seed data below (map to real leaf codes)
+ const accTechOps = { id: codeIdMap.get("401012")! }; // Spares- Others
+ const accCrewMgt = { id: codeIdMap.get("500101")! }; // Salary
+ const accFuel = { id: codeIdMap.get("700101")! }; // Diesel
+ const accSafety = { id: codeIdMap.get("401023")! }; // Stores- Safety Equipment & PPE
+ const accPaint = { id: codeIdMap.get("401027")! }; // Stores- Paints
+ const accElect = { id: codeIdMap.get("401028")! }; // Stores- Electrical
+ const accNavig = { id: codeIdMap.get("600201")! }; // Flag Survey fee
+ const accStores = { id: codeIdMap.get("401031")! }; // Stores- Others
+ const accDeck = { id: codeIdMap.get("401030")! }; // Stores- Tools
// ─── Vendors ─────────────────────────────────────────────────────────────────
const v1 = await db.vendor.upsert({