+ {vessel && (
+
+ )}
= {
+ TECHNICAL: "TCH",
+ MANNING: "MAN",
+ ACCOUNTS: "ACC",
+ MANAGER: "MGR",
+ SUPERUSER: "SUP",
+ AUDITOR: "AUD",
+ ADMIN: "ADM",
+};
+
+/** Find max existing number for prefix and return prefix-(max+1), zero-padded to 3 digits */
+export function nextId(prefix: string, existingIds: (string | null | undefined)[]): string {
+ const re = new RegExp(`^${prefix}-(\\d+)$`, "i");
+ let max = 0;
+ for (const id of existingIds) {
+ if (!id) continue;
+ const m = id.match(re);
+ if (m) max = Math.max(max, parseInt(m[1], 10));
+ }
+ return `${prefix}-${String(max + 1).padStart(3, "0")}`;
+}
diff --git a/App/prisma/migrations/20260527010000_vessel_code/migration.sql b/App/prisma/migrations/20260527010000_vessel_code/migration.sql
new file mode 100644
index 0000000..318cd2b
--- /dev/null
+++ b/App/prisma/migrations/20260527010000_vessel_code/migration.sql
@@ -0,0 +1,12 @@
+ALTER TABLE "Vessel" ADD COLUMN "code" TEXT;
+
+WITH numbered AS (
+ SELECT id, ROW_NUMBER() OVER (ORDER BY id) AS n
+ FROM "Vessel"
+)
+UPDATE "Vessel" SET "code" = 'SITE-' || LPAD(n::text, 3, '0')
+FROM numbered
+WHERE "Vessel".id = numbered.id;
+
+ALTER TABLE "Vessel" ALTER COLUMN "code" SET NOT NULL;
+ALTER TABLE "Vessel" ADD CONSTRAINT "Vessel_code_key" UNIQUE ("code");
diff --git a/App/prisma/schema.prisma b/App/prisma/schema.prisma
index e832d97..17a0cd1 100644
--- a/App/prisma/schema.prisma
+++ b/App/prisma/schema.prisma
@@ -109,6 +109,7 @@ model Site {
model Vessel {
id String @id @default(cuid())
name String
+ code String @unique
isActive Boolean @default(true)
siteId String?
diff --git a/App/prisma/seed.ts b/App/prisma/seed.ts
index 2f78055..a195cc9 100644
--- a/App/prisma/seed.ts
+++ b/App/prisma/seed.ts
@@ -157,25 +157,25 @@ async function main() {
});
// ─── Vessels (Cost Centres) ──────────────────────────────────────────────────
- const findOrCreateVessel = async (name: string, siteId: string) => {
+ const findOrCreateVessel = async (name: string, siteId: string, code: string) => {
const vessel = await db.vessel.findFirst({ where: { name } });
if (vessel) {
return db.vessel.update({ where: { id: vessel.id }, data: { siteId } });
}
- return db.vessel.create({ data: { name, siteId } });
+ return db.vessel.create({ data: { name, code, siteId } });
};
- const mvStar = await findOrCreateVessel("MV Pelagia Star", siteBOM.id);
- const mvWind = await findOrCreateVessel("MV Aegean Wind", siteJNP.id);
- const mvPoseidon = await findOrCreateVessel("MV Poseidon", siteKDL.id);
- const mvNereid = await findOrCreateVessel("MV Nereid", siteCHE.id);
- const mvThetis = await findOrCreateVessel("MV Thetis", siteKOC.id);
- const mvTriton = await findOrCreateVessel("MV Triton", siteVIZ.id);
- const mvAmphitrite = await findOrCreateVessel("MV Amphitrite", siteHAL.id);
- const mvProteus = await findOrCreateVessel("MV Proteus", sitePAR.id);
- const mvGalatea = await findOrCreateVessel("MV Galatea", siteMNG.id);
- const mvCallisto = await findOrCreateVessel("MV Callisto", siteGOA.id);
- await findOrCreateVessel("MV Doris", siteCHE.id);
+ const mvStar = await findOrCreateVessel("MV Pelagia Star", siteBOM.id, "SITE-001");
+ const mvWind = await findOrCreateVessel("MV Aegean Wind", siteJNP.id, "SITE-002");
+ const mvPoseidon = await findOrCreateVessel("MV Poseidon", siteKDL.id, "SITE-003");
+ const mvNereid = await findOrCreateVessel("MV Nereid", siteCHE.id, "SITE-004");
+ const mvThetis = await findOrCreateVessel("MV Thetis", siteKOC.id, "SITE-005");
+ const mvTriton = await findOrCreateVessel("MV Triton", siteVIZ.id, "SITE-006");
+ const mvAmphitrite = await findOrCreateVessel("MV Amphitrite", siteHAL.id, "SITE-007");
+ const mvProteus = await findOrCreateVessel("MV Proteus", sitePAR.id, "SITE-008");
+ const mvGalatea = await findOrCreateVessel("MV Galatea", siteMNG.id, "SITE-009");
+ 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({
diff --git a/Progress/TODO.md b/Progress/TODO.md
index 5d22a99..d497dc2 100644
--- a/Progress/TODO.md
+++ b/Progress/TODO.md
@@ -117,22 +117,22 @@
- site edit does not work? location updates but does not save
-- WAIT SAVING WORKS, THE BUTTON JUST SHOWS SAVING FOR SOME REASON
- pincode to geocode in bg. works on initial save
-- confirm button while deleting
+- confirm button while deleting [DONE]
- vessel edit does not save?
- 0 percent gst does not work [DONE]
- who assigns vendor?
-- email notification take you to po
-- po word appears twice in subject
+- email notification take you to po [DONE]
+- po word appears twice in subject [DONE]
- notification overflows on phone for manager [DONE]
-- gst edit does not show strikethrough update
-- submitted for review notification not needed for submitter
-- show detail of po in email
+- gst edit does not show strikethrough update [DONE]
+- submitted for review notification not needed for submitter [DONE]
+- show detail of po in email [DONE]
- accounts did not get notification
-- in manager/submitter notes show name of person
-- Confirming... button appears as soon as I click Process Payment as accounts
-- phantom product prices updated by accounts in PO history
-- notify manager for receipt and partial receipt
+- in manager/submitter notes show name of person [DONE]
+- Confirming... button appears as soon as I click Process Payment as accounts [DONE]
+- phantom product prices updated by accounts in PO history [DONE]
+- notify manager for receipt and partial receipt [DONE]
- allow submitter to close partially closed po [DONE]
-- partial payment / advance payment for accounts
-- please confirm receipt - only for submitter not for manager
-- rename My Purchase Orders to Closed Purchase Orders
+- partial payment / advance payment for accounts [DONE]
+- please confirm receipt - only for submitter not for manager [DONE]
+- rename My Purchase Orders to Closed Purchase Orders [DONE]