Skip to content

Repository files navigation

Магістраль — облік вантажних перевезень

Робочий макет системи для компанії вантажних перевезень: напрямки з планом рейсів, рейс, номер якого народжується раніше за вантаж, розрахунок завантажувальних метрів і платної ваги, генерація та друк маркувань і CMR, приймання кожного місця окремо, претензії, години водія за AETR, ставка й маржа рейсу.

Це демо, а не продакшн: сервера немає, усе живе в localStorage браузера. Зроблене під конкретну телефонну розмову — і, що важливо, під розмову, від якої в нас лишився тільки фрагмент: середина стенограми не дійшла, тому частина рішень тут — свідомо позначені припущення, а не факти (docs/00-ground-truth.md).

Але логіка справжня. Завантажувальні метри й платна вага рахуються, а не вписані: 24 палети 1,2×0,8 дають 9,6 LDM з 13,6 · факт 11 520 кг · платна 16 800 кг → 1 машина. Штрихкоди Code 128 читаються сканером з екрана. План напрямку рухається від одного вердикту приймання. І місце з претензією фізично не може поїхати замовнику як прийняте — це заборонено таблицею переходів, а не перевіркою у формі.

Запуск

npm install
npm run dev        # http://localhost:5173

npm run build — збірка, npm test — тести предметної логіки, npm run lint — oxlint.

Що всередині

Розділ Що показує
Огляд Рейка планів по трьох напрямках, живий список того, що чекає на людину, остання активність
Планування рейсу Номер рейсу народжується тут, до вантажу. Схема кодування з чотирьох варіантів (і власний шаблон), драбина вантажу з розрахунком LDM і платної ваги, оснащення проти складу, друк аркуша маркувань і шляхового листа
Шляховий лист Дзеркало паперу від водія: галочки по місцях, фактична вага, одометр, пальне, пломба, номер CMR, простій. Тут рейс із просто броні стає виконаним
Приймання та претензії Скан місця, підказаний вердикт за виміряними критеріями, вердикт по кожному місцю з коментарем, сума претензії, наслідок для плану одразу
Рейси Стан кожного рейсу, плечі маршруту з фактичним часом, розподіл по машинах за кузовом і температурою, панель придатності, витрати й маржа. Закриття блокується без CMR
Заявки Реєстр броней. Заявки, за якими ще немає вантажу, показані як норма, а не як помилка
Вантаж Журнал усіх місць, фільтри, CSV, походження місць від заявленої партії
Напрямки Контракт на обсяг: план рейсів на місяць, типовий вантаж, вимоги замовника, ставка, прогноз виконання
Транспорт і Водії Парк із документами і термінами, свої машини й підрядницькі, години водіїв за AETR, паспорти й візи
Партнери Замовники · Пункти · Перевізники · Постачальники · Договори
Оснащення і Довідники Ремені, кути, пломби, палети, термописці із залишком; типи вантажу, критерії приймання, схеми нумерації, коефіцієнти LDM

Демо на 6 хвилин

Перед показом натисніть «Скинути демо-дані» в сайдбарі — збережений стан у браузері перебиває свіжий сід, і це найкоротший шлях зіпсувати показ.

0:00 · Огляд. Вголос: «Головний напрямок — Харків → Варшава, 13 рейсів з 20 за місяць; одна машина пішла порожнім пробігом, 8 місць чекають на приймання». Рейка планів — перше, за що чіпляється око. Зверніть увагу на пораду «рекомендовано ще 10 рейсів з урахуванням зривів 23,5%»: цифра порахована з фактичних претензій і порожніх пробігів, а не вписана — напрямок віддав 17 закритих рейсів, але чистими з них вийшли 13.

Одразу ж — чесна репліка, з якої треба починати саме цю зустріч: «Ми не почули вашу відповідь про закордон, тому напрямок тут наш здогад. Якщо ви возите Україну — цей рядок міняється, система ні».

0:30 · Планування рейсу. Напрямок Харків → Варшава → номер рейсу народжується першим: Р-26-0418, машина, водій, плечі маршруту з кордоном. Скажіть уголос: разом із номером рейсу зʼявляється бронь-заявка без вантажу — інакше надрукованому номеру не було б де жити.

Перемкніть схему нумерації — чотири варіанти, у кожного своя назва і одне речення ризику. Якщо клієнт запропонує свій формат, наберіть його у «Власний шаблон»: коди перегенеруються на очах. Тут же перемикач код місця містить номер заявки / не містить.

Далі — драбина вантажу:

Р-26-0418                          рейс — народжується першим
├─ ЗМ-26-1043                      заявка = бронь місця на машині
│   ├─ …-PL1 … …-PL24              24 палети 1,2×0,8 · по 480 кг
│   └─ пломба UA 7 418 293
└─ ЗМ-26-1044                      догруз: номер є, вантажу ще нема

24 × (1,2 × 0,8 / 2,4) = 24 × 0,4 = 9,6 LDM з 13,6
факт 11 520 кг · обʼємна × 333 кг/м³ · платна 16 800 кг (9,6 × 1750) → 1 машина

Ця арифметика — головний аргумент екрана. Палети не «важать», вони займають метри, і платять за максимум із фактичної та обʼємної ваги. Система знає, скільком машинам їхати.

1:30 · Той самий екран, нижче. Чек-лист оснащення проти складу: ремені, кути, пломби, термописець — із попередженням по позиції, якої лишається мало. Внизу — кількість маркувань. Натисніть Друк маркувань: аркуш A4 зі справжніми Code 128 і тихими зонами, окремі наліпки на пломби. Потім Друк шляхового листа — той самий папір, що поїде з водієм, уже з номером рейсу, кодами місць і порожніми галочками.

2:10 · Шляховий лист. Скануйте Р-26-0418 (є демо-кнопка). Відмітьте 23 місця з 24, одному впишіть фактичну вагу 470 кг замість 480 — це і буде недостача, яку далі знайде приймання. Внесіть одометр, пальне, простій на кордоні, номер пломби і CMR. Поруч рахуються години: «1 240 км при 68 км/год → 18 год 14 хв за кермом · у водія лишилось 9 год цього тижня» — це AETR, і це закон, а не наша вигадка. Зберегти → рейс із броні стає виконаним.

3:10 · Заявки. Поруч із заповненими заявками — рядки зі штампом «МІСЦЕ ЗАБРОНЬОВАНО · ВАНТАЖУ НЕМА». Скажіть уголос: це нормальний стан приблизно четверті реєстру, бо номер друкується в Харкові до того, як вантаж підтвердили. Відкрийте картку заявки: місця, походження від заявленої партії, історія подій із виконавцем і часом.

3:50 · Приймання та претензії. Скануйте код місця з рейсу. Підтягуються рейс, заявка, напрямок і виміряні критерії: «470 кг проти 480 заявлених — недовага 2,1%, допуск 2%». Система сама пропонує «претензія · недостача». Поставте вердикт з коментарем, решту місць прийміть. Дивіться на план: 13 → 12, рейс перестав бути чистим. Ваш вердикт перебив підказку — це записалось у журнал.

4:40 · Огляд, 10 секунд. Сигнал зʼявився сам: «претензія по рейсу Р-26-0418 · недостача 10 кг». Ніхто його не вводив. Це і є «система рахує, а не показує».

5:00 · Рейси. Відкрийте рейс, який ще комплектується, і додайте до нього заявку — панель придатності відмовляє з причинами, і всі вони перевірні: «не влазить 2,4 LDM», «потрібен реф, у машини тент», «у водія віза до 02.09 — закордонний рейс неможливий», «у машини техогляд прострочений», «години AETR вичерпані». Розподіл по машинах ділить вантаж сам: реф −18 і тент не їдуть разом, ADR клас 3 не їде з харчовими. Внизу — витрати й маржа: пальне за нормою, платні дороги, добові, ставка перевізника, якщо рейс відданий на сторону. Натисніть Закрити рейс без CMR — система не дасть і скаже, чого саме бракує.

5:50 · Партнери й Транспорт. Замовники · Пункти · Перевізники · Постачальники · Договори, Транспорт і Водії — по пʼять секунд: «розділи є, наповнимо разом; а перевізники тут є тому, що ми не знаємо, парк у вас свій чи підрядний — це наше друге питання».

Дані демо

3 напрямки (головний PL14 «Харків → Варшава» — 13 чистих рейсів з плану 20 при 17 закритих, DE11 «Берлін → Одеса» на рефі, CZ17 «Дніпро → Прага» щойно відкритий), 28 рейсів у всіх станах включно з одним порожнім пробігом, 39 заявок (з них 12 — бронь без вантажу, empty: true), 266 місць вантажу (100 прийнято, 8 чекають приймання, 5 із претензією), 13 машин (тягачі, напівпричепи, рефи, ADR, одна на ТО з простроченим техоглядом), 10 водіїв (в одного спливає віза, в одного вичерпані години AETR), 8 замовників, 14 пунктів в Україні та ЄС, 3 перевізники, 4 постачальники, журнал на 1096 подій.

Числа вище — не з голови: вони зняті з buildSeed() і закріплені тестами в seed.test.ts, тому README і екран не можуть розійтися незамітно.

Що варто підкрутити перед показом справжньому клієнту: частка зривів 23,5% на головному напрямку — це навчальна цифра для демо, а не правдоподібна для живої експедиції (там 2–4%). Вона зроблена такою, щоб порада «рекомендовано ще 10 рейсів» була видимою на екрані. Якщо клієнт чіплятиметься за реалістичність — зменшуємо кількість претензій у сіді.

Генератор детермінований (src/lib/seed.ts, без Math.random() і Date.now()) — на будь-якому компʼютері однакові цифри. Дата демо — 24.08.2026. Ключ збереження — mahistral-demo-v1.

Стек

React 19 + TypeScript, Vite, Tailwind v4, shadcn/ui (Radix), Recharts, Zustand + persist, vitest.

Архітектура: що саме тут «справжнє»

Уся предметна логіка зібрана в чистих модулях без React, кожен із тестами:

  • codes.ts — чотири схеми нумерації, конструктор власного шаблону, контрольний знак (ловить кожну одиничну підміну знака і кожну перестановку сусідніх), номери рейсу, заявки, місця, CMR і пломби. Випадкові частини детерміновані від номера — на кожному компʼютері однакові.
  • code128.ts — справжній кодер Code 128B із контрольним символом за модулем 103. Смуги малюються прямокутниками, а не фоном, бо фонову картинку браузер має право не друкувати.
  • derive.tsexpandLoad(): заявка замовника → місця з кодами, оснащення, кількість маркувань, LDM, обʼєм, платна вага і кількість машин. Тут живе фізика завантажувальних метрів, і вона одна на всю систему.
  • dispatch.tsplanVehicles() ділить вантаж по машинах за кузовом, температурою і ADR; orderEligibility() повертає всі причини, чому цю заявку не можна дати на цей рейс, а не першу.
  • hours.ts — AETR / Регламент ЄС 561/2006: 9 год за кермом на добу (двічі на тиждень 10), перерва 45 хв після 4,5 год, 11 год добового відпочинку, 56 год на тиждень. Це закон, тому цифри не налаштовуються.
  • economics.ts — пальне за нормою на 100 км, платні дороги, добові, ставка перевізника → витрати рейсу → маржа у € і % → ставка за км.
  • inspect.tssuggestVerdict() міряє те, що можна виміряти: недовагу проти заявленої (>2% → недостача), години в дорозі проти ліміту типу вантажу, відхилення температури, невідповідність пломби. Вердикт людини завжди перебиває підказку, і перебивання пишеться в журнал.
  • plan.ts — план напрямку міряється закритими рейсами, а не місцями; частка зривів рахується з фактичних претензій і порожніх пробігів, і з неї виходить «рекомендовано ще N рейсів».
  • transitions.ts — три таблиці переходів: рейс, заявка, місце. претензія → доставлено заборонено таблицею і тестом, який перебирає всі пари. Рейс не закривається без CMR і без вердикту по кожному місцю.

Чого свідомо немає

Справжнього бекенду й автентифікації, прав на рівні полів, ролей крім диспетчера, митного оформлення в деталях (T1, TIR, ЕАД, УКТЗЕД), паливних карт і реконсиляції транзакцій, лотів шин і плану ТО, зарплатної відомості, GPS-трекінгу в реальному часі. Останнє — принципово: демо працює без сервера, і намалювати рухому машину на карті без бекенду можна тільки збрехавши. Позиція в моделі є там, де вона справді береться з життя: плече маршруту з фактичним часом прибуття зі шляхового листа.

Усе це — предмет технічного завдання, а не демо.

Що саме ми чули дослівно, а що вигадали як галузевий дефолт — docs/00-ground-truth.md (до нас дійшов лише фрагмент стенограми, і це там сказано першим рядком). Порядок денний Zoom на 12 питань — docs/05-open-questions.md. Карта моделі — docs/01-model.md.

About

Магістраль — демо системи обліку вантажних перевезень: LDM і платна вага, маркування Code 128, CMR, приймання по місцях, години водія за AETR

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages