Перейти к содержимому
VC
Кейс 14 из 33 · Интернет-торговля · операции

Обмен файлами с партнёром по отгрузке: файл дня туда, стоимость и треки обратно

Склад бренда товаров для активного отдыха из США каждый день отдаёт партнёру по отгрузке файл с партией и получает его назад с трек-номерами и стоимостью доставки. Теперь файл собирается сам и выглядит как рабочая книга, а ответ партнёра разбирается и встаёт в нужные строки живой таблицы.

Отрасль
Интернет-торговля, бренд для активного отдыха из США
Стек
Python · openpyxl · Apps Script
Сроки
≈ 3 недели до записи стоимости в книгу
Итог
стоимость доставки: поиск глазами → сама в строку
01 · Боль

Файл для партнёра готовили руками, ответ партнёра разносили глазами

Тот же бренд, те же две рабочие книги склада и тот же приёмник, что в кейсе «Оплаченные заказы Shopify → рабочие таблицы склада». Там заказы приезжают в книгу из двух магазинов. Здесь другой источник данных и другой адресат: внешний партнёр по отгрузке, который работает в файле xlsx.

Каждый вечер оператор склада ставил в книге дату отправки партии, выгружал всю книгу в Excel, удалял всё, кроме строк за сегодня, рисовал рамки, поправлял многострочные адреса, которые Excel склеивал в одну строку, и отправлял файл партнёру в чат. Партнёр возвращал тот же файл с трек-номерами и стоимостью доставки по каждой посылке.

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

Запрос оператора склада

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

02 · Решение

Две половины: файл дня туда, стоимость и треки обратно

Половина «туда» собирает файл дня по дате, которую оператор ставит в книге, и повторяет раскраску книги. Половина «обратно» разбирает файл партнёра, переносит из него стоимость доставки в живую книгу и раскладывает трек-номера комплектов по строкам листа. Сама запись в книгу идёт через тот же приёмник на Apps Script, что и в основном конвейере; здесь описано только то, что происходит вокруг партнёра.

01
Дата отправки в книге

Оператор ставит дату партии в колонке «Дата отправки». Сторож каждые 5 минут замечает новую дату и ждёт, пока строки перестанут прибавляться

02
Файл дня

Строки за день, сетка, переносы, ширины по данным. Правила раскраски книги пересчитаны построчно, в xlsx лежит настоящее условное форматирование

03
Партнёр заполняет

Трек-номера и стоимость доставки по каждой посылке вписываются в тот же файл, файл возвращается в чат: в тему группы или личным сообщением

04
Разбор входящего xlsx

Ключ «номер заказа + суффикс строки», стоимость встаёт в свою строку, запись только поверх нетронутой исходной формулы

05
Треки и детектор

Треки комплекта раскладываются по строкам листа при равном счёте; отдельный детектор ищет заказы, отгруженные целиком, у которых остались строки без трека

Файл дня собирается по дате, которую ставит оператор

Оператор ставит дату отправки один раз в день, перед отправкой партии, и эта дата стала и фильтром, и пусковым сигналом. Сторож читает книгу через приёмник в режиме «только чтение», замечает новую дату и присылает файл в тему группы через 10 минут после того, как строки за эту дату перестали прибавляться. Пауза защищает партнёра от неполного файла: без неё выгрузка ушла бы в момент, когда оператор ещё проставляет даты. Если заказы приходят позже, уходит вторая версия файла, до трёх на одну дату. Фиксированные часы отправки отменены по просьбе оператора: они присылали файл до того, как партия собрана.

Первая сетка была раз в 20 минут, и вместе с паузой и следующим запуском ожидание готового файла доходило примерно до 50 минут. Сетка доведена до 5 минут, ожидание сократилось примерно до 15, а для случая «сели только отправить таблицу» в чате появилась команда, которая собирает файл сразу. Нагрузку на чтения измерили заранее: один прогон читает четыре колонки книги постранично, это два чтения, и пятиминутная сетка в рабочем окне даёт около 380 чтений в сутки при большом запасе до квот.

Раскраска книги: правила читаются и считаются на нашей стороне

Владелец попросил, чтобы в файле «не терялось» ничего из ручной выгрузки: в книге цветные товары, красный «нет трека», оранжевый «отправлено». Цвета книги держатся на условном форматировании: правило смотрит на текст в колонке товара и красит ячейку, сама ячейка при этом остаётся белой. Чтение цвета ячейки поэтому бесполезно, читать нужно сами правила. Приёмник уже умел отдавать их служебным действием, так что раскраска воспроизведена без единой правки в Apps Script и без выкатки, которая здесь стоила бы дорого.

Правила, зависящие от самой строки, считаются построчно: совпадение по тексту, пусто или заполнено, больше или меньше числа. Первое совпадение выигрывает, как в самой таблице. Правила на COUNTIF, которые подсвечивают дубли номера и трека, пропущены осознанно: они считают по всей колонке книги, в файле дня три десятка строк, и «дублей нет» там было бы ложью: партнёр увидел бы чистую таблицу там, где в книге горит предупреждение. Число пропущенных правил печатается вслух, потому что молчаливое частичное покрытие хуже отсутствующего.

Прогон на живом файле: покрашено 148 ячеек, пропущено 2 правила. Сверено со снимком экрана книги по каждому цвету: совпало. Вычисление правил закрыто пятью тестами: порядок правил, пустые и заполненные ячейки, числовые пороги в формате книги «$13,02», пропуск COUNTIF и правило, которое красит только текст и уступает следующему правилу с заливкой.

Подсветка гаснет в момент ввода

Оператор сформулировал главное требование к подсветке: она обязана исчезать, когда партнёр заносит данные. Заливкой этого не добиться: красная ячейка «нет трека» осталась бы красной после того, как трек вписан, и файл начал бы врать ровно в момент, когда им пользуются. Правила «пусто или заполнено» и числовые пороги перенесены внутрь xlsx настоящим условным форматированием: ISBLANK на треке, дате трека и отметке «отправлено», пороги на стоимости. Excel пересчитывает их сам. Правила по тексту, например «возврат» и «замена», остались заливкой: от ввода партнёра они не зависят и должны быть видны всегда.

По просьбе оператора убраны цвета товаров: важны они в большой книге, в файле дня они лишние. Добавлена сетка, которую раньше рисовали руками перед отправкой, колонки доведены до «Примечаний», адрес приезжает многострочным и по центру, как в книге. Ширина колонок считалась по шапке, поэтому заголовок «Дата получения трек номера» на 26 знаков растягивал колонку под десятизначное значение. Теперь ширина берётся по данным, а шапка переносится: колонка даты трека 28 → 9, стоимости 28–31 → 9, суммарная ширина таблицы 412 → 217 знаков, почти вдвое. Ещё три колонки, которые склад никогда не заполнял, спрятаны без удаления: видимая ширина дошла до 191, данные остаются в файле и раскрываются одним движением.

Стоимость доставки: точный ключ и формула как признак свободной ячейки

Партнёр возвращает ту же таблицу, которую ему выгрузили, и это снимает сопоставление по названиям товара, на котором раньше случались ошибки в одну букву. Ключ точный: номер заказа с суффиксом строки, например 12345, 12345.2, 12345.3. Из файла партнёр удаляет около 10% строк, заказы с других складов, поэтому строка ищется по номеру во всей книге; окно строк однажды промахнулось бы, и промах выглядел бы как «такого заказа в книге нет».

Из шести денежных колонок партнёр заполняет ровно одну, и она никогда не пуста: в ней либо число, которое оператор вписал руками, либо исходная формула деления. Привычный предохранитель «пишем только в пустую ячейку» здесь не защитил бы ничего. Признаком свободной ячейки сделан текст исходной формулы, и проверяет его приёмник в момент записи. Оператор правит книгу руками прямо сейчас, и проверка по выгрузке минутной давности означала бы затереть работу оператора.

В файле нашлась «тонкость с большими палатками», о которой оператор предупреждал: строка палатки вбирает стоимость своих строк-аксессуаров, уехавших отдельными посылками, при этом аксессуарные строки сохраняют стоимость и у себя. Складываются только части одного товара; разные товары одного заказа стоят каждый за себя. Правило выведено из ручной работы оператора и подтверждено им дословно уже после вывода. Нули не пишутся: у партнёра $0 означает «в этой посылке стоимости нет», и такие строки склад оставляет с формулой. Строка, которой нет в книге, не создаётся; если номер совпал при другом товаре, строка пропускается и показывается; строка с треком и без стоимости называется вслух как вопрос партнёру.

Проверка на ручной работе оператора: алгоритм воспроизвёл 34 из 34 заполненных вручную строк дословно, включая все 7 сумм по крупным товарам. Расхождение на 50 строках файла одно: ручное число отличалось от числа в файле партнёра, и оно оставлено оператору как вопрос. Первые шесть дней модуль работал в режиме витрины на отдельной странице книги. После двух подтверждений «верно» режим закрыт, и запись пошла в рабочие строки: 13 и 43 строки на живых файлах, пропусков 0. Витрина сохранена рядом с записью как журнал правок: видно, что именно вписано.

Деньги в другой колонке

Через день после включения записи оператор сообщил, что стоимости из очередного файла в книге нет. Разбор показал: в этом файле партнёр заполнил другую денежную колонку, привычная пустовала по всему файлу. Модуль читал только привычную и честно докладывал «трек есть, стоимости нет». Деньги по догадке не переносятся: колонка выбрана явно, собрана витрина, и только после подтверждения оператора, что партнёр перепутал колонки, вписано 92 строки за один день, пропусков 0. Перед записью сохранён список затронутых строк, чтобы правку можно было откатить. Три строки остались нетронутыми: там стояли числа, вписанные рукой, и приёмник по своему правилу их обходит.

Чтобы случай не повторился молча, добавлен автоподхват с узкой границей: если привычная колонка пуста по всему файлу при заполненной соседней, берётся соседняя, и об этом говорится вслух. Если в привычной есть хоть одно число, поведение прежнее. Тогда же приёмник входящих научился забирать файлы из личной переписки, потому что человек присылает файл туда, где разговаривает. В тему группы уходит отчёт только когда что-то вписано, повторный прогон те же файлы не трогает, память по идентификатору сообщения. Приёмник опрашивает чат каждые 2 минуты в окне 7–22 МСК; опрос стоит один вызов Telegram и таблиц не касается.

Треки комплекта раскладываются по строкам листа

Комплект в магазине одна позиция: «палатка + печь + пол». В книге по просьбе оператора это три строки. Треки группировались по названию позиции, приёмник находил по нему только строку палатки, второй и третий трек отбрасывались. Строки печи и пола оставались пустыми, выглядели неотправленными и уходили партнёру на повторную отгрузку.

Порядок раскладки взят по строкам листа. Раскладка по частям названия положила бы трек печи на пол: в названии позиции печь идёт раньше пола, на листе же порядок другой, палатки первыми, остальное по порядку имён, который задал оператор. Гипотеза проверена на независимом заказе, который оператор заполнил руками и который при проектировании виден не был: предложение бота совпало с ручным заполнением по всем трём трекам до цифры.

Правило оператора: сколько треков, столько товаров уехало, товары никогда не едут вместе под одним треком. Поэтому раскладка делается только при равном счёте: треков ровно столько же, сколько строк. Живой контрпример показал цену догадки: у заказа с двумя треками на три строки оператор поставил второй трек на печь, порядок листа отдал бы его полу, и один трек лёг бы на два товара. Поймано проверкой состояния до записи, в книгу не ушло, контрпример закреплён тестом. При неравном счёте книга остаётся нетронутой, в тему группы уходит сообщение с номерами треков и составом комплекта, чтобы ручная раскладка заняла минуту.

Сравнение старой и новой раскладки на 970 живых заказах: затронуто ровно 2 заказа, изменений при том же ключе 0. В контуре треков 110 зелёных тестов, четыре из них новые, включая эталонный порядок и запрет выдумывать.

Второй признак: заказ отгружен целиком, а строки без трека есть

Цена пропущенного дефекта равна повторной отправке товара клиенту и повторной оплате доставки. До этого его ловило только внимание оператора, который «не знаю каким чутьём» присмотрелся к двум заказам. Отдельный модуль ищет такие строки списком и в книгу ничего не пишет.

Признаков два. Узкий признак «трек потерялся по дороге» пропустил бы комплект с одним треком на три строки: трек в книге стоит, ничего не потеряно, и две строки всё равно выглядят неотправленными. Второй признак независим от первого: заказ в магазине отгружен целиком, и в книге у него остались строки с пустым треком. Покрытие проверено, чтобы признак не оказался мёртвым: 726 из 924 активных заказов проходят как «отгружен целиком». Прогон за 60 дней по 924 активным заказам дал одну находку: тот самый заказ с потерянным треком и двумя строками комплекта.

03 · Стек

Стандартная библиотека там, где её хватает, openpyxl там, где нужен

Python, стандартная библиотека

Разбор входящего xlsx как zip с XML, без сторонних зависимостей; запросы к приёмнику через urllib

openpyxl

Сборка файла дня: сетка, переносы, ширины по данным, условное форматирование через FormulaRule и CellIsRule

Google Apps Script, приёмник

Выгрузка только на чтение, служебное действие с выдачей правил условного форматирования книги, действие записи стоимости поверх нетронутой исходной формулы

Telegram Bot API

Файлы дня в тему группы, команда сборки файла в чате, приём входящих xlsx из темы и личных сообщений через getFile

systemd timers

Сторож даты отправки каждые 5 минут с 09:00 до 24:00 МСК; приёмник входящих каждые 2 минуты с 07:00 до 22:00 МСК

pytest

8 тестов разбора стоимости, 5 тестов вычисления правил раскраски, 110 в контуре треков

PythonopenpyxlApps ScriptGoogle SheetsTelegram Bot APIsystemdpytest
04 · Результат

Что изменилось с обеих сторон

Ожидание файла дня
≈ 50 мин ≈ 15 мин

сторож каждые 5 минут; команда в чате собирает файл сразу

Совпадение с ручным заносом
34 / 34

строк стоимости воспроизведено дословно, включая все 7 сумм по крупным товарам

Риск повторной отгрузки
1

находка на 924 активных заказа за 60 дней; признак покрывает 726 из них

Файл партнёру уходит сам, с сеткой, переносами, адресами и подсветкой, которая гаснет по мере заполнения. Ручная доводка перед отправкой исчезла, как исчезла и выгрузка всей книги с удалением лишнего.

Стоимость доставки перестала быть задачей на поиск глазами: на живых файлах записано 13, 43 и 92 строки, пропусков 0, ручные числа оператора остались нетронутыми по правилу записи только поверх исходной формулы. Витрина рядом с записью показывает, что именно вписано; список затронутых строк перед большой правкой даёт откат.

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

Проверки, которые можно показать заказчику

Каждая половина обмена сверена с ручной работой людей до того, как ей доверили живую книгу.

  • Раскраска. 148 ячеек сверены со снимком экрана книги по каждому цвету; 2 пропущенных правила названы вслух.
  • Стоимость. 34 из 34 строк дословно; на 50 строках одно расхождение, и оно ушло оператору вопросом.
  • Треки. Независимый заказ совпал по всем трём трекам; сравнение на 970 заказах затронуло 2, изменений при том же ключе 0.
  • Детектор. 726 из 924 заказов проходят как «отгружен целиком», поэтому единственная находка говорит о настоящем покрытии.

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

05 · Применимость

Где ещё ложится та же методология

За кейсом стоит типовая задача «отдать данные подрядчику в его формате и принять его ответ обратно в живую таблицу». Подрядчик работает в Excel, таблица на нашей стороне живая: формулы, ручные правки, свой порядок строк. Тот же контур ложится на:

  • Перевозчики и внешние склады → реестр отправок: файл партии туда, треки и тарифы обратно в строки
  • Бухгалтерия на аутсорсе → реестр операций на проверку, обратно исправленные суммы и комментарии в те же строки
  • Поставщики и закупки → заявка в шаблоне поставщика, обратно подтверждённые цены, сроки и остатки
  • Подрядчики по контенту и переводам → выгрузка карточек в xlsx, обратно заполненные колонки без сдвига строк
  • Любой обмен «наш файл → чужие руки → наш файл», где ответ обязан встать в нужную строку при нетронутом ручном вводе людей
Что переиспользуется на следующих проектах
  • Раскраска книги, вычисленная на нашей стороне из её же правил, и настоящее условное форматирование в xlsx, которое гаснет при вводе
  • Точный ключ «номер + суффикс строки» и поиск по всей книге; сопоставление по названиям исключено
  • Признак свободной ячейки по тексту исходной формулы там, где ячейка никогда не пуста
  • Правило «раскладывать только при равном счёте, иначе сказать вслух с фактами»
  • Детектор с двумя независимыми признаками и проверкой покрытия до того, как его результату поверили
  • Режим витрины перед записью и сохранённый список затронутых строк перед любой большой правкой
Похожая задача?

Если ответ подрядчика у вас кто-то разносит по таблице вручную — это снимается

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

Готовы начать?

Аудит за 9 900 ₽ — с конкретным отчётом и сметой

Расскажу что внедрить в вашем бизнесе в первую очередь, какая будет окупаемость, и нужен ли вообще AI для вашей задачи (иногда — нет).

Или просто напишите свой вопрос — отвечу в течение 2 часов