Перейти до змісту

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)

Per phase2-carrier-apis § 1:

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

  1. Maersk customer agreement timeline для ZAMMLER — production traffic blocker. Action: Phase 4 — outreach до Maersk UA account team.
  2. DCSA OpenAPI Python codegen tooling choicedatamodel-code-generator vs openapi-python-client vs custom. Action: Phase 4 prototype, document choice в adapter README.
  3. Maersk rate limits — exact numbers — не публічно. Action: Phase 5 — empirically test (burst 100 RPS until 429), document.
  4. eBL platform pricing transparency — WaveBL opaque, Enigio "pay-once" but no public tier prices. Action: Phase 8 sales call — отримати quotes.
  5. MSC eBL platform preference — підтвердити що MSC mandates WaveBL exclusively, чи приймає інші DCSA-compliant hubs. Action: Phase 8 — direct outreach.
  6. 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).
  7. Multi-port routing (Constanța feeder) — як представляти у одній OceanBooking. Action: Phase 5 — design workshop з ZAMMLER pilot.
  8. House vs Master B/L консолідація workflow — детальний UX для NVOCC scenarios. Action: Phase 5 — UX design sessions.
  9. Reefer monitoring depth — IoT integration scope (temperature sensors, alarms via Geotab/Samsara). Action: Phase 8 — pilot client demand-driven.

§ 9. Linkbacks


Phase 3 implementation-ready spec — 2026-05-12.