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

Разрешить сайту считать ваши посещения с помощью Яндекс Метрики? Для этого она сохранит в браузере файлы cookie. Как сайт использует cookie

VC
Кейс 8 из 22 · Интернет-торговля · операции

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

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

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

Файл для партнёра собирали вручную, ответ разносили по таблице на глаз

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

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

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

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

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

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

02 · Решение

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

Первая часть собирает дневной файл по дате, которую оператор ставит в таблице, и повторяет раскраску таблицы. Вторая разбирает файл партнёра, переносит стоимость доставки в рабочую таблицу и раскладывает трек-номера комплектов по строкам. Записывает в таблицу тот же приёмник на Apps Script, что и в основной выгрузке заказов. Apps Script — это скрипты, которые работают внутри самой Google-таблицы. Здесь описано только то, что связано с партнёром.

01
Дата отправки в таблице

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

02
Дневной файл

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

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

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

04
Разбор ответа

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

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 строки, заполненные вручную, включая все 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 из темы и из личных сообщений

Таймеры systemd

Сторож даты отправки каждые 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, которое гаснет при заполнении
  • Точный ключ «номер + суффикс строки» и поиск по всей таблице, без сопоставления по названиям
  • Признак свободной ячейки по тексту исходной формулы там, где ячейка никогда не бывает пустой
  • Правило «раскладывать только при равном счёте, иначе прямо сообщить с фактами»
  • Проверка с двумя независимыми признаками: сначала подтвердили, что она срабатывает, потом поверили её результату
  • Режим просмотра перед записью и сохранённый список затронутых строк перед любой большой правкой
Похожая задача?

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

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

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

Начните с аудита за 5 000 ₽: письменный отчёт и расчёт окупаемости

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

Или просто задайте вопрос. В рабочее время отвечаю в течение 2 часов