Skip to content

Procurement

PR-to-payment, unblocked.

A defect that’s been “awaiting parts” for ninety days is almost always stuck somewhere in the procurement chain. The Procurement pipeline does two things: it tracks every active PO through the funnel, and it surfaces the structural problems — aged POs, supplier underperformance, budget overrun — that systematically slow it down.

aged POsforwarder backlogsupplier scorecardssafety-criticalbudget compliance

The funnel

PR raised ──▶ PO approved ──▶ Supplier confirms ──▶
Goods ready ──▶ Forwarder picks up ──▶ Vessel receives ──▶
Invoice cleared

Each transition has a stage threshold:

StageThresholdWhat “stuck” means
PR → PO approval5 daysAuthority-limit or budget-code issue
PO → supplier confirmation3 daysSupplier didn’t acknowledge — chase needed
Supplier → goods readyper quoted lead-timeProduction / stock issue
Goods → forwarder pickup14 daysForwarder consolidation gap
Forwarder → vesselper route ETAIn transit
Vessel receipt → invoice clearance7 daysDocumentation / accounting

Time-in-stage for every active PO:

T_stage(PO) = t_exit − t_enter

A PO sitting beyond the stage threshold is flagged. The aggregate of stuck POs across stages is the funnel-health view.

Three views, three questions

Forwarders backlog — POs with forwarders that haven’t reached the vessel yet. Sorted by ETA; safety-critical items surface at the top regardless of ETA.

Open POs older than 180 days — the structural-problem view. A PO older than 180 days is rarely just “still in transit”. It’s almost always: cancelled but not closed, supplier abandoned, wrong vessel, partial-delivery dispute, or simply forgotten. Most items can be closed with a single phone call.

Purchase log — end-to-end log from PR raise to invoice clearance. The source of truth for a specific PO’s history; the other two views aggregate from it.

Worked example — MV POSUN

Weekly procurement review:

ViewCountNotable
Forwarders backlog182 safety-critical (one delayed 22 days vs ETA)
Open > 180 days116 likely “cancelled but not closed”, 3 supplier abandoned, 2 partial-delivery disputes
Aged at approval stage4Awaiting authority approval > 7 days
Budget varianceTechnical +18%, Stores +6%Technical category over threshold
Supplier scorecards (failing)2Vendor X 62% on-time, Vendor Y 71% on-time

Verdict: HIGH — technical-budget overrun and the safety-critical lifeboat-winch delay. The pipeline:

  1. Flags the lifeboat-winch motor as immediate action — supplier and forwarder need a chase today.
  2. Generates the 11-PO close-out list with predicted disposition for each.
  3. Routes the budget overrun to the financial pipeline for variance attribution and year-end forecast.
  4. Flags Vendor X for renegotiation review.

Under the hood

Safety-critical pipeline

Items tagged safety-critical get a separate timeline. Any delay against ETA is treated as critical regardless of magnitude:

ItemVesselSupplierStageDays in stageETA
Lifeboat winch motorPOSUNSchat-HardingForwarder222026-05-12
Fire-pump impellerOCEANWärtsiläGoods ready3TBD
EEBD canister x4NEXUSDragerVessel receipt1delivered, awaiting receipt

The list is short by design — most safety-critical items move quickly. Anything sitting on this list more than a few days is escalation-worthy.

Supplier scorecards

Per supplier, the pipeline tracks:

On-time % = N_on_time / N_total × 100
Mean lead time = (1/N) × Σ (t_delivered_i − t_ordered_i)

Plus quality issues per shipment and recent escalations. A scorecard below 70% on-time triggers a renegotiation flag.

The same supplier delivering late on five vessels is a fleet-wide problem, not a per-vessel one. Supplier metrics are aggregated across the fleet.

Budget compliance

Per category (technical / stores / lube / victualling / repairs / etc.):

Variance % = (Actual − Budget) / Budget × 100

Variance over 10% is flagged; over 25% triggers escalation. Cross-references the financial pipeline for the year-end forecast view.

180-day filter — code snapshot
# Filter POs older than 180 days that are still open
threshold_date = datetime.utcnow() - timedelta(days=180)
aged_pos = po_collection.aggregate([
{"$match": {
"imo": {"$in": active_imos},
"status": {"$nin": ["CLOSED", "CANCELLED", "INVOICED"]},
"prRaisedDate": {"$lte": threshold_date}
}},
{"$addFields": {
"ageDays": {
"$divide": [
{"$subtract": [datetime.utcnow(), "$prRaisedDate"]},
1000 * 60 * 60 * 24
]
}
}},
{"$sort": {"ageDays": -1}},
])

Below 90 days the noise is too high (legitimately in-transit POs are common). Below 180 days the structural problems are isolated. Above 180 days, the structural problems dominate.

Closure-rate trend
Closure rate = N_POs_closed_in_period / (N_open_at_start + N_opened_in_period)

A falling closure rate combined with rising aged-PO count is the structural drift indicator — the same logic used in the defects pipeline.

Escalation triggers
TriggerSeverity
Safety-critical item delayed against ETACRITICAL
Stuck delivery more than 30 days with no updateCRITICAL
Budget overrun above 25% in any categoryHIGH
Supplier on-time below 70% with active POsHIGH
PO awaiting approval more than 14 daysHIGH
Aged-PO count rising for 2+ consecutive periodsHIGH