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

Модуль "ContainerHub" — Контейнерний Термінал

Django app name: containerhub DOP Section code: containerhub App Store code: appContainerHub Plugin key: container_hub Requires: appEssentials, appLogistic Тип: module (subscription)


1. Загальний опис

Модуль ContainerHub забезпечує повний цикл управління контейнерним терміналом (сухий порт, морський порт, залізничний хаб):

Функціональний блок Опис
Пообʼєктний облік контейнерів Кожен контейнер — окремий обʼєкт обліку з номером ISO 6346, типом, власником, станом, історією переміщень
Управління двором (Yard) Термінали → зони → позиції (row/bay/tier). 3D ізометрична карта двору зі штабелюванням
Stacking Optimization Алгоритми оптимального розміщення (first-to-leave on top), мінімізація перестановок, pre-marshalling
Gate Control Оформлення вʼїзду/виїзду контейнерів (gate-in / gate-out) з фіксацією стану, пломб, VGM
Інвентаризація Три режими: ручна (мобільний додаток), AI-дрон (Jetson + YOLO + OCR), комбінована. План/факт аналіз
Демередж / Детеншен Автоматичний розрахунок за тарифами shipping line. Alerts при наближенні до deadline
Контейнерна логістика Інтеграція з модулем Logistic: shipment legs, vessel bookings, multimodal transport
Мобільний додаток Розширення Driver App для yard-менеджера: сканування, gate-in/out, inventory walk

2. Залежності від існуючих модулів

┌──────────────────────────────────────────────────────────┐
│                    ContainerHub (новий)                       │
│  Container, ContainerHub, YardZone, GateTransaction,         │
│  ContainerMovement, YardInventory, DemurrageCalc         │
├──────────────────────────────────────────────────────────┤
│ uses ↓                                                   │
├──────────────┬───────────────┬───────────────────────────┤
│  Essentials  │    Fleet      │       Logistic            │
│  Client      │  Vehicle      │  ContainerType            │
│  Currency    │  Driver       │  ShippingLine (new here)  │
│  Organization│  LocationPoint│  Shipment + Legs          │
│  Contract    │  Route        │  VesselBooking            │
│              │               │  LogisticLocation         │
└──────────────┴───────────────┴───────────────────────────┘

Перевикористання: - fleet.Vehicle — шасі / тягач / платформа для перевезення контейнера - fleet.Driver — водій автомобіля на gate-in/gate-out - fleet.LocationPoint — геокоординати терміналу - essentials.Client — контрагенти: shipping lines, перевізники, клієнти - essentials.Currency — валюта тарифів і нарахувань - logistic.ContainerType — вже задизайнений (20DC, 40HC тощо) - logistic.Shipment + ShipmentLeg — мультимодальні відправки - logistic.VesselBooking — бронювання морського фрахту


3. Моделі — Довідники (Master Data)

3.1 Container (Контейнер) — пообʼєктний облік

Base: MasterDataModel

Поле Тип Опис
container_number CharField(11), unique per tenant Номер ISO 6346: ABCU1234567
container_type FK → logistic.ContainerType 20DC, 40DC, 40HC, 45HC, OT, FR, RF
owner_type Enum: own, leased, shipping_line, client Тип власності
owner FK → essentials.Client (nullable) Власник / орендодавець / лінія
shipping_line FK → ShippingLine (nullable) Лінія (для лінійних контейнерів)
manufacturing_year IntegerField (nullable) Рік виробництва
tare_weight DecimalField Вага тари (кг)
max_payload DecimalField Максимальна вантажопідйомність (кг)
is_reefer BooleanField Рефрижератор
condition Enum: new, good, damaged, needs_repair, scrapped Поточний стан
current_containerhub FK → ContainerHub (nullable) Де зараз знаходиться
current_zone FK → YardZone (nullable) В якій зоні
current_position CharField (nullable) Позиція: "A-05-03-2" (zone-row-bay-tier)
last_gate_in DateTimeField (nullable) Останній вʼїзд
last_inspection_date DateField (nullable) Дата останнього огляду
csc_plate_date DateField (nullable) CSC табличка — дата наступної перевірки
image ImageField (nullable) Фото контейнера

Subtables: - ContainerInspection (related) — історія оглядів/пошкоджень - ContainerSeal (related) — історія пломб

_subtables:

_subtables = [
    {'code': 'inspections', 'name': 'Inspection History', 'name_ua': 'Історія оглядів',
     'type': 'related', 'autoload': False},
    {'code': 'seals', 'name': 'Seal History', 'name_ua': 'Історія пломб',
     'type': 'related', 'autoload': False},
]


3.2 ContainerInspection (Огляд контейнера)

Base: TenantAwareModel

Поле Тип Опис
container FK → Container Контейнер
inspection_date DateTimeField Дата/час огляду
inspector FK → User (nullable) Хто оглядав
condition_before Enum Стан до
condition_after Enum Стан після
damage_type Enum: none, dent, hole, rust, floor, door, roof, wall Тип пошкодження
damage_location CharField Місце пошкодження (front, back, left, right, top, bottom)
damage_description TextField Опис
photo ImageField (nullable) Фото пошкодження
repair_required BooleanField Потребує ремонту
repair_cost_estimate DecimalField (nullable) Оцінка вартості ремонту

3.3 ContainerSeal (Пломба)

Base: TenantAwareModel

Поле Тип Опис
container FK → Container Контейнер
seal_number CharField Номер пломби
seal_type Enum: bolt, cable, padlock, electronic Тип пломби
action Enum: applied, removed, verified, broken Дія
action_date DateTimeField Дата/час
action_by FK → User (nullable) Хто виконав
gate_transaction FK → GateTransaction (nullable) При якій операції

3.4 ContainerHub (Термінал / Порт)

Base: MasterDataModel

Поле Тип Опис
containerhub_type Enum: sea_port, dry_port, rail_containerhub, inland_depot, warehouse Тип терміналу
location FK → fleet.LocationPoint Географія (lat/lon, адреса, місто)
capacity_teu IntegerField Максимальна місткість (TEU)
current_occupancy_teu IntegerField Поточне заповнення (авто-обчислюється)
contact_phone CharField (nullable) Телефон
contact_email EmailField (nullable) Email
working_hours CharField (nullable) Режим роботи

Subtables: - YardZone (inline) — зони двору

_subtables:

_subtables = [
    {'code': 'zones', 'name': 'Yard Zones', 'name_ua': 'Зони двору',
     'type': 'inline', 'autoload': True},
]


3.5 YardZone (Зона двору)

Base: TenantAwareModel

Поле Тип Опис
containerhub FK → ContainerHub Термінал
zone_code CharField Код зони: "A", "B1", "REEFER-1"
zone_type Enum: import, export, empty, reefer, dangerous, transshipment, inspection, repair Призначення
rows IntegerField Кількість рядів
bays IntegerField Кількість місць в ряду
max_tiers IntegerField Максимальна висота штабелювання
capacity_teu IntegerField Авто = rows × bays × max_tiers (для 20ft)
is_active BooleanField Активна
has_power BooleanField Є підключення електрики (для рефрижераторів)
has_rail_access BooleanField Є залізничний доступ

3.6 ShippingLine (Судноплавна лінія)

Base: MasterDataModel

Поле Тип Опис
client FK → essentials.Client Звʼязок з карткою клієнта
scac_code CharField(4) Standard Carrier Alpha Code (MAEU, HLCU, CMDU)
free_days_import IntegerField, default=14 Безкоштовних днів (імпорт)
free_days_export IntegerField, default=7 Безкоштовних днів (експорт)
detention_free_days IntegerField, default=7 Безкоштовних днів детеншен
demurrage_rate_20 DecimalField (nullable) Ставка демереджу 20ft/день
demurrage_rate_40 DecimalField (nullable) Ставка демереджу 40ft/день
detention_rate_20 DecimalField (nullable) Ставка детеншену 20ft/день
detention_rate_40 DecimalField (nullable) Ставка детеншену 40ft/день
currency FK → essentials.Currency Валюта ставок

Примітка: ShippingLine може бути частиною logistic модуля (спільна для Logistic і ContainerHub). Фінальне рішення — при імплементації.


3.7 ContainerTariff (Тарифи терміналу)

Base: MasterDataModel

Поле Тип Опис
containerhub FK → ContainerHub Термінал
tariff_type Enum: storage, handling_in, handling_out, reefer_monitoring, gate_fee, weighing, inspection Тип послуги
container_size Enum: 20, 40, 45 Розмір контейнера
loaded_status Enum: loaded, empty, any Навантажений/порожній
rate DecimalField Ставка
rate_unit Enum: per_day, per_operation, per_hour Одиниця
currency FK → essentials.Currency Валюта
effective_from DateField Діє з
effective_to DateField (nullable) Діє до
free_days IntegerField, default=0 Безкоштовних днів зберігання

4. Моделі — Документи (Transactions)

4.1 GateTransaction (Gate-In / Gate-Out)

Base: TransactionModel

Основний документ оформлення вʼїзду/виїзду контейнера на/з території терміналу.

Поле Тип Опис
transaction_type Enum: gate_in, gate_out Тип операції
container FK → Container Контейнер
containerhub FK → ContainerHub Термінал
transport_mode Enum: truck, rail, internal, crane Спосіб доставки
vehicle FK → fleet.Vehicle (nullable) Автомобіль
driver FK → fleet.Driver (nullable) Водій
vehicle_plate CharField (nullable) Номерний знак (якщо зовнішній)
driver_name CharField (nullable) ПІБ водія (якщо зовнішній)
booking FK → ContainerBooking (nullable) Посилання на бронювання слоту
shipping_line FK → ShippingLine (nullable) Лінія
bill_of_lading CharField (nullable) Номер коносаменту
seal_number CharField (nullable) Номер пломби
seal_intact BooleanField (nullable) Пломба ціла (при gate-in)
container_condition Enum: good, damaged, needs_inspection Стан контейнера
damage_notes TextField (nullable) Опис пошкоджень
is_loaded BooleanField Навантажений / порожній
cargo_description TextField (nullable) Опис вантажу
cargo_weight DecimalField (nullable) Вага вантажу
vgm_weight DecimalField (nullable) Verified Gross Mass
is_dangerous BooleanField, default=False Небезпечний вантаж
imo_class CharField (nullable) Клас IMO (якщо небезпечний)
un_number CharField (nullable) UN номер (якщо небезпечний)
yard_zone FK → YardZone (nullable) Зона розміщення (при gate-in)
yard_position CharField (nullable) Позиція: row-bay-tier
temperature_set DecimalField (nullable) Встановлена температура (рефрижератор)
gate_timestamp DateTimeField Фактичний час проїзду через gate

Subtables: - GatePhoto (related) — фото при gate-in/out

Бізнес-логіка при проведенні (post_document): - Gate-In: оновити Container.current_containerhub, current_zone, current_position, last_gate_in - Gate-Out: обнулити current_containerhub/zone/position; автоматичний розрахунок демереджу - Запис у ContainerLedger


4.2 ContainerMovement (Внутрішнє переміщення)

Base: TransactionModel

Поле Тип Опис
container FK → Container Контейнер
containerhub FK → ContainerHub Термінал
from_zone FK → YardZone Звідки
from_position CharField Позиція (row-bay-tier)
to_zone FK → YardZone Куди
to_position CharField Нова позиція
reason Enum: restack, consolidation, inspection, reefer_connect, loading_prep, reposition Причина
equipment_used Enum: rtg, reach_stacker, forklift, side_loader, manual Техніка
moved_at DateTimeField Фактичний час переміщення

Бізнес-логіка при проведенні: - Оновити Container.current_zone, current_position - Запис у ContainerLedger


4.3 ContainerBooking (Бронювання слоту)

Base: TransactionModel

Truck Appointment System — бронювання часового вікна для gate-in / gate-out.

Поле Тип Опис
booking_type Enum: gate_in, gate_out Тип
container FK → Container (nullable) Контейнер (якщо відомий)
container_number CharField (nullable) Номер (якщо ще не в системі)
container_type FK → logistic.ContainerType (nullable) Тип контейнера
containerhub FK → ContainerHub Термінал
scheduled_date DateField Планова дата
time_slot_from TimeField Вікно: початок
time_slot_to TimeField Вікно: кінець
transport_company FK → essentials.Client (nullable) Перевізник
vehicle_plate CharField (nullable) Номер авто
driver_name CharField (nullable) Водій
driver_phone CharField (nullable) Телефон водія
is_loaded BooleanField Навантажений
cargo_description TextField (nullable) Вантаж
is_dangerous BooleanField, default=False Небезпечний
shipping_line FK → ShippingLine (nullable) Лінія
bill_of_lading CharField (nullable) Коносамент
actual_arrival DateTimeField (nullable) Фактичний час прибуття
gate_transaction FK → GateTransaction (nullable) Створений gate-документ

4.4 YardInventory (Інвентаризація двору)

Base: TransactionModel

Поле Тип Опис
containerhub FK → ContainerHub Термінал
inventory_type Enum: full, zone, spot_check Повна / зонна / вибіркова
zone FK → YardZone (nullable) Зона (для зонної)
source Enum: manual, mobile_app, drone_ai, combined Джерело даних
started_at DateTimeField (nullable) Час початку
completed_at DateTimeField (nullable) Час завершення
expected_count IntegerField Очікувана кількість (з облікових даних)
actual_count IntegerField, default=0 Фактична кількість
confirmed_count IntegerField, default=0 Підтверджено
missing_count IntegerField, default=0 Відсутні
unexpected_count IntegerField, default=0 Не в обліку
misplaced_count IntegerField, default=0 Не на місці

Subtables: - InventoryLine (inline) — рядки інвентаризації - DroneFlightResult (related) — результати дрон-обльоту

_subtables:

_subtables = [
    {'code': 'lines', 'name': 'Inventory Lines', 'name_ua': 'Рядки інвентаризації',
     'type': 'inline', 'autoload': True},
    {'code': 'droneResults', 'name': 'Drone Flight Results', 'name_ua': 'Результати дрону',
     'type': 'related', 'autoload': False},
]


4.5 InventoryLine (Рядок інвентаризації)

Base: TenantAwareModel

Поле Тип Опис
inventory FK → YardInventory Документ
sequence IntegerField Порядковий номер
container FK → Container (nullable) Контейнер (з облікових даних)
container_number CharField Номер (сканований / розпізнаний)
expected_zone FK → YardZone (nullable) Очікувана зона
expected_position CharField (nullable) Очікувана позиція
actual_zone FK → YardZone (nullable) Фактична зона
actual_position CharField (nullable) Фактична позиція
line_status Enum: confirmed, missing, unexpected, misplaced Статус
detected_by Enum: manual, mobile_scan, drone_ai Хто виявив
ai_confidence DecimalField (nullable) Впевненість AI (0.0–1.0)
photo_url URLField (nullable) Фото / кадр з дрону
notes TextField (nullable) Коментар

4.6 DemurrageCalculation (Розрахунок демереджу / детеншену)

Base: TransactionModel

Поле Тип Опис
calculation_type Enum: demurrage, detention Тип нарахування
container FK → Container Контейнер
shipping_line FK → ShippingLine Лінія
gate_in FK → GateTransaction Gate-in документ
gate_out FK → GateTransaction (nullable) Gate-out (nullable = ще на терміналі)
period_from DateField Початок періоду
period_to DateField Кінець (або today якщо не виїхав)
total_days IntegerField Загальна кількість днів
free_days IntegerField Безкоштовних днів
chargeable_days IntegerField Платних днів
rate_per_day DecimalField Ставка за день
currency FK → essentials.Currency Валюта
total_amount DecimalField Загальна сума
client FK → essentials.Client (nullable) На кого нараховано
invoice FK → essentials.Invoice (nullable) Повʼязаний рахунок
notes TextField (nullable) Коментар

Бізнес-логіка:

total_days = (period_to - period_from).days
chargeable_days = max(0, total_days - free_days)
total_amount = chargeable_days × rate_per_day

Автоматичне створення при gate-out (якщо chargeable_days > 0). Підтримка перерахунку для контейнерів, що ще на терміналі.


5. Моделі — Регістри

5.1 ContainerLedger (Регістр руху контейнерів)

Base: RegisterModel

Поле Тип Опис
container FK → Container Контейнер
containerhub FK → ContainerHub Термінал
event_type Enum: gate_in, gate_out, movement, inventory_adjustment Тип події
zone FK → YardZone (nullable) Зона
position CharField (nullable) Позиція
is_loaded BooleanField Навантажений
reference_type CharField Тип документа-підстави
reference_id IntegerField ID документа-підстави

5.2 StorageChargeLedger (Регістр нарахувань за зберігання)

Base: RegisterModel

Поле Тип Опис
container FK → Container Контейнер
containerhub FK → ContainerHub Термінал
charge_type Enum: storage, handling, reefer, gate_fee, demurrage, detention Тип нарахування
days IntegerField (nullable) Кількість днів
rate DecimalField Ставка
amount DecimalField Сума
currency FK → essentials.Currency Валюта
client FK → essentials.Client (nullable) Контрагент

6. AI Drone — Інтеграція з Jetson + YOLO

6.1 Архітектура

┌─────────────────────────────────────────────────────┐
│  Дрон + NVIDIA Jetson                               │
│  ┌──────────┐  ┌──────────┐  ┌────────────────┐    │
│  │ Камера   │→ │ YOLO v8  │→ │ Container OCR  │    │
│  │ 4K/8K    │  │ Detection│  │ (ISO 6346)     │    │
│  └──────────┘  └──────────┘  └───────┬────────┘    │
│                                       │             │
│  ┌────────────────────────────────────┘             │
│  │ JSON: container_number, bbox, GPS, confidence    │
│  └──────────┬──────────────────────────────────┘    │
│             │ Batch upload (WiFi/4G)                │
└─────────────┼───────────────────────────────────────┘
┌─────────────────────────────────────────────────────┐
│  Backend: POST /api/v1/containerhub/drone/upload/       │
│                                                      │
│  1. Валідація payload                                │
│  2. Matching: container_number → Container record    │
│  3. Mapping: GPS coords → YardZone + position        │
│  4. Створення InventoryLine records                  │
│  5. Порівняння з обліковими даними (expected vs AI)  │
│  6. Генерація discrepancy report                     │
└─────────────────────────────────────────────────────┘

6.2 DroneFlightResult (Результат обльоту)

Base: TenantAwareModel

Поле Тип Опис
inventory FK → YardInventory Інвентаризація
flight_id CharField, unique ID обльоту
containerhub FK → ContainerHub Термінал
started_at DateTimeField Початок обльоту
ended_at DateTimeField (nullable) Кінець обльоту
total_detections IntegerField, default=0 Кількість детекцій
matched_count IntegerField, default=0 Знайдено в обліку
unmatched_count IntegerField, default=0 Не знайдено
avg_confidence DecimalField (nullable) Середня впевненість
flight_path_json JSONField (nullable) Маршрут обльоту (GeoJSON)
processing_status Enum: uploading, processing, completed, failed Статус обробки

6.3 DroneDetection (Окрема детекція)

Base: TenantAwareModel

Поле Тип Опис
flight_result FK → DroneFlightResult Обліт
container_number_raw CharField OCR-результат (як розпізнав)
container_number_normalized CharField (nullable) Нормалізований номер
container FK → Container (nullable) Знайдений запис (nullable = не знайдено)
confidence DecimalField Впевненість (0.0–1.0)
latitude DecimalField GPS широта
longitude DecimalField GPS довгота
estimated_zone FK → YardZone (nullable) Зона (обчислена з GPS)
estimated_position CharField (nullable) Позиція (обчислена)
image_url URLField (nullable) Кадр з камери
bbox_json JSONField (nullable) Bounding box [x, y, w, h]
match_status Enum: matched, new_container, not_found, low_confidence Результат матчингу

6.4 API Endpoint — Drone Upload

POST /api/v1/containerhub/drone/upload/

{
  "flight_id": "FLIGHT-2026-02-28-001",
  "containerhub_id": 1,
  "started_at": "2026-02-28T08:00:00+06:00",
  "ended_at": "2026-02-28T08:45:00+06:00",
  "detections": [
    {
      "container_number": "MAEU1234567",
      "confidence": 0.97,
      "latitude": 43.2375,
      "longitude": 76.9457,
      "image_url": "https://storage.example.com/flights/001/frame_0042.jpg",
      "bbox": [120, 340, 480, 210]
    },
    ...
  ]
}

Response:

{
  "flight_result_id": 42,
  "total_detections": 2487,
  "matched": 2451,
  "unmatched": 36,
  "avg_confidence": 0.94,
  "inventory_id": 15,
  "status": "processing"
}


7. Процеси (UI компоненти)

7.1 YardMap — 3D Ізометрична карта двору

Тип: ItemType.PROCESS, processCode: yardMap

Проблема: Контейнери стоять один на одному (до 5 тірів). 2D-карта не відображає висоту штабеля і не показує, який контейнер "заблокований" зверху.

Рішення — два режими візуалізації:

Режим 1: Plan View (вид зверху)

  • Grid-карта зон терміналу: row × bay
  • Кожна клітинка показує верхній контейнер + badge з кількістю тірів ("×3")
  • Клік на клітинку → Stack Detail Panel (бічна панель)

Режим 2: Isometric 3D View

  • Ізометрична проекція (CSS transforms або Canvas/WebGL)
  • Контейнери — кольорові паралелепіпеди, складені один на одного
  • Видно повну висоту штабеля
  • Hover → tooltip з номером контейнера та departure date
  • Zoom / pan / rotate контроли

Stack Detail Panel (бічна панель при кліку на bay)

Bay A-05-03          [×4 containers]
┌─────────────────────────────────┐
│ Tier 4 (top):  MAEU1234567    │  ← departure: Mar 2 (first!)
│ Tier 3:        HLCU8901234    │  ← departure: Mar 5
│ Tier 2:        CMDU5678901    │  ← departure: Mar 3 ⚠️ blocked!
│ Tier 1 (bot):  TCKU3455621    │  ← departure: Mar 10
└─────────────────────────────────┘
⚠️ CMDU5678901 departs Mar 3 but blocked by 2 containers above!
[Generate Rehandling Plan]

Колірне кодування:

  • Зелений — departure > 7 днів (не терміновий)
  • Жовтий — departure 3-7 днів
  • Оранжевий — departure 1-3 дні
  • Червоний — departure сьогодні / прострочений
  • Синій — порожній контейнер
  • Фіолетовий — reefer (з індикатором температури)
  • Штрихований — dangerous goods (IMO)
  • Сірий з іконкою — damaged / needs repair

Стекування-індикатори:

  • Зелена рамка навколо стека — стек "здоровий" (ранні departure зверху)
  • Червона рамка — стек "хворий" (потрібен restack: ранній departure заблокований)
  • Badge "⚠️ N" — кількість заблокованих контейнерів у стеку

Фільтри:

  • За зоною, типом контейнера, shipping line, днями на терміналі
  • "Show problems only" — показати лише стеки з блокованими контейнерами
  • За departure window (сьогодні, завтра, цей тиждень)

Дії:

  • Drag-and-drop контейнера між позиціями → створює ContainerMovement
  • Кнопка "Optimize Zone" → запуск алгоритму pre-marshalling
  • Кнопка "Generate Rehandling Plan" → для конкретного заблокованого контейнера

7.2 GateControl — Панель контролю воріт

Тип: ItemType.PROCESS, processCode: gateControl

  • Два стовпці: Gate-In (зліва) | Gate-Out (справа)
  • Зверху: черга бронювань на сьогодні (ContainerBooking зі статусом approved)
  • Кнопка "Швидке оформлення" → форма GateTransaction
  • Live-лічильники: вʼїхало / виїхало за сьогодні
  • Сканер: поле для введення/сканування номера контейнера

7.3 InventoryDashboard — Дашборд інвентаризації

Тип: ItemType.PROCESS, processCode: inventoryDashboard

  • Кнопка "Нова інвентаризація" → вибір типу (повна / зонна / spot-check)
  • Кнопка "Запустити дрон-обліт" → інтеграція з drone API
  • Активна інвентаризація: progress bar (очікувано vs перевірено)
  • Таблиця відхилень: missing / unexpected / misplaced
  • AI detection gallery: кадри з дрону з bounding boxes
  • Кнопка "Прийняти результати" → проведення інвентаризації

7.4 DemurrageTracker — Трекер демереджу

Тип: ItemType.PROCESS, processCode: demurrageTracker

  • Три зони:
  • В зоні ризику — free days закінчуються через 1-3 дні
  • Нараховується — chargeable days > 0, контейнер ще на терміналі
  • Завершено — gate-out виконано, фінальна сума зафіксована
  • Кольорове кодування за сумою нарахувань
  • Фільтр по shipping line, клієнту, періоду
  • Кнопка "Перерахувати" — масовий перерахунок для всіх активних
  • Export в Excel/CSV

8. Stacking Optimization Engine — Оптимізація штабелювання

8.1 Три задачі оптимізації

┌─────────────────────────────────────────────────────────────────────┐
│                    STACKING OPTIMIZATION                            │
│                                                                     │
│  1. PLACEMENT             2. RETRIEVAL           3. PRE-MARSHALLING │
│  (куди поставити?)        (як дістати?)          (переставити заздалегідь) │
│                                                                     │
│  Потяг прибуває з 80      Потрібен контейнер     Ніч / простій:     │
│  контейнерами. Куди       CMDU5678901, а він     переставити так,   │
│  поставити кожен, щоб     під 3 іншими.          щоб завтра все     │
│  ті що їдуть першими      Мінімум переміщень?    їхало без restacks │
│  були зверху?                                                       │
└─────────────────────────────────────────────────────────────────────┘

8.2 Задача 1 — Placement Optimization (Оптимальне розміщення)

Коли: Gate-in або розвантаження потяга/судна.

Вхідні дані: - Контейнер: тип, розмір, shipping line, planned departure date, is_dangerous, is_reefer - Стан двору: поточний стан всіх зон/позицій/стеків (з ContainerLedger) - Обмеження: max_tiers зони, reefer → тільки зони з електрикою, dangerous → окрема зона

Алгоритм — Greedy з Look-Ahead (практичний, <1 сек):

def find_optimal_slot(container, yard_state, zones):
    """
    Знайти найкращу позицію для нового контейнера.
    Правило: контейнер з РАНІШИМ departure НЕ МОЖЕ бути ПІД контейнером з ПІЗНІШИМ departure.
    """
    candidates = []

    for zone in zones:
        if not zone_accepts(zone, container):  # reefer, dangerous, size checks
            continue

        for row, bay in zone.available_slots():
            stack = yard_state.get_stack(zone, row, bay)

            if stack.height >= zone.max_tiers:
                continue  # стек повний

            # Scoring:
            score = 0

            # 1. Пріоритет однорідності: контейнери з тим же departure window
            if stack.height > 0:
                top_departure = stack.top().planned_departure
                if same_window(top_departure, container.planned_departure):
                    score += 100  # ідеально — той же тиждень
                elif container.planned_departure >= top_departure:
                    score += 50   # OK — новий їде пізніше (буде знизу логічно)
                else:
                    score -= 200  # ПОГАНО — новий їде раніше але буде зверху заблокований

            # 2. Група по shipping line (легше відвантажувати разом)
            if stack.height > 0 and stack.top().shipping_line == container.shipping_line:
                score += 30

            # 3. Менша висота стека — менше ризику
            score -= stack.height * 10

            # 4. Близькість до gate-out (для контейнерів що їдуть скоро)
            if container.days_to_departure < 3:
                score += zone.proximity_to_gate * 20

            candidates.append((zone, row, bay, score))

    candidates.sort(key=lambda x: -x[3])
    return candidates[0] if candidates else None

Результат: Для кожного контейнера gate-in отримуємо рекомендовану позицію. Оператор бачить на YardMap підсвічену клітинку "РЕКОМЕНДОВАНО" і може прийняти або обрати іншу.

8.3 Задача 2 — Retrieval (Rehandling Plan)

Коли: Потрібно дістати конкретний контейнер для gate-out, а він заблокований.

Вхідні дані: - Цільовий контейнер і його позиція (zone, row, bay, tier) - Усі контейнери зверху нього (blocking containers) - Вільні позиції в зоні/сусідніх зонах - Departure dates blocking контейнерів (враховуємо при переміщенні)

Алгоритм — Beam Search з мінімізацією:

def generate_rehandling_plan(target_container, yard_state):
    """
    Генерує послідовність переміщень для витягнення target_container
    з мінімальною кількістю операцій.

    Block Relocation Problem (BRP) — NP-hard, але для стеків висотою ≤5
    точне рішення знаходиться за <100 мс (beam search, depth ≤5).
    """
    stack = yard_state.get_stack_for(target_container)
    blocking = stack.containers_above(target_container)

    if not blocking:
        return []  # Контейнер зверху — просто забрати

    plan = []
    for blocker in reversed(blocking):  # зверху вниз
        # Знайти найкращу позицію для blocker (щоб НЕ створити нових блокувань)
        dest = find_optimal_slot(blocker, yard_state, prefer_low_stacks=True)
        plan.append({
            'step': len(plan) + 1,
            'action': 'relocate',
            'container': blocker.container_number,
            'from': f'{stack.zone.code}-{stack.row:02d}-{stack.bay:02d}-{blocker.tier}',
            'to': f'{dest.zone.code}-{dest.row:02d}-{dest.bay:02d}-{dest.tier}',
            'reason': f'Unblock {target_container.container_number}',
        })
        yard_state.apply_move(blocker, dest)  # оновити стан для наступного кроку

    plan.append({
        'step': len(plan) + 1,
        'action': 'retrieve',
        'container': target_container.container_number,
        'from': f'{stack.zone.code}-{stack.row:02d}-{stack.bay:02d}-{target_container.tier}',
        'to': 'GATE-OUT',
    })

    return plan

UI — Rehandling Plan Viewer:

🔧 Rehandling Plan for CMDU5678901 (Gate-Out)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Step 1: Move MAEU1234567  A-05-03-4 → A-05-07-2   (unblock)
Step 2: Move HLCU8901234  A-05-03-3 → A-05-07-3   (unblock)
Step 3: Retrieve CMDU5678901  A-05-03-2 → GATE-OUT ✅

Total moves: 3 (2 relocations + 1 retrieval)
Estimated time: ~12 min (4 min per move)
Equipment: Reach Stacker RS-01

[Execute Plan] [Modify] [Cancel]

Кнопка "Execute Plan" → автоматично створює N документів ContainerMovement + 1 GateTransaction.

8.4 Задача 3 — Pre-Marshalling (Нічна оптимізація)

Коли: Простій (ніч, вихідні). Завтра очікується багато gate-out — переставити контейнери заздалегідь.

Вхідні дані: - Усі контейнери на терміналі з planned_departure - Gate-out розклад на завтра (ContainerBooking зі статусом approved) - Поточний стан двору

Алгоритм — Simulated Annealing (офлайн, хвилини):

def pre_marshalling_plan(containerhub, target_date, yard_state):
    """
    Перебудувати стеки так, щоб контейнери з departure ≤ target_date
    були на верху своїх стеків. Мінімізуємо кількість переміщень.

    Результат: 60-80% зниження кількості restacks при завтрашніх gate-out.
    """
    # 1. Визначити "хворі" стеки (departure order порушений)
    sick_stacks = find_sick_stacks(yard_state, target_date)

    # 2. Для кожного хворого стека — цільовий стан (відсортований за departure)
    target_config = compute_target_configuration(sick_stacks)

    # 3. Simulated Annealing — знайти мінімальну послідовність переміщень
    #    current → target з урахуванням обмежень (max_tiers, зони, тощо)
    plan = simulated_annealing_solve(
        current=yard_state,
        target=target_config,
        max_iterations=10000,
        cooling_rate=0.995,
    )

    return plan  # List of ContainerMovement records

UI — Pre-Marshalling Dashboard (в InventoryDashboard або окремий process):

🌙 Pre-Marshalling Plan — Night Shift
ContainerHub: Almaty Dry Port
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

Tomorrow's gate-outs: 47 containers
Blocked containers: 12 (need restacking)
Estimated restacks without optimization: 31 moves
After pre-marshalling: 8 moves (74% reduction ✅)

Pre-marshalling moves needed: 18 moves (~72 min)
Net savings: 13 moves (~52 min of crane time tomorrow)

Plan:
 1. MAEU1234567  A-05-03-4 → A-05-07-2
 2. HLCU8901234  A-05-03-3 → A-05-08-1
 ... (18 moves total)

[Execute Plan] [Schedule for Night Shift] [Cancel]

8.5 Train Unloading Optimization (Специфіка ЖД)

Сценарій: Прибуває потяг з 80 контейнерами на 40 платформах. Їх потрібно розвантажити краном у двір. Порядок розвантаження фіксований (від голови до хвоста потяга або навпаки). Але позицію в дворі ми обираємо.

Додатковий документ:

TrainArrival (Прибуття потяга) — extends TransactionModel

Поле Тип Опис
containerhub FK → ContainerHub Термінал
train_number CharField Номер потяга
origin FK → fleet.LocationPoint Звідки
arrival_time DateTimeField Час прибуття
platform_count IntegerField Кількість платформ
container_count IntegerField Кількість контейнерів
unloading_order Enum: head_first, tail_first Порядок розвантаження

Subtables: - TrainManifest (inline) — список контейнерів на потязі

TrainManifest (Маніфест потяга)

Поле Тип Опис
train_arrival FK → TrainArrival Потяг
sequence IntegerField Порядок на потязі (від голови)
platform_number CharField Номер платформи
container FK → Container (nullable) Контейнер (якщо вже в системі)
container_number CharField Номер контейнера
container_type FK → logistic.ContainerType Тип
shipping_line FK → ShippingLine (nullable) Лінія
planned_departure DateField (nullable) Планова дата відправки з терміналу
consignee FK → essentials.Client (nullable) Одержувач
is_loaded BooleanField Навантажений
is_dangerous BooleanField Небезпечний
weight DecimalField (nullable) Вага
assigned_zone FK → YardZone (nullable) Рекомендована зона (алгоритмом)
assigned_position CharField (nullable) Рекомендована позиція (алгоритмом)

Алгоритм розвантаження потяга:

def optimize_train_unloading(train_arrival, yard_state):
    """
    Для кожного контейнера в маніфесті (в порядку розвантаження)
    визначити оптимальну позицію в дворі.

    Ключове правило: контейнери що їдуть РАНІШЕ — ставити ВИЩЕ в стеку.
    Оскільки порядок розвантаження фіксований (потяг), алгоритм працює
    жадібно в порядку розвантаження.
    """
    manifest = train_arrival.manifest.order_by('sequence')

    # Сортуємо контейнери за departure date (для grouping)
    departure_groups = group_by_departure_window(manifest)

    placement_plan = []
    for container_info in manifest:
        slot = find_optimal_slot(
            container=container_info,
            yard_state=yard_state,
            # Додатковий пріоритет: групувати контейнери з того ж потяга
            # з тим же departure window в сусідні позиції
            prefer_group=departure_groups[container_info.departure_window],
        )
        placement_plan.append({
            'sequence': container_info.sequence,
            'container': container_info.container_number,
            'platform': container_info.platform_number,
            'assigned_zone': slot.zone.code,
            'assigned_position': f'{slot.row:02d}-{slot.bay:02d}-{slot.tier}',
            'departure': container_info.planned_departure,
        })
        yard_state.apply_placement(container_info, slot)

    return placement_plan

UI — Train Unloading Planner:

🚂 Train Unloading Plan — Train #4521 from Khorgos
Containers: 80 | Platforms: 40 | Unload: Head First
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

 #  Platform  Container     Departure   → Zone  Position
 1  PL-001    MAEU1234567   Mar 2       → A     01-05-3
 2  PL-001    HLCU8901234   Mar 2       → A     01-05-4  (same stack!)
 3  PL-002    CMDU5678901   Mar 10      → B     03-02-1
 4  PL-002    TCKU3455621   Mar 5       → A     02-01-2
 ...

Stacking quality score: 94/100 ✅
Expected restacks at gate-out: 3 (of 80)
Estimated unloading time: 4h 20min

[Print Plan for Crane Operator] [Execute] [Modify]

8.6 Stacking KPIs (Метрики якості штабелювання)

KPI Формула Ціль
Rehandle Ratio total_moves / productive_moves < 1.2 (ідеал: 1.0)
Sick Stack % stacks_with_wrong_order / total_stacks × 100% < 10%
Average Stack Height total_tiers_used / occupied_bays < 3.0 (для max_tiers=5)
Placement Acceptance Rate accepted_suggestions / total_suggestions × 100% > 80%
Pre-Marshall Efficiency restacks_saved / moves_spent × 100% > 150%

8.7 Backend API — Stacking Optimization

POST /api/v1/containerhub/stacking/suggest-placement/
  Body: { container_id, containerhub_id }
  Response: { zone, row, bay, tier, score, alternatives[] }

POST /api/v1/containerhub/stacking/rehandling-plan/
  Body: { container_id }
  Response: { steps[], total_moves, estimated_minutes, equipment }

POST /api/v1/containerhub/stacking/pre-marshalling/
  Body: { containerhub_id, target_date, zones[]? }
  Response: { moves[], sick_stacks_before, sick_stacks_after, savings }

POST /api/v1/containerhub/stacking/train-unloading-plan/
  Body: { train_arrival_id }
  Response: { placements[], quality_score, expected_restacks }

9. Railway Operations — Специфіка залізниці (Dry Port)

9.1 Gauge Change (Хоргос)

Хоргос — точка зміни колії (1435mm Китай → 1520mm Казахстан/СНД). Контейнери перевантажуються з одних платформ на інші. Для dry port у Алмати залізничний вʼїзд працює на широкій колії 1520mm.

9.2 Процес розвантаження ЖД

Потяг прибуває на під'їзну колію терміналу
┌─────────────────────────────────┐
│ 1. Реєстрація TrainArrival     │  ← Маніфест від ЖД оператора
│    (ручне введення або EDI)     │     або ручне введення
└──────────┬──────────────────────┘
┌─────────────────────────────────┐
│ 2. Алгоритм Placement          │  ← Автоматичне призначення
│    Optimization для кожного     │     позицій у дворі
│    контейнера в маніфесті       │
└──────────┬──────────────────────┘
┌─────────────────────────────────┐
│ 3. Друк плану для кранівника   │  ← Порядок: платформа → позиція
│    (або відображення на планшеті│     Crane operator бачить куди
│    кранівника)                  │     ставити кожен контейнер
└──────────┬──────────────────────┘
┌─────────────────────────────────┐
│ 4. Розвантаження краном (RTG /  │  ← Кожне зняття = GateTransaction
│    Reach Stacker / Gantry)      │     type=gate_in, transport_mode=rail
└──────────┬──────────────────────┘
┌─────────────────────────────────┐
│ 5. Підтвердження кожної        │  ← Mobile app або сканер
│    позиції (scan → confirm)     │
└─────────────────────────────────┘

9.3 Особливості ЖД vs Авто

Аспект Автомобіль (gate-in) Залізниця (train unloading)
Одиниця 1 контейнер за раз 40-80 контейнерів пакетом
Порядок Довільний (черга) Фіксований (порядок платформ)
Обладнання Reach stacker RTG / Gantry crane
Швидкість ~5 хв/контейнер ~3 хв/контейнер (кран швидший)
Оптимізація Placement per container Batch placement для всього потяга
Документ GateTransaction TrainArrival → GateTransaction[]

10. Мобільний додаток — Розширення

8.1 Нові екрани для yard-менеджера

Екран Опис
ContainerScanScreen Камера для сканування номера контейнера (OCR) + ручне введення
GateFormScreen Швидка форма gate-in / gate-out: номер, стан, пломба, фото, зона
InventoryWalkScreen Режим обходу: зона → рядки → підтвердження наявності кожного контейнера
ZoneBrowserScreen Список зон + вміст кожної зони (контейнери)
ContainerDetailScreen Картка контейнера: всі дані + історія переміщень

8.2 WatermelonDB — Нові таблиці

// containers — sync pull only (reference + current state)
containers: {
  columns: [
    { name: 'container_number', type: 'string' },
    { name: 'container_type_name', type: 'string' },
    { name: 'owner_name', type: 'string' },
    { name: 'condition', type: 'string' },
    { name: 'current_zone_code', type: 'string' },
    { name: 'current_position', type: 'string' },
    { name: 'is_loaded', type: 'boolean' },
    { name: 'is_reefer', type: 'boolean' },
    { name: 'last_gate_in', type: 'number' }, // timestamp
  ]
},

// gate_transactions — sync pull + push
gate_transactions: {
  columns: [
    { name: 'number', type: 'string' },
    { name: 'transaction_type', type: 'string' },
    { name: 'container_number', type: 'string' },
    { name: 'container_condition', type: 'string' },
    { name: 'seal_number', type: 'string' },
    { name: 'seal_intact', type: 'boolean' },
    { name: 'is_loaded', type: 'boolean' },
    { name: 'yard_zone_code', type: 'string' },
    { name: 'yard_position', type: 'string' },
    { name: 'gate_timestamp', type: 'number' },
    { name: 'status', type: 'string' },
    { name: 'photo_uri', type: 'string' }, // local URI
  ]
},

// inventory_tasks — sync pull + push (assigned lines for current user)
inventory_tasks: {
  columns: [
    { name: 'inventory_server_id', type: 'string' },
    { name: 'container_number', type: 'string' },
    { name: 'expected_zone_code', type: 'string' },
    { name: 'expected_position', type: 'string' },
    { name: 'line_status', type: 'string' }, // pending → confirmed/missing/misplaced
    { name: 'actual_zone_code', type: 'string' },
    { name: 'actual_position', type: 'string' },
    { name: 'notes', type: 'string' },
    { name: 'photo_uri', type: 'string' },
  ]
}

9. Backend URL Structure

/api/v1/containerhub/

# Master Data (CRUD через UniversalViewSet або router)
containers/                          # Container
containers/<pk>/inspections/         # ContainerInspection (nested)
containers/<pk>/seals/               # ContainerSeal (nested)
containerhubs/                           # ContainerHub
containerhubs/<pk>/zones/                # YardZone (nested)
shipping-lines/                      # ShippingLine
container-tariffs/                   # ContainerTariff

# Transactions
gate-transactions/                   # GateTransaction
gate-transactions/<pk>/photos/       # GatePhoto (nested)
container-movements/                 # ContainerMovement
container-bookings/                  # ContainerBooking
yard-inventories/                    # YardInventory
yard-inventories/<pk>/lines/         # InventoryLine (nested)
yard-inventories/<pk>/drone-results/ # DroneFlightResult (nested)
demurrage-calculations/              # DemurrageCalculation

# Special endpoints
drone/upload/                        # POST — drone batch upload
drone/detections/<flight_id>/        # GET — detections by flight
yard-map/<containerhub_id>/              # GET — aggregated yard data for map
demurrage/recalculate/               # POST — mass recalculation
gate/today-schedule/<containerhub_id>/   # GET — bookings for today

# Stacking Optimization
stacking/suggest-placement/          # POST — optimal slot for new container
stacking/rehandling-plan/            # POST — how to retrieve blocked container
stacking/pre-marshalling/            # POST — night shift optimization plan
stacking/train-unloading-plan/       # POST — batch placement for train

# Railway
train-arrivals/                      # TrainArrival CRUD
train-arrivals/<pk>/manifest/        # TrainManifest (nested)

10. Frontend Config Structure

containerhub (Section)
├── containerhubMasterData (Group)
│   ├── container_registry (Subgroup)
│   │   ├── containers (MASTERDATA) — Контейнери
│   │   ├── containerTypes (MASTERDATA) → logistic (reuse)
│   │   └── shippingLines (MASTERDATA) — Судноплавні лінії
│   ├── containerhub_infrastructure (Subgroup)
│   │   ├── containerhubs (MASTERDATA) — Термінали
│   │   └── containerTariffs (MASTERDATA) — Тарифи
│   └── (reuse logistic locations, cargo types, freight contractors)
├── containerhubTransactions (Group)
│   ├── gate_operations (Subgroup)
│   │   ├── gateTransactions (TRANSACTIONDATA) — Gate-In / Gate-Out
│   │   └── containerBookings (TRANSACTIONDATA) — Бронювання слотів
│   ├── yard_operations (Subgroup)
│   │   ├── containerMovements (TRANSACTIONDATA) — Переміщення
│   │   └── yardInventories (TRANSACTIONDATA) — Інвентаризація
│   └── financial (Subgroup)
│       └── demurrageCalculations (TRANSACTIONDATA) — Демередж / Детеншен
├── containerhubProcesses (Group)
│   ├── yard_operations (Subgroup)
│   │   ├── yardMap (PROCESS) — 3D Карта двору (ізометрія + plan view)
│   │   ├── stackingOptimizer (PROCESS) — Оптимізація штабелювання
│   │   └── trainUnloading (PROCESS) — Планувальник розвантаження потяга
│   ├── gate_operations (Subgroup)
│   │   └── gateControl (PROCESS) — Контроль воріт
│   ├── inventory (Subgroup)
│   │   └── inventoryDashboard (PROCESS) — Дашборд інвентаризації
│   └── financial (Subgroup)
│       └── demurrageTracker (PROCESS) — Трекер демереджу
└── containerhubAnalytics (Group)
    └── reports (Subgroup)
        ├── containerhubOccupancy (REPORT) — Заповненість терміналу
        ├── containerTurnover (REPORT) — Оборотність контейнерів
        ├── demurrageReport (REPORT) — Звіт по демереджу
        └── inventoryAccuracy (REPORT) — Точність обліку

11. Демередж vs Детеншен — Бізнес-логіка

ДЕМЕРЕДЖ (Demurrage)
= Плата shipping line за зберігання контейнера в порту/терміналі
  понад вільний період (free days).
  Нараховується ПОКИ контейнер НА ТЕРМІНАЛІ.

  Формула:
  chargeable_days = max(0, (gate_out_date - gate_in_date) - free_days_import)
  demurrage = chargeable_days × rate_per_day

ДЕТЕНШЕН (Detention)
= Плата shipping line за використання контейнера ПОЗА ТЕРМІНАЛОМ
  (від gate-out до повернення порожнього контейнера).
  Нараховується ПОКИ контейнер ПОЗА ТЕРМІНАЛОМ.

  Формула:
  chargeable_days = max(0, (return_date - gate_out_date) - detention_free_days)
  detention = chargeable_days × detention_rate_per_day

Таймлайн (Import):
  ┌─────────────┬──────────────┬──────────────┬──────────────┐
  │ Vessel      │  Free Days   │  DEMURRAGE   │  DETENTION   │
  │ Discharge   │  (in port)   │  (in port)   │  (outside)   │
  │             │              │              │              │
  ├─── Gate-In ─┤              │              │              │
  │             ├── Free ──────┤              │              │
  │             │              ├── Paid ──────┤              │
  │             │              │              ├── Gate-Out ──┤
  │             │              │              │   Free/Paid  │
  │             │              │              │   → Return   │
  └─────────────┴──────────────┴──────────────┴──────────────┘

12. Рішення по уточнюючих питаннях

# Питання Рішення
1 EDI / COPARN ✅ Так — Phase 7
2 Рефрижераторний моніторинг ✅ Так — Phase 6
3 Weighbridge / VGM ✅ Так — Phase 6
4 Митний контроль ✅ Так — Phase 6
5 Container Pooling ✅ Так — Phase 6
6 Залізничний вʼїзд ✅ Так — Phase 2 (TrainArrival + unloading optimizer)
7 Мульти-термінал ✅ Так — один тенант = N терміналів (Алмати + Хоргос + Актау)
8 Scope Поетапно — Phase 1 MVP → Phase 7 Advanced

13. Дорожня карта імплементації

Phase 1 — Core (MVP)

  • [ ] Django app containerhub + models: Container, ContainerHub, YardZone, ShippingLine, ContainerTariff
  • [ ] ContainerInspection, ContainerSeal (subtables)
  • [ ] GateTransaction (gate-in / gate-out) з проведенням
  • [ ] ContainerMovement
  • [ ] ContainerLedger register
  • [ ] EntityRegistry registration + serializers + views
  • [ ] Frontend config: containerhub.ts section
  • [ ] Базові CRUD для всіх довідників і документів
  • [ ] App Store entry: appContainerHub

Phase 2 — Yard & Railway

  • [ ] ContainerBooking (truck appointment)
  • [ ] TrainArrival + TrainManifest (ЖД operations)
  • [ ] YardMap process — Plan View (2D, grid з tier badges)
  • [ ] Stack Detail Panel (перегляд вмісту стека по тірах)
  • [ ] GateControl process panel
  • [ ] StorageChargeLedger register

Phase 3 — Stacking Optimization

  • [ ] Stacking engine: Placement Optimization (greedy з look-ahead)
  • [ ] Stacking engine: Rehandling Plan (beam search, BRP solver)
  • [ ] Stacking engine: Pre-Marshalling (simulated annealing)
  • [ ] Stacking engine: Train Unloading Optimizer (batch placement)
  • [ ] YardMap — Isometric 3D View (Canvas/WebGL)
  • [ ] Stacking Optimizer process UI
  • [ ] Train Unloading Planner process UI
  • [ ] Stacking KPIs dashboard

Phase 4 — Inventory

  • [ ] YardInventory + InventoryLine
  • [ ] InventoryDashboard process
  • [ ] Mobile app: InventoryWalkScreen, ContainerScanScreen, GateFormScreen
  • [ ] Mobile app: ZoneBrowserScreen, ContainerDetailScreen
  • [ ] Mobile sync: containers, gate_transactions, inventory_tasks

Phase 5 — AI Drone

  • [ ] DroneFlightResult + DroneDetection models
  • [ ] Drone upload API endpoint
  • [ ] GPS → Zone mapping algorithm
  • [ ] Container number OCR matching pipeline (YOLO + TrOCR)
  • [ ] AI detection gallery UI
  • [ ] Drone + Manual combined inventory workflow

Phase 6 — Financial & Advanced

  • [ ] DemurrageCalculation with auto-compute
  • [ ] DemurrageTracker process
  • [ ] Detention tracking (gate-out → container return cycle)
  • [ ] Integration with essentials.Invoice (автоматичне виставлення рахунків)
  • [ ] Reports: ContainerHub Occupancy, Container Turnover, Demurrage Report, Inventory Accuracy
  • [ ] Container condition/inspection workflow
  • [ ] Seal management (full lifecycle)
  • [ ] Dangerous goods (IMO/UN classification, зони)
  • [ ] Reefer monitoring (IoT датчики, temperature alerts)
  • [ ] Container Pooling (own/lease/line cost analysis)
  • [ ] Weighbridge / VGM integration
  • [ ] Customs hold/release workflow

Phase 7 — Integrations

  • [ ] EDI COPARN (container release/booking from shipping lines)
  • [ ] EDI BAPLIE (vessel bay plan)
  • [ ] Kazakhstan customs EDI (if applicable)
  • [ ] Shipping line portals integration
  • [ ] ЖД оператор EDI (вагонний лист)

14. Аналіз ринку та актуальності

14.1 Ринок TOS (ContainerHub Operating System)

Показник Значення
Глобальний ринок TOS (2024) $1.2–3.2 млрд
Прогноз (2033) $2.0–7.4 млрд
CAGR 6.5–9.7%

Enterprise гравці: Navis N4 (350+ терміналів), TOPS Expert, CyberLogitec OPUS, Tideworks Mainsail — впровадження коштує мільйони $ + роки інтеграції.

Mid-range: Navis Octopi (SaaS для < 100K TEU/рік), RBS TOS+, ContPark — операційний workflow, але слабка оптимізація стекування.

Ринкова ніша: Доступного TOS з вбудованою оптимізацією штабелювання + AI інвентаризацією для ICD/dry port на 50K–200K TEU/рік в emerging markets — практично не існує.

14.2 Казахстан — Середній Коридор (Middle Corridor)

Метрика 2023 2024 2025
Фрахт через Middle Corridor ~2 млн тон ~4.5 млн тон (+70%) 6+ млн тон
Контейнерний транзит (Казахстан) ~13K TEU ~50.5K TEU 58K+ TEU (+15%)
Китай-Європа-Китай TEU ~1.1K ~27.6K (×25!)
Контейнерні потяги через Актау ~10 300+ (×30!) 600 (план)
Хоргос throughput 365K TEU 372K TEU

Ключові факти: - Хоргос Gateway — найбільший dry port у світі, 372K TEU/рік (ємність 500K) - Порт Актау — новий контейнерний хаб за $38 млн, ємність зростає з 92K до 300K контейнерів/рік - Після 2022 Середній Коридор став стратегічним маршрутом Китай-Європа в обхід РФ - Цифровізація Хоргосу скоротила час обробки контейнера з 5 годин до 1 години

14.3 Стекування — чому це критично

Показник Без оптимізації З оптимізацією
Rehandle Ratio 1.3–1.8 < 1.1
% непродуктивних рухів крана 20–40% < 10%
Вартість зайвого переміщення $50–150 / рух
Для 2500 контейнерів/міс тисячі зайвих рухів сотні (економія ~$100K+/рік)
Час на gate-out 15–25 хв (avg) 5–8 хв

Pre-marshalling знижує кількість restacks при gate-out на 60–80%.

14.4 Drone / AI Inventory — стан технології

Технологія Зрілість Точність
Warehouse drone inventory (indoor) ✅ Production 99.9% (Gather AI)
Container gate OCR (fixed camera) ✅ Production 98–99.5% (Visy, commercial)
YOLO + TrOCR container OCR (research) ✅ Ready 99.1–99.4%
Container yard drone inventory ⚠️ Emerging ~95% (estimated)
Tesseract on containers ❌ Unusable 0.4%

Висновок: YOLO v8 для детекції + TrOCR для OCR номерів — оптимальний стек. Jetson Orin здатний обробляти в реальному часі. Повна автономна інвентаризація дроном — emerging, але технічно реалізовуєма вже зараз.

14.5 Конкурентна перевага модуля ContainerHub

ESWF ContainerHub vs Enterprise TOS vs Mid-Range SaaS

                          Navis N4    Octopi    ESWF ContainerHub
Ціна впровадження         $$$$$       $$$       $$
Час впровадження          1-2 роки    3-6 міс   1-3 міс
Stacking optimization     ✅          ❌        ✅
AI drone inventory        ❌          ❌        ✅
Demurrage auto-calc       ✅          ✅        ✅
Mobile app (offline)      ⚠️ limited  ⚠️        ✅ (WatermelonDB)
Railway operations        ✅          ⚠️        ✅
Multi-containerhub            ✅          ✅        ✅
Інтеграція з DOP          ❌ окремо   ❌ окремо ✅ native (same DB)
Open source               ❌          ❌        ✅ (framework)
Emerging markets focus    ❌          ⚠️        ✅