Перейти к содержимому

Логистика · Веб

Reista

Кабинет для клиентов транспортной компании: статус груза, маршрут и документы по каждой отправке — без звонка диспетчеру. Основная работа была не в кабинете: до проекта статус нигде не хранился, он жил в переписке водителей и в памяти диспетчера.

Роль
Продуктовый дизайн, фронтенд, бэкенд
Срок
4 месяца
Стек
Next.js · TypeScript · PostgreSQL

Что изменилось

−70%
звонков в диспетчерскую: было около 700 в неделю, стало около 210. Второе число — четвёртый месяц после открытия; в первые недели звонков было больше, чем до кабинета.
518 → 28
звонков в неделю про статус и документы. Остальные темы — изменение заявки и нештатные ситуации — как были около 180 в неделю, так и остались.
18% → 4%
отправок в пути, у которых последнее событие старше суток. Первое число — четвёртая неделя после запуска событий, второе — спустя три месяца. Разрыв закрыла не техника, а то, что его стало видно снаружи.
в тот же день
появляется копия подписанной накладной. Раньше её ждали день-два: клиент писал запрос, бухгалтерия искала скан.

Что не изменилось — сроки доставки. Кабинет не ускорил ни одну машину: груз едет ровно столько, сколько ехал, и сорванных дат меньше не стало. Изменилось время, за которое о срыве узнают: клиент получает письмо тогда же, когда о задержке узнала диспетчерская, а не через два дня и не от собственного получателя.

Что было до

Статус груза знала только диспетчерская. Клиент звонил, диспетчер открывал карточку отправки и пересказывал то, что видел на экране, — а на экране были заявка, рейс и плановая дата. Где груз сейчас, экран не знал: это диспетчер достраивал по переписке с водителем и по утреннему обзвону перевозчиков.

  • Статуса не было ни в одной программе. Учётная система знала, что отправку приняли и что по ней выставили счёт; всё, что между этим, лежало в переписке водителей, в утреннем обзвоне перевозчиков и в голове того, кто ведёт направление. Когда он уходил в отпуск, направление отвечало клиентам хуже.
  • Около 700 звонков в неделю на шестерых диспетчеров. Три четверти — «где мой груз» и «пришлите документы»: вопросы, на которые мог ответить только диспетчер и только пока он на месте.
  • О задержке клиент узнавал последним — обычно от собственного получателя, который ждал машину. Диспетчерская знала о ней с утра, но предупредить было некому и нечем: списка «кого это касается» не существовало.

Что сделали

  • Кабинет, где у отправки видно последнее событие с временем, местом и источником, а рядом — весь её путь: что подтверждено и что пока план.

  • Уведомления в кабинете и на почту: по умолчанию — передача в доставку, вручение и любой сдвиг плановой даты, остальные события включаются по желанию.

  • Документы по отправке скачиваются оттуда же, без запроса в диспетчерскую: копия подписанной накладной появляется в день вручения.

Результат

Диспетчеры перестали пересказывать экран по телефону: звонков стало на 70% меньше, а оставшиеся — про нештатные ситуации и изменения в заявке, то есть про то, где нужен человек, а не экран. Но сработал не кабинет сам по себе: сначала у перевозчика появились события, потом их стало видно снаружи, и только после этого их начали отмечать вовремя.

Кто заказчик

Межрегиональный перевозчик сборных грузов: четыре терминала, свои машины и наёмные, около 2 100 отправок в месяц, 540 компаний-клиентов. Средняя отправка едет четыре дня и проходит через два терминала. В диспетчерской шесть человек, каждый ведёт свои направления.

Отправка — это не машина, а несколько мест груза в чужой машине: в одном рейсе едут отправки десяти-пятнадцати компаний, и каждая сходит на своём терминале. Поэтому вопрос «где мой груз» не сводится к вопросу «где машина»: у отправки своя история, и с историей рейса она совпадает только на одном участке пути.

Задача

Как ставил заказчик
Сделать личный кабинет: клиент заходит и видит, где его груз. Работа оценивалась в пару месяцев — данные же есть, надо только показать их наружу.
Как задача выглядела после разбора
Данных не было. В учётной системе у отправки было поле «состояние» на одиннадцать значений; регулярно проставлялись три, и две из трёх — уже после доставки. Между приёмом и вручением база молчала. Значит кабинет — вторая и меньшая часть работы, а первая — сделать так, чтобы у перевозчика впервые появилась запись о том, где груз, и чтобы её кто-то вёл. Кабинет здесь не витрина, а причина: пока статус видит только диспетчер, его можно не отмечать; когда его видит клиент — уже нельзя.

Почему нельзя было в лоб

Сам кабинет — тонкая часть задачи. Сложное сидело выше по течению, в том, откуда берутся события: их надо снимать с людей, занятых другим, и с машин, половина которых чужая.

Учётная система перевозчика
Коробочная программа для транспортной компании на поддержке поставщика: заявки, рейсы, тарифы, счета. Переписывать её никто не дал, да и незачем — единственное, что мы бы туда добавили, это статус, а статуса в ней и не было. Разделение вышло само собой: она остаётся источником того, что за отправка и по какой она заявке, а всё, что с отправкой происходит, живёт у нас. Наружу она отдаёт зеркало базы на чтение, и обратно в неё не уходит ни строки.
Наёмный транспорт
В высокий сезон 47% рейсов идут машинами наёмных перевозчиков. Их водители не сотрудники: приложение им не поставишь и кнопку нажимать не заставишь. Любое решение, опирающееся на действие водителя, покрывает половину объёма, а кабинет с половиной отправок хуже, чем никакого: клиент один раз увидит пустую карточку и вернётся к телефону.
Телематика
Трекеры стоят на своих машинах. У наёмных их либо нет, либо данные никто не отдаст. Точка на карте у одной отправки и пустое место у соседней — это не частичная функция, а обещание, которое выполняется через раз.
Работа на терминале
У кладовщика заняты руки, и в сезон он не станет заполнять форму — а данные нужнее всего именно в сезон. Событие обязано сниматься движением, которое он и так делает: за секунду и без клавиатуры. Иначе оно не снимается совсем, и выясняется это через месяц после запуска.
Что нельзя показывать наружу
Во внутренней карточке отправки лежат ставка наёмного перевозчика, телефон водителя, номер машины и состав рейса, а рейс сборный: рядом едут отправки десяти-пятнадцати других компаний. Открыть эту карточку клиенту нельзя ни целиком, ни почти целиком.

Как шла работа

  1. 01

    Откуда берётся статус

    3 недели

    Две недели рядом с диспетчерской: слушали звонки и отмечали тему каждого — отсюда 700 в неделю и три четверти про статус и документы. Параллельно разбирали, что в учётной системе есть на самом деле. Итог этапа — не макет кабинета, а список событий и ответ на вопрос, кто каждое может подтвердить, не отвлекаясь от своей работы. Список начинался с двенадцати событий, осталось шесть: вычеркнули всё, что некому подтвердить.

  2. 02

    События

    1,5 месяца

    Своя база: отправка, рейс, событие и источник события. Чтение из учётной системы, запись только к себе. Терминальный экран и сканеры на четырёх площадках, отметка вручения с телефона водителя. Половина этапа ушла на пилот одного терминала: из формы убирали поле за полем, пока не осталось сканирование и одна кнопка, а сам экран переехал со стола на стойку у ворот — кладовщик не отходит от паллеты, чтобы что-то отметить.

  3. 03

    Кабинет

    1 месяц

    Список отправок, карточка с путём, документы, пользователи, подписки. Внутри компании-клиента завели отдельных пользователей вместо общего логина: логист, бухгалтер и руководитель приходят за разным и подписаны на разное. Подписка по умолчанию — на передачу в доставку, на вручение и на любой сдвиг плановой даты: по журналу звонков было видно, что звонят не вообще, а за день до ожидаемой даты. Остальные четыре события можно включить, и включают их редко.

  4. 04

    Открытие волнами

    3 недели

    Сначала 40 компаний, дающих 55% отправок, через две недели — остальные. В первые дни звонков стало не меньше, а больше, и это было правильно: клиент видел, что с четверга по его отправке ничего не отмечено, и звонил именно про это. Этап оказался не про запуск, а про закрытие разрывов: на два терминала доехали вторые сканеры, а утренний обзвон перевозчиков впервые стал попадать в базу, а не в блокнот.

Технические решения

Стек выбран под то, что кабинет читают часто, а меняют редко: Next.js отдаёт страницу с сервера, чтобы ссылка из письма открывалась статусом, а не спиннером; PostgreSQL хранит события и сам следит, чтобы в выборку не попала чужая отправка; TypeScript держит один словарь событий на сервере и на экране — их шесть, и седьмой мимо интерфейса не заведётся.

  • Состояние заменили событием

    Поле «состояние» описывало счёт, а не груз, поэтому чинить его не стали. Вместо него — записи о том, что произошло: тип, время, место, кто отметил. Типов шесть: принят у отправителя, принят на терминале, загружен в рейс, прибыл на терминал назначения, передан в доставку, вручён. Статус нигде не хранится — он и есть последнее событие. Следствие важнее модели: устаревший статус перестал быть скрытым дефектом данных и стал видимой строкой — последнее событие было в среду в 14:20, и это всё, что известно.

  • Событие снимается одним движением

    Этикетка на месте груза уже была: по ней кладовщик раскладывал груз по направлениям, но код на ней был для глаз, его никто не считывал. На четырёх терминалах поставили сканер и экран, где после сканирования нажимают одну из двух кнопок: принял или загрузил. Вручение отмечает водитель с телефона — фотографией подписанной накладной. Одно действие даёт и событие, и первую копию документа: она попадает к клиенту задолго до того, как бумажный оригинал доедет до его бухгалтерии.

  • Карта показывает маршрут, а не машину

    Трекер есть у своей машины и нет у наёмной, поэтому живой точки в кабинете нет ни у кого. Карта рисует путь отправки по терминалам: что пройдено, когда и чем подтверждено, что осталось и к какой дате по плану. Такой маршрут одинаково честен для своего рейса и для наёмного, и его не приходится объяснять клиенту, который вчера видел движущуюся точку, а сегодня не видит.

  • Наружу отдаётся проекция, а не карточка

    Кабинет не читает внутреннюю запись отправки: он читает отдельную таблицу, куда попадают только поля, разрешённые клиенту. Отсечка по компании стоит не в коде экрана, а в самой базе — политика PostgreSQL на уровне строк применяется ко всем запросам, включая те, что напишут через год. Забыть условие в одной выборке легко; обойти правило, которое применяет база, — нет.

  • Кабинет не досказывает за диспетчерскую

    Статус вперёд не вычисляется. Если последнее событие — прибытие на терминал назначения во вторник, кабинет пишет это, а не «в пути»: плановая дата стоит рядом и подписана как план. У каждого события виден источник — снято сканером на терминале, отмечено водителем или проставлено диспетчером со слов перевозчика. Отметка со слов и отметка сканера выглядят по-разному намеренно: у отправки, которую везёт наёмная машина, вручение почти всегда со слов, и клиент должен понимать, чему верит.

Что осталось за рамками

Часть работы не входила в кабинет по решению, часть упирается не в код.

  • Заказ перевозки. Кабинет только показывает: заявка по-прежнему приходит письмом или по телефону и заводится диспетчером. Принять заявку — значит проверить габариты, направление и тариф, то есть писать внутрь, а не показывать наружу. Это отдельная работа, и она тяжелее.

  • События от наёмных перевозчиков. Их отмечает диспетчер со слов, по утреннему обзвону. Чтобы отмечал сам перевозчик, ему нужен свой кабинет, а это другой продукт и другой разговор — о том, кто кому что обязан.

  • Мелкие отправители. Из 540 компаний в кабинет за месяц заходит 62%, и на них приходится 89% отправок. Тому, кто отправляет раз в квартал, звонок дешевле логина, и переучивать его никто не собирается.

  • Оригиналы документов. Бумажный комплект едет как ехал: копия в кабинете не заменяет оригинал для бухгалтерии клиента. Электронный документооборот перевозчик заводит отдельно и не в этом проекте.

Сейчас в работе: плановая дата, посчитанная по фактическим срокам за первый год вместо тарифного норматива, и приём груза у отправителя — единственное из шести событий, которое до сих пор ставится задним числом, уже на терминале.

Контакты

Пришлите описание задачи

Ответ с объёмом работ, сроками и оценкой бюджета придёт в течение 24 часов.

Первый звонок — 30 минут, без обязательств с вашей стороны.