ShipCore-Sea — Ocean Freight¶
NEW vertical у Phase 5 (~4-6 тижнів). Будується поверх ContainerHub container model (через thin core) + Forwarder Booking workflow. Sea — окрема regulatory framework (EMSWe, не eFTI) + DCSA для B/L.
§ 1. Module Mission¶
Mission. ShipCore-Sea — це DCSA-native ocean freight backend для UA mid-market forwarder. Покриває vessel schedules → OceanBooking → BillOfLading (paper + eBL через interop hubs) → tracking → demurrage/detention. Кожен CarrierAdapter дотримується DCSA OpenAPI specs (Booking 2.0, TNT 2.2, BL 3.0, VGM 1.0), що дає 70%+ reusable code across carriers.
Target клієнт. - UA forwarder з ocean shipments (зернові, контейнери) — primary через ZAMMLER tier Wave 1 (C8). - EU forwarder з UA-маршрутами (Black Sea Corridor) — secondary, через Constanța feeder + Дунайські порти. - Дунайський термінал operator — bridge користувач: shipcore_sea VesselSchedule пов'язаний з shipcore_terminal yard ops.
Wedge у sea vertical.
1. DCSA-first з Phase 5 — VGM REST (не EDIFACT VERMAS), Booking 2.0, BL 3.0. Один з небагатьох UA-friendly TMS що це робить нативно (C5).
2. Maersk Spot perform (D9) — найбільший deep-sea учасник UA-маршрутів (Constanța feeder + Дунай), OAuth2 + sandbox без customer contract.
3. eBL interop hubs — CargoX + WaveBL primary з Phase 8 (P2-N4). Конкурентоспроможно з Riege/CargoWise.
4. Black Sea Corridor specifics — Constanța feeder workflow, Unity insurance, "In Transit to Ukraine" notation. UA-EU forwarder получає це out-of-box, не custom development.
5. Demurrage/Detention timers — інтеграція з shipcore_terminal.DemurrageCalculation, real-time alerts.
§ 2. Поточна реалізація (статус quo)¶
2.1 Що НЕ існує¶
ShipCore-Sea не існує як окремий backend app. Sea-related функціонал на 2026-05-12 розпорошений:
2.2 Що частково є у ContainerHub (можна leverage)¶
Master Data:
- Container (ISO 6346, з condition, current_location, owner_type) — буде lifted у thin core backend/shipcore/
- ContainerType (20DC, 40DC, 40HC, 45HC, OT, FR, RF)
- ShippingLine (з SCAC code) — буде refactored у shipcore.Carrier
- Terminal, YardZone (контейнерний термінал — лишається у shipcore_terminal)
Transactions (terminal-side, потребують sea-side counterparts):
- ContainerBooking — реrminal-perspective бронювання; ShipCore-Sea додає forwarder-perspective OceanBooking з vessel/voyage details
Maturity: ContainerHub 🟢 high (24 моделі, 35+ endpoints, drone CV), але це термінальна перспектива. ShipCore-Sea дає forwarder перспективу — той самий контейнер, інший workflow.
2.3 Recon з Phase 0/1/2¶
- D6: нова Django app
backend/shipcore_sea/, depends on thin core. - D9: Maersk Spot perform у Phase 5.
- D11: hybrid CarrierAdapter (Maersk direct + Vizion fallback).
- C5: DCSA VGM REST.
- C6: eBL hubs Phase 8.
- P2-N4: eBL hubs scope = CargoX + WaveBL primary, Enigio/TradeGo/IQAX — Phase 9+.
- Важливо: sea НЕ через eFTI (eFTI = road/rail/IWT/air). Sea regulated через EMSWe (Reg EU 2019/1239) + DCSA eBL. Часта плутанина у документації.
§ 3. Цільовий стан v1 (Phase 5) + v1.5 (Phase 8)¶
3.1 Нові моделі¶
Master Data — у thin core (backend/shipcore/models/):
# backend/shipcore/models/vessel.py
class Vessel(TenantScopedModel):
"""Судно. Може бути shared master data (cross-tenant) per UA Vessel Registry."""
name = CharField(max_length=120)
imo = CharField(max_length=7, unique=True, db_index=True)
# IMO number — 7-digit, globally unique
mmsi = CharField(max_length=9, blank=True, db_index=True)
# Maritime Mobile Service Identity, 9-digit
flag = CharField(max_length=2) # ISO 3166-1 alpha-2
capacity_teu = IntegerField()
vessel_type = CharField(choices=[
('container', 'Container'),
('bulk', 'Bulk carrier'),
('tanker', 'Tanker'),
('general', 'General cargo'),
('roro', 'Ro-Ro'),
('barge', 'River barge'),
])
owner_carrier = FK('shipcore.Carrier', blank=True, null=True)
year_built = IntegerField(blank=True, null=True)
# backend/shipcore/models/port.py
class Port(TenantScopedModel):
"""Порт. Майже завжди seed з UN/Locode dataset."""
unlocode = CharField(max_length=5, unique=True, db_index=True)
# UN/Locode: 2-letter country + 3-letter location
name = CharField(max_length=120)
country = CharField(max_length=2) # ISO 3166-1 alpha-2
port_type = CharField(choices=[
('sea', 'Sea port'),
('river', 'River port'),
('dry', 'Dry port'),
], default='sea')
latitude = DecimalField(max_digits=10, decimal_places=7, blank=True, null=True)
longitude = DecimalField(max_digits=10, decimal_places=7, blank=True, null=True)
time_zone = CharField(max_length=40)
corridors = ManyToMany('MarketCorridor', blank=True)
Transactions — у backend/shipcore_sea/:
# backend/shipcore_sea/models/voyage.py
class Voyage(TenantScopedModel):
"""Рейс судна = vessel × voyage_no × dates."""
vessel = FK('shipcore.Vessel')
voyage_number = CharField(max_length=15)
# Carrier-assigned: '447W', '2025E03', ...
carrier = FK('shipcore.Carrier')
service_code = CharField(max_length=20, blank=True)
# Carrier service line: 'AE7', 'AME2', ...
departure_port = FK('shipcore.Port', related_name='departing_voyages')
arrival_port = FK('shipcore.Port', related_name='arriving_voyages')
planned_departure = DateTimeField()
planned_arrival = DateTimeField()
actual_departure = DateTimeField(blank=True, null=True)
actual_arrival = DateTimeField(blank=True, null=True)
cutoff_doc = DateTimeField(blank=True, null=True)
# Documentation cutoff
cutoff_si = DateTimeField(blank=True, null=True)
# Shipping instruction cutoff
cutoff_vgm = DateTimeField(blank=True, null=True)
# VGM cutoff
cutoff_gate = DateTimeField(blank=True, null=True)
# Physical gate-in cutoff
status = CharField(choices=[
('scheduled', 'Scheduled'),
('departed', 'Departed'),
('arrived', 'Arrived'),
('cancelled', 'Cancelled'),
], default='scheduled')
class Meta:
unique_together = [('vessel', 'voyage_number', 'planned_departure')]
indexes = [
Index(fields=['tenant', 'departure_port', 'planned_departure']),
Index(fields=['tenant', 'arrival_port', 'planned_arrival']),
]
# backend/shipcore_sea/models/vessel_schedule.py
class VesselSchedule(TenantScopedModel):
"""Розклад заходу в порти — окремі port calls по voyages.
Часто pulled з Carrier Commercial Schedules API."""
vessel = FK('shipcore.Vessel')
port = FK('shipcore.Port')
voyage = FK(Voyage, blank=True, null=True)
eta = DateTimeField()
etd = DateTimeField()
ata = DateTimeField(blank=True, null=True) # actual arrival
atd = DateTimeField(blank=True, null=True) # actual departure
last_pulled_at = DateTimeField(auto_now=True)
pulled_from_source = CharField(max_length=40, default='MAERSK_CS')
# MAERSK_CS, HAPAG_CS, CMA_CS, MSC_CS, MANUAL
# backend/shipcore_sea/models/ocean_booking.py
class OceanBooking(TenantScopedModel):
"""Sea-specific extension до master Booking. 1:1 з Booking.
Дані частково з Maersk DCSA Booking 2.0 response."""
booking = OneToOne('shipcore_forwarder.Booking', related_name='ocean_details')
voyage = FK(Voyage)
carrier = FK('shipcore.Carrier')
fcl_or_lcl = CharField(choices=[('fcl', 'FCL'), ('lcl', 'LCL')], default='fcl')
container_count = IntegerField(default=0)
port_of_loading = FK('shipcore.Port', related_name='+')
port_of_discharge = FK('shipcore.Port', related_name='+')
place_of_receipt = FK('shipcore.LocationPoint', related_name='+', blank=True, null=True)
place_of_delivery = FK('shipcore.LocationPoint', related_name='+', blank=True, null=True)
# Carrier reference:
carrier_booking_request_ref = CharField(max_length=80, blank=True)
# First response від DCSA Booking POST (PENDING_UPDATE_CONFIRMATION)
carrier_booking_ref = CharField(max_length=80, blank=True)
# Final confirmed (e.g., 'MAEU2025XXXX')
# Cutoffs (cached з Voyage):
cutoff_doc = DateTimeField(blank=True, null=True)
cutoff_si = DateTimeField(blank=True, null=True)
cutoff_vgm = DateTimeField(blank=True, null=True)
cutoff_gate = DateTimeField(blank=True, null=True)
status = CharField(choices=[
('draft', 'Draft'),
('pending', 'Pending carrier confirmation'),
('confirmed', 'Confirmed'),
('loaded', 'Loaded on vessel'),
('discharged', 'Discharged at PoD'),
('cancelled', 'Cancelled'),
], default='draft')
# backend/shipcore_sea/models/ocean_booking_equipment.py
class OceanBookingEquipment(TenantScopedModel):
"""Container assignment до OceanBooking."""
ocean_booking = FK(OceanBooking, related_name='equipment')
container_type = FK('shipcore.ContainerType')
container = FK('shipcore.Container', blank=True, null=True)
# Може бути null до empty-release
equipment_reference = CharField(max_length=12, blank=True)
# ISO 6346 container number after release
is_shipper_owned = BooleanField(default=False)
seal_number = CharField(max_length=20, blank=True)
is_reefer = BooleanField(default=False)
reefer_temperature_c = DecimalField(max_digits=5, decimal_places=2, blank=True, null=True)
# VGM (per DCSA VGM 1.0):
verified_gross_mass_kg = DecimalField(max_digits=10, decimal_places=2, blank=True, null=True)
vgm_method = CharField(choices=[
('weighing', 'Method 1 — weighing'),
('calculation', 'Method 2 — calculation'),
], blank=True)
vgm_submitted_at = DateTimeField(blank=True, null=True)
vgm_carrier_ref = CharField(max_length=40, blank=True)
# backend/shipcore_sea/models/bill_of_lading.py
class BillOfLading(TenantScopedModel):
"""Морський коносамент. Paper paths + eBL via DCSA BL 3.0."""
ocean_booking = FK(OceanBooking, related_name='bills')
bl_type = CharField(choices=[
('master', 'Master B/L'),
('house', 'House B/L'),
], default='master')
parent_master = FK('self', blank=True, null=True, related_name='houses')
# Set для House BL що пов'язана з Master
bl_number = CharField(max_length=40, unique=True)
# Carrier-assigned (e.g., 'MAEU0987654')
document_subtype = CharField(choices=[
('original', 'Original'),
('express_release', 'Express Release'),
('sea_waybill', 'Sea Waybill'),
], default='original')
issuance_type = CharField(choices=[
('paper', 'Paper'),
('ebl', 'Electronic (eBL)'),
], default='paper')
document_status = CharField(choices=[
('draft', 'Draft'),
('issued', 'Issued'),
('surrendered', 'Surrendered'),
('released', 'Released'),
], default='draft')
# eBL specifics (Phase 8+):
ebl_platform = CharField(choices=[
('cargox', 'CargoX'),
('wavebl', 'WaveBL'),
('enigio', 'Enigio'),
('iqax', 'IQAX/GSBN'),
], blank=True)
ebl_handle = CharField(max_length=120, blank=True)
# Platform-side identifier (token, hash, ...)
# DCSA BL 3.0 payload (JSONField for flexibility — schema evolves):
dcsa_payload = JSONField(default=dict)
issued_at = DateTimeField(blank=True, null=True)
issued_by_user = FK('auth.User', blank=True, null=True)
# backend/shipcore_sea/models/booking_confirmation.py
class BookingConfirmation(TenantScopedModel):
"""Carrier-side confirmation events для audit.
Кожна зміна status з боку carrier creates new record."""
ocean_booking = FK(OceanBooking, related_name='confirmations')
status = CharField(max_length=40)
confirmed_at = DateTimeField()
carrier_payload = JSONField()
source_adapter = CharField(max_length=20)
3.2 Services¶
# backend/shipcore_sea/services/booking_flow.py
async def create_ocean_booking(
booking: Booking,
voyage: Voyage,
equipment: list[ContainerSpec],
parties: BookingParties,
) -> OceanBooking:
"""
1. Створює OceanBooking + OceanBookingEquipment.
2. Resolves CarrierAdapter за voyage.carrier.scac_code.
3. adapter.create_booking(BookingRequest) → BookingConfirmation (DCSA).
4. Зберігає carrier_booking_request_ref.
5. Emits ShipmentEvent(event_code='CFD', classifier='PLN').
6. Schedules webhook listener для confirmation.
"""
async def issue_vgm(equipment: OceanBookingEquipment, weight_kg: Decimal, method: str) -> str:
"""
Per DCSA VGM 1.0 REST API.
1. Validates weight ≥ tare + minimum threshold.
2. Builds VGMSubmission payload.
3. adapter.submit_vgm() → carrier ref.
4. Updates equipment.verified_gross_mass_kg + vgm_carrier_ref.
5. Emits ShipmentEvent(event_code='VGM').
"""
# backend/shipcore_sea/services/bl_workflow.py
async def issue_bill_of_lading(
ocean_booking: OceanBooking,
bl_type: str = 'master',
issuance_type: str = 'paper',
ebl_platform: str | None = None,
) -> BillOfLading:
"""
Per DCSA BL 3.0.
Paper path:
1. adapter.issue_bill_of_lading() → carrier BL number + PDF.
2. document_status='issued'.
eBL path (Phase 8):
1. Build DCSA eBL TransportDocument payload.
2. ebl_adapter.issue(payload, carrier=carrier) → EblHandle.
3. document_status='issued', ebl_handle stored.
"""
async def transfer_ebl(bl: BillOfLading, to_party: Client) -> None:
"""Phase 8. eBL platform transfer (CargoX/WaveBL adapter)."""
# backend/shipcore_sea/services/vessel_schedule_sync.py
async def sync_carrier_schedules(carrier: Carrier, port_pairs: list[tuple[Port, Port]]) -> int:
"""
Pull DCSA Commercial Schedules або carrier-specific Schedules API.
Updates VesselSchedule records. Returns count of updates.
Scheduled via Celery beat (daily 02:00 UTC).
"""
# backend/shipcore_sea/services/demurrage_alerts.py
def check_demurrage_thresholds() -> list[DemurrageAlert]:
"""
Periodic job. Перевіряє active containers near demurrage/detention deadline.
Інтеграція з shipcore_terminal.DemurrageCalculation.
Emits DemurrageAlert WebSocket message + email notification.
"""
3.3 ViewSets / API endpoints¶
| URL | Method | Operation |
|---|---|---|
/api/sea/vessels/ |
CRUD | Vessel master data |
/api/sea/vessels/{id}/schedule/ |
GET | VesselSchedule timeline |
/api/sea/voyages/ |
CRUD | Voyage |
/api/sea/voyages/sync/ |
POST | Manual trigger schedule sync |
/api/sea/ports/ |
CRUD | Port master data |
/api/sea/ports/seed-unlocode/ |
POST | Bulk import з UN/Locode CSV |
/api/sea/ocean-bookings/ |
CRUD | OceanBooking |
/api/sea/ocean-bookings/{id}/issue-vgm/ |
POST | Submit VGM via DCSA VGM 1.0 |
/api/sea/ocean-bookings/{id}/issue-bl/ |
POST | Issue B/L (paper or eBL) |
/api/sea/bills-of-lading/ |
CRUD | BillOfLading |
/api/sea/bills-of-lading/{id}/transfer-ebl/ |
POST | eBL transfer (Phase 8) |
/api/sea/bills-of-lading/{id}/surrender-ebl/ |
POST | eBL surrender (Phase 8) |
/api/sea/demurrage/active/ |
GET | Active demurrage timers |
/api/sea/demurrage/alerts/ |
GET | Approaching deadlines |
3.4 Frontend компоненти¶
Phase 5:
- frontend/erp/src/components/ShipCore/Sea/ — нова root
- VesselScheduleBoard.tsx — Gantt/Timeline view розкладів суден (port × vessel × time)
- OceanBookingForm.tsx — sea-specific booking (vessel/voyage selection, cutoffs, container assign)
- OceanBookingDashboard.tsx — Kanban (draft / pending / confirmed / loaded / discharged)
- VGMEditor.tsx — VGM submission UI per container
- BLEditor.tsx — B/L editor (3-stage workflow: draft → issued → released/surrendered)
- BLPrintPreview.tsx — B/L print template (DCSA-compatible structure)
- DemurrageDashboard.tsx — все active demurrage таймери з alerts
- OceanTrackingMap.tsx — vessel + container на карті (integration з Vizion/MarineTraffic AIS)
Phase 8 (eBL):
- EblPlatformSelector.tsx — Carrier × Platform routing config
- EblTransferModal.tsx — eBL transfer UX
- EblHistoryDrawer.tsx — audit trail для eBL operations
3.5 Integrations¶
| Integration | Status | Phase |
|---|---|---|
| Maersk Spot direct (DCSA Booking 2.0 + TNT 2.2 + BL 3.0 + VGM 1.0) | 📋 NEW | 5 |
| Vizion aggregator fallback | 📋 NEW | 5 |
| ContainerHub DemurrageCalculation | ✅ existing | 5 internal bridge |
| Hapag-Lloyd direct | 📋 NEW | 8 |
| CMA CGM direct (two-tier) | 📋 NEW | 8 |
| MSC direct | 📋 NEW | 8.5+ |
| CargoX eBL | 📋 NEW | 8 |
| WaveBL eBL | 📋 NEW | 8 |
| Enigio eBL | 📋 NEW | 9+ |
| IQAX/GSBN eBL | 📋 NEW | 9+ |
| MarineTraffic AIS | 📋 NEW | 8+ |
| EMSWe submission (NL/PL first) | 📋 NEW | 9+ |
Деталі — phase2-carrier-apis, phase2-eu-compliance § Track 5.
§ 4. Phase plan¶
Phase 4 — thin core foundation¶
Scope:
- [ ] Vessel, Port, Voyage models у backend/shipcore/ (thin core)
- [ ] Seed Port з UN/Locode dataset (UA + RO + PL + DE + TR + EG + IL primary)
- [ ] CarrierAdapter ABC + pydantic models з DCSA OpenAPI YAML
- [ ] BookingConfirmation model
Estimate: ~3-5 днів у складі Phase 4 загального обсягу
Phase 5 — Sea-Maersk-eBL-paper¶
Scope:
- [ ] backend/shipcore_sea/ Django app skeleton
- [ ] OceanBooking + OceanBookingEquipment + BillOfLading models
- [ ] MaerskSpotAdapter повний implementation (per phase2-carrier-apis § 1)
- OAuth2 token caching
- DCSA Booking 2.0
- DCSA TNT 2.2 (polling + webhook subscription)
- DCSA VGM 1.0
- DCSA BL 3.0
- [ ] VizionAdapter (aggregator fallback)
- [ ] DCSA pydantic models generation tooling — pick datamodel-code-generator (Python) vs openapi-python-client per spike
- [ ] vessel_schedule_sync Celery beat job
- [ ] demurrage_alerts integration з shipcore_terminal.DemurrageCalculation
- [ ] B/L PDF print template (paper)
- [ ] Frontend: VesselScheduleBoard + OceanBookingForm + OceanBookingDashboard + VGMEditor + BLEditor + BLPrintPreview + DemurrageDashboard + OceanTrackingMap
- [ ] Tests: ≥30 (booking lifecycle, VGM submission, BL issuance, webhook handling)
- [ ] Pilot ZAMMLER: real Maersk customer agreement → production traffic перевалідація
Dependencies: - Phase 4 done - Maersk Spot sandbox + production credentials (registration + customer agreement) - Vizion API key - ZAMMLER pilot agreement signed
Deliverables: - ✅ Working Maersk integration end-to-end (sandbox to production) - ✅ Sea Booking документ + B/L paper print - ✅ VGM submission per DCSA VGM 1.0 - ✅ Demurrage timers wired - ✅ At least 1 production booking via Maersk Spot
Estimate: ~5-6 тижнів (з ~5-7 тижневого Phase 5 sea-side estimate, phase2-carrier-apis § 8.2)
Phase 8 — eBL + EU carriers expansion (v1.5)¶
Scope:
- [ ] Hapag-Lloyd direct adapter (~50-70h) — found via cleanest dev portal
- [ ] CMA CGM direct adapter (~60-80h) — two-tier (public free 30-day → private OAuth2)
- [ ] eBL hubs primary: CargoX adapter + WaveBL adapter (per P2-N4)
- [ ] Adapter pattern: EblPlatformAdapter (Protocol) → CargoXAdapter, WaveBlAdapter, EnigioAdapter, IqaxAdapter (Phase 9+ for last two)
- [ ] Routing logic: Based on Carrier.preferred_ebl_platform FK
- [ ] DCSA PINT API support
- [ ] EMSWe submission lite — NL or PL pilot
Deliverables: - ✅ Hapag-Lloyd + CMA CGM production traffic - ✅ eBL issuance + transfer via CargoX OR WaveBL для primary 80% UA-EU deep-sea volume
Estimate: ~6-8 тижнів (з ~16-тижневого Phase 8 загального обсягу P2-R3)
Phase 8.5 — MSC + remaining hubs¶
- MSC adapter (~60-80h) — починаємо з Schedules + T&T, Booking 2.0 коли live
- Enigio + IQAX hubs
Phase 9+¶
- ONE / ZIM / COSCO / Evergreen / Yang Ming / HMM — через Vizion only (no direct adapters until volume justifies)
- MarineTraffic AIS direct integration
- EMSWe full multi-MS rollout
§ 5. Compliance integration¶
Sea не через eFTI! Sea regulated через EMSWe + DCSA. Часта плутанина.
5.1 Документи¶
| Документ | Стандарт | Phase | Trigger |
|---|---|---|---|
| Bill of Lading (paper) | Hague-Visby Rules / Hamburg Rules / Rotterdam Rules | 5 | OceanBooking.status='loaded' |
| Sea Waybill | Same | 5 | shipper-chosen non-negotiable variant |
| Express Release | Same | 5 | shipper instruction |
| eBL | DCSA BL 3.0 + PINT API | 8 | ebl_platform set on Carrier |
| EMSWe ship report | Reg (EU) 2019/1239 | 9+ | Per MS pilot |
| DCSA VGM submission | DCSA VGM v1.0 (Nov 2025) | 5 | Before vessel cutoff_vgm |
| ICS2 ENS Sea | Reg (EU) 2015/2447 (cross-border to EU) | 8 | Cross-border inbound to EU |
| eFTI sea | НЕМАЄ — sea не у eFTI | — | — |
5.2 Інтеграція з backend/shipcore/compliance/¶
backend/shipcore/compliance/
├── exporters/
│ └── emswe.py ← sea-specific projections
├── integrations/
│ ├── ebl_cargox.py ← Phase 8 primary
│ ├── ebl_wavebl.py ← Phase 8 primary
│ ├── ebl_enigio.py ← Phase 9+
│ ├── ebl_iqax.py ← Phase 9+
│ └── ics2_itsp.py ← ENS Sea (per P2-N2 — via ITSP partner)
5.3 eBL routing strategy¶
class EblPlatformAdapter(Protocol):
def issue(self, dcsa_ebl_payload: dict, carrier: Carrier) -> EblHandle: ...
def transfer(self, handle: EblHandle, to_party: Party) -> None: ...
def surrender(self, handle: EblHandle) -> None: ...
# Routing: based on Carrier.preferred_ebl_platform.
# Fallback to tenant-level default (tenant.settings.default_ebl_platform).
# Phase 8 v1.5: CargoX + WaveBL (covers ~80% UA-EU deep-sea volume з MSC+Maersk+Hapag-Lloyd).
Деталі — phase2-eu-compliance § Track 5.
§ 6. Standards mapping¶
| Стандарт | Куди в моделі |
|---|---|
| IMO number | Vessel.imo — 7-digit unique |
| MMSI | Vessel.mmsi — 9-digit |
| UN/Locode | Port.unlocode — 5-char |
| ISO 6346 | OceanBookingEquipment.equipment_reference, Container.container_number |
| ISO 668 | ContainerType.iso_code — 22G1, 42G1, 45G1, 22R1, ... |
| SCAC | Carrier.scac_code |
| DCSA Booking 2.0 | OceanBooking payload aligned, JSONField for flexibility |
| DCSA TNT v2.2 | ShipmentEvent schema-aligned |
| DCSA BL 3.0 | BillOfLading.dcsa_payload |
| DCSA VGM v1.0 | OceanBookingEquipment.verified_gross_mass_kg + vgm_method |
| SOLAS VGM | Tied to DCSA VGM (regulatory basis), per C5 |
| UNECE Recommendation N.24 | ShipmentEvent.event_code (sea-specific: CFD/ECR/CGI/CLO/VDP/TRN/VAP/CDS/CRL/POD) |
| HS Code (WCO) | Booking.hs_code ≥6 digits |
| EORI | Client.eori_code для EU customers |
§ 7. Integrations specifics¶
7.1 Maersk Spot (Phase 5 P0)¶
class MaerskSpotAdapter(BaseCarrierAdapter):
SCAC = "MAEU"
DCSA_VERSIONS = {"booking": "2.0", "tnt": "2.2", "bl": "3.0", "vgm": "1.0"}
BASE_URL_PROD = "https://api.maersk.com"
BASE_URL_SANDBOX = "https://api-sit.env.maersk.com"
# Methods: get_quote(), create_booking(), submit_vgm(),
# issue_bill_of_lading(), stream_events()
# Auth: OAuth2 client credentials + Consumer-Key header
# Token endpoint: /customer-identity/oauth/v2/access_token
Key endpoints:
- GET /offers/v3/offers — Quote pull
- POST /booking/v2/bookings — DCSA Booking 2.0
- POST /vgm/v1/verified-gross-mass — DCSA VGM 1.0
- POST /bills-of-lading/v3 — DCSA BL 3.0
- GET /track-and-trace/v2/events — DCSA TNT 2.2
Sample workflow (per phase2-carrier-apis § 1.7):
1. Quote pull → offerId, validUntil, totalPrice, transitDays, sailings[]
2. POST /booking/v2/bookings → carrierBookingRequestReference (PENDING)
3. Webhook → status=CONFIRMED, carrierBookingReference=MAEU2025XXXX
4. After gate-in: POST /vgm/v1/verified-gross-mass
5. POST /bills-of-lading/v3 → transportDocumentReference, status=ISSUED
6. GET /track-and-trace/v2/events?carrierBookingReference=...
7.2 Hapag-Lloyd Quick Quotes (Phase 8)¶
- Portal:
api-portal.hlag.com - Auth: OAuth2 Authorization Code
- DCSA TNT v2.2 ✅ live
- Booking 2.0 + BL 3.0 — у процесі (founding member)
7.3 CMA CGM (Phase 8 — two-tier)¶
Per P2-N8: - Public tier: API key + 30-day free trial → container milestones only - Private tier: OAuth2 + booking party identification → rail/inland events
7.4 MSC (Phase 8.5+)¶
Per P2-N9: - DCSA Commercial Schedules ✅ live - Booking 2.0 + eBL 3.0 — scheduled, not live - Skip integration до 2026-Q3+; avoid EDI fallback
7.5 Vizion (Phase 5 aggregator)¶
Per phase2-carrier-apis § 5.1: - Pricing: ~$4-5/container, ~$200-500/month per 1000 containers - Coverage: 99% global ocean, 33+ shipping lines, 60+ standardized milestones - Use case: tracking-only — quote/booking/BL/VGM not supported (aggregator scope)
7.6 eBL platforms (Phase 8 — CargoX + WaveBL primary)¶
| Platform | Tech | UA-key carriers | DCSA API v3.0 | Pricing |
|---|---|---|---|---|
| CargoX | Blockchain (Ethereum) | MSC, Maersk, Hapag-Lloyd, ONE, ZIM, HMM | ✅ PINT | $1/unit; HMM free до 2026-12-31 |
| WaveBL | Blockchain (private) | MSC ★, ZIM, Hapag-Lloyd, ONE, PIL | Partial | Pay-as-you-go (opaque) |
Деталі — phase2-eu-compliance § 5.7.
7.7 Demurrage/Detention integration¶
Internal bridge до shipcore_terminal.DemurrageCalculation:
- Demurrage timer від
gate_outдоgate_in_loaded - Detention timer від
dischargeдоgate_in_empty - Auto-calculation при наближенні до deadline (configurable threshold 3/2/1 days)
- Alerts → WebSocket Channels broadcast + email notification
7.8 Vessel tracking — AIS vs carrier APIs¶
- Phase 5 primary: Maersk TNT 2.2 + Vizion AIS-rich data
- Phase 8+ optional: Direct AIS (MarineTraffic API ~$200-500/month або FleetMon)
- D11 rationale: Carrier APIs дають authoritative carrier-side events, AIS дає реальні vessel positions. Multi-source fusion = Vizion sweet spot.
§ 8. Open questions / Phase 3 spike-days¶
- Maersk customer agreement timeline для ZAMMLER — production traffic blocker. Action: Phase 4 — outreach до Maersk UA account team.
- DCSA OpenAPI Python codegen tooling choice —
datamodel-code-generatorvsopenapi-python-clientvs custom. Action: Phase 4 prototype, document choice в adapter README. - Maersk rate limits — exact numbers — не публічно. Action: Phase 5 — empirically test (burst 100 RPS until 429), document.
- eBL platform pricing transparency — WaveBL opaque, Enigio "pay-once" but no public tier prices. Action: Phase 8 sales call — отримати quotes.
- MSC eBL platform preference — підтвердити що MSC mandates WaveBL exclusively, чи приймає інші DCSA-compliant hubs. Action: Phase 8 — direct outreach.
- Vessel master data — pull з Maersk schedules vs UA Vessel Registry vs MarineTraffic? Action: Phase 5 spike day — porівняти 3 sources на UA-relevant vessels (Black Sea + Danube).
- Multi-port routing (Constanța feeder) — як представляти у одній OceanBooking. Action: Phase 5 — design workshop з ZAMMLER pilot.
- House vs Master B/L консолідація workflow — детальний UX для NVOCC scenarios. Action: Phase 5 — UX design sessions.
- Reefer monitoring depth — IoT integration scope (temperature sensors, alarms via Geotab/Samsara). Action: Phase 8 — pilot client demand-driven.
§ 9. Linkbacks¶
- README — програмний overview ShipCore
- decisions-2026-05-12 — Phase 0 + Phase 1 corrections
- phase2-summary — 16 Phase 2 corrections
- phase2-carrier-apis — Maersk/Hapag/CMA/MSC + DCSA versions + CarrierAdapter design
- phase2-eu-compliance § Track 5 — eBL hubs deep
- phase2-tracking-partnerships § 1 — Vizion/Terminal49/p44/FourKites
- standards-matrix § 1.4 — DCSA standards inventory
- integration-matrix § 1 — high-level integration list
- roadmap — timeline
- containerhub README — terminal-side perspective (pre-refactor)
- forwarder — paralleled Phase 5 module
- terminal — internal bridge через DemurrageCalculation
Phase 3 implementation-ready spec — 2026-05-12.