docs(crewing): fix leave-planner inconsistencies

- Leave is applied by the Site in-charge on behalf of crew (crew are not
  portal users), not self-service.
- Attendance is visible to site staff + Manager only; the MPO has no
  attendance access. Drop MPO from record_attendance and (consequently)
  generate_wage_report; add a view_attendance permission.
- A leave clash (approved leaves overlapping below a rank's required
  strength) auto-raises a LEAVE requisition; documented across module,
  design decisions, data model, workflows (new sequence), use cases,
  user stories, roles matrix and the architecture diagram.
Hardik 2026-06-22 05:13:22 +05:30
parent 8eaf2ed0a0
commit 2fcd41596e
8 changed files with 130 additions and 34 deletions

@ -113,6 +113,7 @@ flowchart TB
PPE --> A5 --> DB PPE --> A5 --> DB
PLAN --> A6 PLAN --> A6
ATT --> A6 --> DB ATT --> A6 --> DB
A6 -.leave clash.-> SM1
APPR --> A7 --> SM4 --> DB APPR --> A7 --> SM4 --> DB
PAY --> A8 --> WR --> DB PAY --> A8 --> WR --> DB
PAY --> H3 PAY --> H3

@ -210,7 +210,7 @@ erDiagram
string assignmentId FK string assignmentId FK
date date date date
enum status "AttendanceStatus" enum status "AttendanceStatus"
string recordedById FK string recordedById FK "site staff; visible to site + Manager, not MPO"
} }
LEAVE_REQUEST { LEAVE_REQUEST {
string id PK string id PK
@ -219,7 +219,7 @@ erDiagram
date fromDate date fromDate
date toDate date toDate
enum status "LeaveStatus" enum status "LeaveStatus"
string appliedById FK string appliedById FK "Site in-charge (applies for crew)"
} }
PPE_ISSUE { PPE_ISSUE {
string id PK string id PK
@ -327,8 +327,15 @@ erDiagram
appends an `EXPERIENCE_RECORD`, and triggers a backfill requisition. appends an `EXPERIENCE_RECORD`, and triggers a backfill requisition.
- **Verification is a field**`verificationStatus + verifiedById` on - **Verification is a field**`verificationStatus + verifiedById` on
documents, bank and EPF. Routing rule (from the notebook): site-entered data → documents, bank and EPF. Routing rule (from the notebook): site-entered data →
**MPO**; bank + EPF → **Accounts**; attendance is operational (no separate **MPO**; bank + EPF → **Accounts**. **Attendance** is operational: no verify
verify gate). gate, and it is **visible only to site staff and the Manager — the MPO has no
attendance access** (the Manager reviews it for the wage report).
- **Leave** (`LEAVE_REQUEST`) is **applied by the Site in-charge for a crew
member** (`appliedById` = site staff, `assignmentId` = the crew member) and
**decided by the MPO**. An approved leave that **clashes** — overlaps others for
the same rank below its required strength — auto-raises a `Requisition`
(`reason = LEAVE`, `autoRaised`, system-raised), mirroring the sign-off backfill
on `CrewAssignment`.
- **`SalaryStructure`** holds `basic`, `victualingPerDay`, and a JSON - **`SalaryStructure`** holds `basic`, `victualingPerDay`, and a JSON
`allowances`; approved by a Manager. Salary is the restricted field on the `allowances`; approved by a Manager. Salary is the restricted field on the
contract letter for site staff. contract letter for site staff.

@ -51,7 +51,9 @@ A crew member goes on leave, finishes a contract (EOC), is terminated, is
signed off on medical grounds, or leaves for another reason. Any of these signed off on medical grounds, or leaves for another reason. Any of these
creates a **gap** for a specific **rank** on a specific **vessel/site**. On a creates a **gap** for a specific **rank** on a specific **vessel/site**. On a
*scheduled sign-off* the gap (and its **Requisition**) is raised automatically; *scheduled sign-off* the gap (and its **Requisition**) is raised automatically;
otherwise site staff or the MPO raise it manually. otherwise site staff or the MPO raise it manually. A **leave clash** — when
approved leaves overlap so a rank drops below its required strength on a
vessel/site — likewise **auto-raises a Requisition** (reason `LEAVE`).
### 3.2 Sourcing candidates ### 3.2 Sourcing candidates
The MPO shortlists candidates for the rank. **Ex-hands are preferred** (people The MPO shortlists candidates for the rank. **Ex-hands are preferred** (people
@ -89,14 +91,19 @@ The crew member receives an **Employee ID**.
### 3.5 Life on site ### 3.5 Life on site
Site staff record **attendance** daily, **issue PPE** (with boiler-suit and shoe Site staff record **attendance** daily, **issue PPE** (with boiler-suit and shoe
sizes), maintain **documents/bank/EPF/next-of-kin/emergency contact**, and apply sizes), maintain **documents/bank/EPF/next-of-kin/emergency contact**, and apply
for **leave** through the site **leave planner** (which shows every crew for **leave on behalf of crew members** through the site **leave planner** (which
member's contract span and leaves on one timeline). The PM raises **appraisals**; shows every crew member's contract span and leaves on one timeline; crew are data
the **MPO verifies**, the **Manager approves**. subjects, not portal users, so the **Site in-charge applies leave for them**). If
an approved leave **clashes** with another for the same rank, the planner
auto-raises a backfill **Requisition**. The PM raises **appraisals**; the **MPO
verifies**, the **Manager approves**.
### 3.6 Office verification ### 3.6 Office verification
The office is the source of truth for verified data. **The MPO verifies The office is the source of truth for verified data. **The MPO verifies
everything site staff enter, except attendance**; **bank details and EPF are everything site staff enter, except attendance** — which the **MPO cannot see at
verified by Accounts** (UAN/Aadhaar checked against EPFO). all**; attendance is an operational record reviewed by the **Manager** (it feeds
the wage report). **Bank details and EPF are verified by Accounts** (UAN/Aadhaar
checked against EPFO).
### 3.7 Sign-off & backfill ### 3.7 Sign-off & backfill
When a crew member signs off, the assignment closes, their **experience record When a crew member signs off, the assignment closes, their **experience record
@ -122,6 +129,8 @@ Manager**, and **sent to Accounts** for disbursement.
| D8 | **Wage report is a generated artefact**, not live-computed on the Accounts screen. | Auditable monthly snapshot; mirrors PO export/report pattern. | | D8 | **Wage report is a generated artefact**, not live-computed on the Accounts screen. | Auditable monthly snapshot; mirrors PO export/report pattern. |
| D9 | **Reuse `Vessel`/`Site` as the cost centre** so crew cost is attributable on the same axis as PO spend. | Single cost-centre vocabulary across the portal. | | D9 | **Reuse `Vessel`/`Site` as the cost centre** so crew cost is attributable on the same axis as PO spend. | Single cost-centre vocabulary across the portal. |
| D10 | **CV parsing is best-effort with manual fallback.** Extraction populates a draft; a human confirms. | Never block intake on a parser; keep the office in control. | | D10 | **CV parsing is best-effort with manual fallback.** Extraction populates a draft; a human confirms. | Never block intake on a parser; keep the office in control. |
| D11 | **A leave clash auto-raises a Requisition.** When approved leaves overlap so a rank falls below its required strength, the system raises a `LEAVE`-reason requisition automatically (same path as the sign-off backfill). | Overlapping leave was the planner's main blind spot; auto-raising keeps cover proactive instead of reactive. |
| D12 | **Attendance is Manager-only at the office.** Site staff record it; the **Manager** reviews it; the **MPO has no attendance access**. | The MPO's remit is recruitment + record verification (docs/PPE/leave/NoK); attendance is an operational/payroll input the Manager owns. |
## 5. Non-functional requirements ## 5. Non-functional requirements
@ -148,6 +157,9 @@ Manager**, and **sent to Accounts** for disbursement.
result); a programmatic check can follow the GstService precedent. result); a programmatic check can follow the GstService precedent.
4. "Victualing" is a per-day messing allowance, configurable per rank/vessel. 4. "Victualing" is a per-day messing allowance, configurable per rank/vessel.
5. One **active** assignment per crew member at a time. 5. One **active** assignment per crew member at a time.
6. Each rank has a **required strength** per vessel/site (default 1). A *leave
clash* = approved leaves overlapping such that active crew for that rank would
fall below that strength; this is what triggers the auto-requisition (D11).
## 7. Open questions ## 7. Open questions

@ -16,9 +16,9 @@ for email/in-app events.
Today crewing lives in notebooks, WhatsApp, and spreadsheets (the source notes Today crewing lives in notebooks, WhatsApp, and spreadsheets (the source notes
for this design were literally two notebook pages). The pain points it removes: for this design were literally two notebook pages). The pain points it removes:
- **Vacancies are reactive and untracked.** A sign-off or leave creates a gap - **Vacancies are reactive and untracked.** A sign-off or a leave clash creates a
that nobody formally owns until a vessel is short-handed. → Auto-raised gap that nobody formally owns until a vessel is short-handed. → Auto-raised
**Requisitions** on sign-off / end-of-contract. **Requisitions** on sign-off / end-of-contract and on a leave clash.
- **Candidate vetting is ad-hoc.** Competency, document, reference and salary - **Candidate vetting is ad-hoc.** Competency, document, reference and salary
checks happen over chat with no record of who cleared what. → A gated checks happen over chat with no record of who cleared what. → A gated
**Candidate Pipeline** with an event log. **Candidate Pipeline** with an event log.
@ -32,17 +32,17 @@ for this design were literally two notebook pages). The pain points it removes:
| Area | Capability | | Area | Capability |
|---|---| |---|---|
| **Requisitions** | Auto- or manually-raised vacancy for a rank on a vessel/site; reason (leave / end-of-contract / termination / medical / other). | | **Requisitions** | Auto-raised (on sign-off / end-of-contract, or when a **leave clash** leaves a rank short) or manually-raised vacancy for a rank on a vessel/site; reason (leave / end-of-contract / termination / medical / other). |
| **Candidate pipeline** | Shortlist (ex-hand preferred) → competency → document verification → reference check → salary agreement → proposal → interview (optional for ex-hands) → selection / rejection with remarks. | | **Candidate pipeline** | Shortlist (ex-hand preferred) → competency → document verification → reference check → salary agreement → proposal → interview (optional for ex-hands) → selection / rejection with remarks. |
| **CV intake** | Self-apply on the Pelagia site or MPO upload; CV parsed for age, vessel type, rank and experience, with manual fallback. | | **CV intake** | Self-apply on the Pelagia site or MPO upload; CV parsed for age, vessel type, rank and experience, with manual fallback. |
| **Onboarding** | One selection event starts salary, victualing, attendance, experience accrual, PF tracking and the PPE issue checklist. | | **Onboarding** | One selection event starts salary, victualing, attendance, experience accrual, PF tracking and the PPE issue checklist. |
| **Crew records** | Bank details, EPF/UAN, next of kin, emergency contact, contract letter, and role-based seafarer documents (STCW, Aadhaar, PAN, Passport, CDC, COC, photo, licence, medical fitness). | | **Crew records** | Bank details, EPF/UAN, next of kin, emergency contact, contract letter, and role-based seafarer documents (STCW, Aadhaar, PAN, Passport, CDC, COC, photo, licence, medical fitness). |
| **PPE issue register** | Boiler-suit & shoe sizes plus issue tracking for the full PPE kit. | | **PPE issue register** | Boiler-suit & shoe sizes plus issue tracking for the full PPE kit. |
| **Leave & attendance** | Site-level leave planner (contracts + leaves on one timeline) and daily attendance capture. | | **Leave & attendance** | Site-level leave planner — the **Site in-charge applies leave on behalf of crew**, and an overlapping **leave clash auto-raises a requisition**. Daily attendance is captured on site and reviewed by the **Manager** (the MPO has no attendance access). |
| **Appraisal** | PM raises → MPO verifies → Manager approves. | | **Appraisal** | PM raises → MPO verifies → Manager approves. |
| **Sign-off** | Updates experience, closes the assignment, and auto-raises the backfill requisition. | | **Sign-off** | Updates experience, closes the assignment, and auto-raises the backfill requisition. |
| **Wage report** | Monthly, per site: crew × salary × days attended (+ victualing), generated and sent to Accounts. | | **Wage report** | Monthly, per site: crew × salary × days attended (+ victualing), generated and sent to Accounts. |
| **Verification** | Office sign-off: MPO verifies everything except attendance; bank + EPF verified by Accounts. | | **Verification** | Office sign-off: MPO verifies everything except attendance (which the MPO cannot see — it is the Manager's to review); bank + EPF verified by Accounts. |
## How it fits PPMS ## How it fits PPMS

@ -9,10 +9,10 @@ relevant state machine adds the status+role gate on top.
| Crewing actor | PPMS role | Notes | | Crewing actor | PPMS role | Notes |
|---|---|---| |---|---|---|
| PM / APM / Site In-charge | **`SITE_STAFF`** (new, proposed) | apply-only leave, attendance, PPE issue, doc upload, view-only contract (except salary) & bank | | PM / APM / Site In-charge | **`SITE_STAFF`** (new, proposed) | apply-only leave **(on behalf of crew)**, attendance, PPE issue, doc upload, view-only contract (except salary) & bank |
| MPO | **`MANNING`** (existing — "crew-management staff") | recruitment + verifies all site data except bank/EPF | | MPO | **`MANNING`** (existing — "crew-management staff") | recruitment + verifies all site data except bank/EPF; **no attendance access** |
| Accounts | **`ACCOUNTS`** | verifies bank + EPF; consumes wage report | | Accounts | **`ACCOUNTS`** | verifies bank + EPF; consumes wage report |
| Manager | **`MANAGER`** | approves salary structures, candidate list, appraisals, wage reports | | Manager | **`MANAGER`** | approves salary structures, candidate list, appraisals, wage reports; **reviews attendance** |
| Superuser | **`SUPERUSER`** | combined authority across crewing actions | | Superuser | **`SUPERUSER`** | combined authority across crewing actions |
| Auditor | **`AUDITOR`** | read-only across crewing | | Auditor | **`AUDITOR`** | read-only across crewing |
| Admin | **`ADMIN`** | manages ranks & doc requirements | | Admin | **`ADMIN`** | manages ranks & doc requirements |
@ -44,13 +44,14 @@ relevant state machine adds the status+role gate on top.
| `issue_ppe` | ✓ | ✓ | | ✓ | ✓ | | | | `issue_ppe` | ✓ | ✓ | | ✓ | ✓ | | |
| `apply_leave` | ✓ | ✓ | | ✓ | ✓ | | | | `apply_leave` | ✓ | ✓ | | ✓ | ✓ | | |
| `decide_leave` | | ✓ | | ✓ | ✓ | | | | `decide_leave` | | ✓ | | ✓ | ✓ | | |
| `record_attendance` | ✓ | ✓ | | ✓ | ✓ | | | | `record_attendance` | ✓ | | | | ✓ | | |
| `view_attendance` ³ | ✓ | | | ✓ | ✓ | ✓ | |
| `verify_site_records` (docs/PPE/leave/NoK) | | ✓ | | ✓ | ✓ | | | | `verify_site_records` (docs/PPE/leave/NoK) | | ✓ | | ✓ | ✓ | | |
| `verify_bank_epf` | | | ✓ | ✓ | ✓ | | | | `verify_bank_epf` | | | ✓ | ✓ | ✓ | | |
| `raise_appraisal` | ✓ | | | ✓ | ✓ | | | | `raise_appraisal` | ✓ | | | ✓ | ✓ | | |
| `verify_appraisal` | | ✓ | | ✓ | ✓ | | | | `verify_appraisal` | | ✓ | | ✓ | ✓ | | |
| `approve_appraisal` | | | | ✓ | ✓ | | | | `approve_appraisal` | | | | ✓ | ✓ | | |
| `generate_wage_report` | | | | ✓ | ✓ | | | | `generate_wage_report` | | | | ✓ | ✓ | | |
| `approve_wage_report` | | | | ✓ | ✓ | | | | `approve_wage_report` | | | | ✓ | ✓ | | |
| `view_wage_report` | | | ✓ | ✓ | ✓ | ✓ | ✓ | | `view_wage_report` | | | ✓ | ✓ | ✓ | ✓ | ✓ |
| `manage_ranks` | | | | ✓ | | | ✓ | | `manage_ranks` | | | | ✓ | | | ✓ |
@ -58,6 +59,10 @@ relevant state machine adds the status+role gate on top.
¹ Site staff get crew records for **their own site only**, with contract ¹ Site staff get crew records for **their own site only**, with contract
**view-only-except-salary** and bank **view-only**. **view-only-except-salary** and bank **view-only**.
² Accounts see bank/EPF and the wage report, not the full HR record. ² Accounts see bank/EPF and the wage report, not the full HR record.
³ **Attendance is deliberately not visible to the MPO** (or Accounts). Site staff
record it; the **Manager** reviews it (it feeds the wage report).
⁴ The wage report reads attendance, so it is generated by the **Manager** (or the
month-end job) — **not** the MPO, who has no attendance access.
## 3. Field-level restrictions (PII) ## 3. Field-level restrictions (PII)
@ -76,7 +81,14 @@ Enforced in Server Components/Actions on top of the permission map:
**Manager-approved** and a contract letter is attached. **Manager-approved** and a contract letter is attached.
- **Bank and EPF** records can only be set `VERIFIED` by **Accounts** (UAN/Aadhaar - **Bank and EPF** records can only be set `VERIFIED` by **Accounts** (UAN/Aadhaar
EPFO check); everything else site staff enter is verified by **MPO**. EPFO check); everything else site staff enter is verified by **MPO**.
- **Attendance** has no verification gate (operational record). - **Attendance** has no verification gate (operational record). It is **recorded by
site staff and visible only to site staff and the Manager** — the **MPO cannot
see attendance**; the Manager reviews it (it feeds the wage report).
- **Leave is applied by the Site in-charge on behalf of crew** (crew are not portal
users); the **MPO decides** (approves/rejects) leave. An **approved leave that
clashes** — overlaps another for the same rank below its required strength —
**auto-raises a `LEAVE`-reason requisition** (system-raised, same backfill path
as sign-off).
- An **appraisal** must be `MPO_VERIFIED` before a Manager can approve it. - An **appraisal** must be `MPO_VERIFIED` before a Manager can approve it.
- A **wage report** must be `MANAGER_APPROVED` before it is `SENT_TO_ACCOUNTS`. - A **wage report** must be `MANAGER_APPROVED` before it is `SENT_TO_ACCOUNTS`.
- Only **one ACTIVE assignment** per crew member; sign-off is required before a - Only **one ACTIVE assignment** per crew member; sign-off is required before a

@ -18,6 +18,7 @@ flowchart LR
subgraph Requisition subgraph Requisition
U1(["Raise requisition"]) U1(["Raise requisition"])
U2(["Sign off crew → auto-requisition"]) U2(["Sign off crew → auto-requisition"])
U2b(["Leave clash → auto-requisition (system)"])
U3(["Cancel requisition"]) U3(["Cancel requisition"])
end end
subgraph Recruitment subgraph Recruitment
@ -57,7 +58,7 @@ flowchart LR
SS --- U1 & U2 & U12 & U13 & U14 & U15 & U16 & U19 SS --- U1 & U2 & U12 & U13 & U14 & U15 & U16 & U19
CAND --- U4 CAND --- U4
MPO --- U5 & U6 & U7 & U8 & U9 & U11 & U17 & U20 MPO --- U5 & U6 & U7 & U8 & U9 & U11 & U17 & U20 & U2b
MGR --- U3 & U9 & U10 & U11 & U21 & U22 & U23 & U24 MGR --- U3 & U9 & U10 & U11 & U21 & U22 & U23 & U24
ACC --- U18 & U24 ACC --- U18 & U24
ADM --- U25 ADM --- U25
@ -78,9 +79,9 @@ flowchart LR
| UC-09 | Select / reject with remarks | MPO / Manager | interview done / waived | `SELECTED` or `REJECTED` + remarks | | UC-09 | Select / reject with remarks | MPO / Manager | interview done / waived | `SELECTED` or `REJECTED` + remarks |
| UC-10 | Approve salary structure & candidate list | Manager | candidate proposed | salary structure approved | | UC-10 | Approve salary structure & candidate list | Manager | candidate proposed | salary structure approved |
| UC-11 | Onboard candidate | MPO / Manager | candidate selected | `CrewAssignment(ACTIVE)`; salary/victualing/attendance/experience/PF/PPE started; employeeId issued; requisition `FILLED` | | UC-11 | Onboard candidate | MPO / Manager | candidate selected | `CrewAssignment(ACTIVE)`; salary/victualing/attendance/experience/PF/PPE started; employeeId issued; requisition `FILLED` |
| UC-12 | Apply for leave | Site staff | active assignment | `LeaveRequest(APPLIED)` | | UC-12 | Apply for leave (for a crew member) | Site in-charge | crew has active assignment | `LeaveRequest(APPLIED)` on the crew member's assignment |
| UC-13 | View leave planner | Site staff / MPO / Manager | — | timeline of contracts + leaves per site | | UC-13 | View leave planner | Site staff / MPO / Manager | — | timeline of contracts + leaves per site |
| UC-14 | Record attendance | Site staff | active assignment | daily `Attendance` rows | | UC-14 | Record attendance | Site staff | active assignment | daily `Attendance` rows (visible to site staff + Manager; **not** the MPO) |
| UC-15 | Issue PPE | Site staff | active assignment | `PpeIssue` with size (boiler suit/shoes) | | UC-15 | Issue PPE | Site staff | active assignment | `PpeIssue` with size (boiler suit/shoes) |
| UC-16 | Upload documents / EPF / next-of-kin / emergency | Site staff | crew exists | records created `PENDING` verification | | UC-16 | Upload documents / EPF / next-of-kin / emergency | Site staff | crew exists | records created `PENDING` verification |
| UC-17 | Verify docs / PPE / leave | MPO | pending records exist | `VERIFIED` (everything except attendance) | | UC-17 | Verify docs / PPE / leave | MPO | pending records exist | `VERIFIED` (everything except attendance) |
@ -92,6 +93,7 @@ flowchart LR
| UC-23 | Approve wage report | Manager | report generated | `MANAGER_APPROVED``SENT_TO_ACCOUNTS` | | UC-23 | Approve wage report | Manager | report generated | `MANAGER_APPROVED``SENT_TO_ACCOUNTS` |
| UC-24 | View / export wage report | Accounts / Manager | report sent | XLSX/PDF export for disbursement | | UC-24 | View / export wage report | Accounts / Manager | report sent | XLSX/PDF export for disbursement |
| UC-25 | Manage ranks & doc requirements | Admin | — | rank hierarchy + per-rank required documents | | UC-25 | Manage ranks & doc requirements | Admin | — | rank hierarchy + per-rank required documents |
| UC-26 | Auto-requisition on leave clash | System | approved leaves overlap below a rank's required strength | `Requisition(OPEN, reason=LEAVE, autoRaised)`; MPO notified |
## 3. Notable extensions / alternates ## 3. Notable extensions / alternates
@ -105,3 +107,10 @@ flowchart LR
for the site to correct. for the site to correct.
- **UC-22 alt** — attendance gaps flagged before generation; report can be - **UC-22 alt** — attendance gaps flagged before generation; report can be
regenerated until approved. regenerated until approved.
- **UC-12 note** — crew are data subjects, not portal users, so the **Site
in-charge applies leave on their behalf** (apply-only; the MPO decides it).
- **UC-14 note** — attendance is recorded by site staff and **reviewed by the
Manager**; the **MPO has no attendance access** (so the wage report, UC-22, is
generated by the Manager / month-end job, not the MPO).
- **UC-26** — a leave clash (overlapping approved leaves below a rank's required
strength) auto-raises a `LEAVE` requisition; same backfill path as UC-02.

@ -35,6 +35,16 @@ withdrawn vacancies don't clutter the pipeline.
- AC1: only non-`FILLED` requisitions can be cancelled. - AC1: only non-`FILLED` requisitions can be cancelled.
- AC2: reason is required and audited. - AC2: reason is required and audited.
**A5 — Auto-requisition on leave clash**
As the **system**, I want a requisition raised automatically when an approved
leave clashes (a rank drops below its required strength on a vessel/site), so that
the cover gap is owned immediately.
- AC1: approving a leave that overlaps others for the same rank below required
strength creates a `Requisition(OPEN, reason=LEAVE, autoRaised)`.
- AC2: the MPO is notified (email + in-app) and the clash is highlighted on the
planner.
- AC3: a `CrewAction(REQUISITION_RAISED)` is written.
--- ---
## Epic B — Candidate intake ## Epic B — Candidate intake
@ -194,23 +204,29 @@ As **site staff**, I want to record PPE returns, so that the register is current
As **site staff / MPO / Manager**, I want a planner showing every crew member's As **site staff / MPO / Manager**, I want a planner showing every crew member's
contract span and leaves on one timeline, so that I can plan cover. contract span and leaves on one timeline, so that I can plan cover.
- AC1: timeline per site with contract bars + leave bars. - AC1: timeline per site with contract bars + leave bars.
- AC2: overlapping absences for the same rank are highlighted. - AC2: overlapping absences for the same rank are highlighted (a clash below
required strength auto-raises a requisition — see **A5**).
**G2 — Apply for leave** **G2 — Apply for leave (for a crew member)**
As **site staff**, I want to apply for leave (apply-only), so that the office can As the **Site in-charge**, I want to apply for leave **on behalf of a crew member**
approve it. (apply-only; crew are not portal users), so that the office can approve it.
- AC1: I cannot self-approve; status starts `APPLIED`. - AC1: I select the crew member's assignment, dates and type; status starts `APPLIED`.
- AC2: site staff cannot self-approve (no `decide_leave`).
**G3 — Decide leave** **G3 — Decide leave**
As an **MPO**, I want to approve/reject leave with a note, so that the planner As an **MPO**, I want to approve/reject leave with a note, so that the planner
stays accurate. stays accurate.
- AC1: approval moves the assignment to `ON_LEAVE` for the period. - AC1: approval moves the assignment to `ON_LEAVE` for the period.
- AC2: if the approval creates a clash (the rank falls below required strength for
the overlap), a backfill requisition is auto-raised — see **A5**.
**G4 — Record attendance** **G4 — Record attendance**
As **site staff**, I want to record daily attendance, so that wages can be As **site staff**, I want to record daily attendance, so that wages can be
computed. computed.
- AC1: one row per assignment per day; statuses present/absent/half/leave/sign-off. - AC1: one row per assignment per day; statuses present/absent/half/leave/sign-off.
- AC2: no verification gate (operational). - AC2: no verification gate (operational).
- AC3: attendance is visible to **site staff and the Manager only** — the **MPO
has no attendance access**.
--- ---
@ -238,6 +254,8 @@ As a **Manager**, I want to approve a verified appraisal, so that it's final.
As an **MPO**, I want a queue of pending site records (documents, PPE, leave, As an **MPO**, I want a queue of pending site records (documents, PPE, leave,
next-of-kin), so that I can verify everything except attendance. next-of-kin), so that I can verify everything except attendance.
- AC1: verify sets `VERIFIED` + me as `verifiedBy`; reject returns with remarks. - AC1: verify sets `VERIFIED` + me as `verifiedBy`; reject returns with remarks.
- AC2: **attendance never appears in this queue** — the MPO has no attendance
access (it is the Manager's to review).
**I2 — Accounts bank/EPF verification** **I2 — Accounts bank/EPF verification**
As **Accounts**, I want to verify bank details and EPF (UAN/Aadhaar vs EPFO), so As **Accounts**, I want to verify bank details and EPF (UAN/Aadhaar vs EPFO), so

@ -9,11 +9,12 @@ tables**, and **sequence diagrams** for the end-to-end flows.
## 1. Requisition lifecycle ## 1. Requisition lifecycle
The vacancy. Raised automatically on sign-off / end-of-contract, or manually. The vacancy. Raised automatically on sign-off / end-of-contract or on a **leave
clash**, or manually.
```mermaid ```mermaid
stateDiagram-v2 stateDiagram-v2
[*] --> OPEN : raise (auto on sign-off / manual) [*] --> OPEN : raise (auto on sign-off / leave clash / manual)
OPEN --> SHORTLISTING : add candidates OPEN --> SHORTLISTING : add candidates
SHORTLISTING --> PROPOSING : a candidate clears vetting SHORTLISTING --> PROPOSING : a candidate clears vetting
PROPOSING --> INTERVIEWING : interview scheduled PROPOSING --> INTERVIEWING : interview scheduled
@ -29,7 +30,7 @@ stateDiagram-v2
| From | Action | To | Roles | Side-effect | | From | Action | To | Roles | Side-effect |
|---|---|---|---|---| |---|---|---|---|---|
| — | `raise` | OPEN | system (sign-off) / SITE_STAFF / MANNING | email MPO | | — | `raise` | OPEN | system (sign-off **or leave clash**) / SITE_STAFF / MANNING | email MPO |
| OPEN | `start_shortlist` | SHORTLISTING | MANNING | — | | OPEN | `start_shortlist` | SHORTLISTING | MANNING | — |
| SHORTLISTING | `propose` | PROPOSING | MANNING | email Manager | | SHORTLISTING | `propose` | PROPOSING | MANNING | email Manager |
| PROPOSING | `schedule_interview` | INTERVIEWING | MANNING | email candidate | | PROPOSING | `schedule_interview` | INTERVIEWING | MANNING | email candidate |
@ -93,6 +94,40 @@ stateDiagram-v2
`sign-off` closes the tour, appends an `EXPERIENCE_RECORD`, and **auto-raises a `sign-off` closes the tour, appends an `EXPERIENCE_RECORD`, and **auto-raises a
backfill Requisition** for the same rank/vessel. backfill Requisition** for the same rank/vessel.
### 3.1 Leave & the clash backfill
Leave is **applied by the Site in-charge on behalf of a crew member**
(`LeaveRequest(APPLIED)`) and **decided by the MPO** (`APPROVED` / `REJECTED`).
An approval moves the assignment to `ON_LEAVE` for the period. On approval the
state machine checks the rank's cover on that vessel/site over the leave window:
if the approved leaves would drop the rank **below its required strength**, it
**auto-raises a backfill `Requisition`** (reason `LEAVE`, `autoRaised`) — the same
mechanism as the sign-off backfill, surfaced on the planner where the clash is
highlighted. (Attendance, by contrast, is recorded on site and reviewed by the
**Manager**; the **MPO has no attendance access**.)
```mermaid
sequenceDiagram
actor SIC as Site in-charge
participant SYS as App / state machines
actor MPO
participant DB as PostgreSQL
participant N as Notifier
SIC->>SYS: apply leave for crew member (dates)
SYS->>DB: LeaveRequest (APPLIED)
MPO->>SYS: approve leave
SYS->>DB: LeaveRequest APPROVED; assignment ON_LEAVE
SYS->>SYS: check rank cover over the leave window
alt clash — rank below required strength
SYS->>DB: create Requisition (reason=LEAVE, autoRaised, OPEN)
SYS->>N: notify MPO "vacancy (leave clash): <rank> on <vessel>"
N-->>MPO: email + bell
else covered
Note over SYS: no requisition needed
end
```
--- ---
## 4. Appraisal lifecycle ## 4. Appraisal lifecycle
@ -245,7 +280,9 @@ sequenceDiagram
``` ```
Site staff see contract letters **view-only except salary** and bank details Site staff see contract letters **view-only except salary** and bank details
**view-only**; salary and full account numbers are gated. **view-only**; salary and full account numbers are gated. **Attendance** is
entered on site and **reviewed by the Manager only — the MPO has no attendance
access**; it has no verification gate.
--- ---