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

Оплаченные заказы Shopify → рабочие таблицы склада, без ручного заноса

Двусторонний обмен между двумя магазинами Shopify и рабочими Google-таблицами склада: заказы, треки, статусы и отмены проставляются сами каждые 30 минут — со сверкой, бэкапами и самолечением.

Отрасль
Интернет-торговля, бренд для активного отдыха из США
Стек
Python · Apps Script · Sheets API
Сроки
≈ 5 недель до боевых книг
Итог
ручной занос → авто каждые 30 мин
01 · Боль

Каждый оплаченный заказ переносился в таблицу руками

Бренд товаров для активного отдыха из США продаёт через два магазина на Shopify, товар едет из Китая. Вся логистика — отгрузки, склады, треки — жила в двух больших Google-таблицах, которые вели двое операционистов. Каждый оплаченный заказ они переносили туда вручную: номер заказа, товар, склад отгрузки, дату, трек-номер.

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

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

02 · Решение

Обмен из двух половин: Python забирает, Apps Script аккуратно кладёт

Половина на Python отвечает за «что записать»: забирает оплаченные заказы обоих магазинов через Shopify Admin API, приводит данные к единому виду и собирает пакет строк. Половина на Google Apps Script отвечает за «как записать так, чтобы человек не заметил вмешательства» — и именно она оказалась сложной частью.

01
Shopify Admin API

Оплаченные заказы двух магазинов: каждые 30 минут забираются только новые

02
Приведение к единому виду

Справочники названий товаров, склад отгрузки, статусы отмен и частичных возвратов

03
Пакет строк

Что вставить, что обновить, что пометить отменённым — решается до записи

04
Запись через Apps Script

Вставка строки в середину таблицы, протяжка формул и форматирования соседних строк

05
Рабочая книга

Операционист видит заказ на своём месте — порядок строк и ручные правки целы

Нормализация на стороне Python

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

Advanced Sheets Service вместо SpreadsheetApp

Первый приёмник был написан на привычном SpreadsheetApp — стандартной объектной модели Apps Script. На таблицах такого размера ему стабильно не хватало памяти. Приёмник переписан на Advanced Sheets Service: вместо обхода ячеек — пакетные запросы к Sheets API. Приёмник дошёл до 87-й версии — почти каждая новая версия закрывала очередное поведение Google, о котором нет ни строчки в документации.

Вставка в середину сетки и протяжка формул

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

С подсветкой отмен вышел отдельный сюжет. Правило условного форматирования было развёрнуто в книге и не загоралось ни разу: основной модуль не передавал приёмнику флаг отмены, поэтому красить было нечего. Масштаб проверили по книге складского учёта — на 16 478 строк недостающий флаг нашёлся ровно на одной. Флаг доезжает, правило загорается, утверждение выше стало верным.

Четыре предохранителя, которые держат бота в стороне от работы человека

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

  • Запись только поверх формулы. Стоимость доставки уходит исключительно в ячейку с нетронутой исходной формулой. Ячейку, в которую человек вписал значение руками, бот обходит стороной.
  • Чтение целевых строк перед записью партии. Если там есть хоть что-то, запись отменяется целиком, в журнал и в чат уходит громкий отказ с номерами строк, а заказы догоняют следующим прогоном. Поводом стал живой случай: второй оператор вписал товар в строку без номера заказа, и через 13 минут бот записал туда свой.
  • Якорь вставки по концу первой непрерывной цепочки. Одна съехавшая строка раньше утаскивала вниз каждый следующий заказ; теперь смещение остаётся локальным.
  • Явный отказ на неизвестное имя действия. Раньше такое имя проваливалось в ветку дописывания строк: опечатка означала бы запись номеров строк в книгу как заказов, и приёмник отвечал бы на это «успешно». Перед вводом предохранителя сверены все 19 значений действий, которые шлёт основной модуль.

Живая проверка после выкатки заняла три запроса: опечатка в имени действия возвращает ошибку, пустое дописывание отвечает «добавлено 0», рабочее действие отрабатывает штатно.

Обратное направление: сверка книги с магазинами — только на чтение

Та же складская книга теперь ежедневно сверяется с магазинами. Экспорт диапазона идёт строго на чтение, и это закреплено двумя тестами по тексту самого модуля: функции записи в нём отсутствуют. Для конвейера, который пишет в живые рабочие таблицы, такая проверка — прямое продолжение темы безопасности записи. Живой прогон: 701 строка, расхождения в пределах порога.

Схлопывание перекрывающихся правил валидации

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

Обвязка эксплуатации: восемь таймеров

Обмен работает без присмотра, поэтому вокруг него собран контур эксплуатации на systemd-таймерах:

  • Постинг — каждые 30 минут, новые и изменившиеся заказы
  • Экспорт файла дня — сетка проверки доведена до 5 минут, ожидание файла сократилось примерно с 50 минут до 15
  • Приём входящих файлов от партнёра по отгрузке — каждые 15 минут в окне 7–22 МСК, файлы забираются и из темы группы, и из личной переписки
  • Пересборка витрины названий — около 187 успешных сборок в сутки двумя контурами
  • Healthcheck — жив ли контур, доезжают ли записи
  • Суточная сверка — построчное сравнение книги с Shopify
  • Ночной бэкап — снимок книг до любых записей следующего дня
  • Прогон тестов приёмника — на реальном окружении

Ускорение файла дня стоит пояснить: к пятиминутной сетке добавлена команда в чате, которая собирает срез немедленно, а нагрузку на чтения предварительно измерили — около 380 чтений в сутки, то есть запас до квот остаётся большим. Весь контур этого проекта живёт на сервере, где сейчас 58 таймеров с ежедневной сверкой состояния.

Сверху — уведомления операционисту в Telegram, повторные попытки при сбоях Apps Script и отдельный тест, который падает, если справочник бизнес-правил разошёлся с кодом. Последнее звучит мелко, но именно оно не даёт документации превратиться в художественную литературу.

Самовосстановление, которое уступает человеку

Восстановление потерянных записей умело возвращать строки в книгу и возвращало их в том числе тогда, когда строку намеренно удалил человек. Замер переигровкой на живых снимках дал 67 заказов из 80 возвратов за сутки: в 84% срабатываний бот спорил с человеком.

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

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

Чего стоила проверка сторожей

Контур эксплуатации имеет смысл ровно настолько, насколько проверено, что его сигнал доходит до человека. Проверка сторожей дала три находки.

  • Отказ одного из двух магазинов молча занижал число лишних обещаний по остаткам с 9 до 3. Цифра читалась как хорошая новость, при том что по ней закупают; товар, заведённый только в упавшем магазине, выпадал из выборки целиком.
  • Сторож зависших заказов на рабочей машине был мёртв: под системным планировщиком нет ssh-агента, и сторож писал «источник не отдал заказов, молчу» ровно теми же словами, какими исправный сторож сообщает, что докладывать не о чем. Запущенный вручную, тот же код нашёл настоящие зависшие заказы возрастом 5 и 11 дней. Вылечено переездом на сервер.
  • Монитор сверки складских книг честно считался каждые четыре часа и писал файл, который не открывал ни один человек.

Что показал год эксплуатации

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

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

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

Выкатка по манифестам контрольных сумм. У кода, который пишет в боевые таблицы, цели выкатки не существовало вообще: код возили руками, и на сервере две недели незамеченной пролежала версия без починки распознавания машины. Теперь есть цель: rsync без удаления — рядом с кодом лежат окружение, суточные файлы и ручные резервные копии, — собственный файл манифеста и 62 файла со сверкой всех контрольных сумм. Перед заведением цели проверено направление расхождения по каждому файлу: строк, которые есть на сервере и отсутствуют в репозитории, нет нигде, иначе выкатка молча затёрла бы ручную правку. В следующей выкатке сверка перед заливкой дала 107 совпавших файлов.

Переезд на боевые книги «вторым контуром»

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

03 · Стек

Ничего экзотического — вся сложность в поведении Google

Python + Shopify Admin API

Выгрузка оплаченных заказов обоих магазинов, запросы через urllib без лишних зависимостей

Google Apps Script

Запись в таблицу: вставка в середину, протяжка формул и форматирования

Google Sheets API

Чтение состояния таблицы для сверки и выгрузки, пакетные правки

systemd timers + flock

Восемь расписаний, защита от наложения запусков, оповещения при сбое

Telegram Bot API

Уведомления операционисту: что записано, что разошлось, что упало

pytest

1362 зелёных теста в каталоге отчётности (2,5 недели назад было 1249) и 2472 по всему репозиторию, включая проверку расхождения справочника бизнес-правил с кодом

PythonShopify Admin APIApps ScriptGoogle Sheets APIsystemdTelegram Bot APIpytest
04 · Результат

Сравнение до и после

Перенос заказов
вручную 30 мин

интервал автоматического постинга, оба магазина

Новых ошибок в книге
0

711 проблемных ячеек до запуска бота — и ровно 711 после

Контур эксплуатации
8

таймеров: постинг, экспорт, приём файлов, витрина названий, healthcheck, сверка, бэкап, тесты

Ручной перенос заказов в таблицы исчез как класс работы. Оплаченные заказы обоих магазинов появляются в рабочих книгах сами — вместе с треком, складом, статусом и пометкой отмены, если заказ отменили.

Метрика «ноль новых ошибок» стоит объяснения, потому что она важнее скорости. Перед запуском книга была проинвентаризована: 711 проблемных ячеек — наследие лет ручной работы. После запуска бота проверка повторилась и дала ровно те же 711. Автоматизация не добавила в живой рабочий документ ни одной новой ошибки — при том, что писала в него каждые полчаса.

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

Справочник и цвета доезжают до людей

Три свежие проверки того, что решения оператора действительно доходят до книги в том виде, в каком он их принял.

  • Справочник стал единственным источником правды по названиям. Имя, заданное оператором в справочнике, применялось в книге отгрузок и молча оставалось сырым длинным названием магазина в книге складского учёта: самолечение имён заменяло там 0 ячеек, то есть не срабатывало никогда. Справочник на 260 строк даёт 244 соответствия; после правки обе живые строки приведены к нужному имени, потерь нет.
  • Детектор расхождений «в книге один товар, в магазине другой». Признак — взаимный остаток, имена приводятся через справочник. Прогон на живых данных дал ровно 2 находки на 710 активных заказах и 7680 базовых номерах при нуле ложных. Пометка сделана символом в скрытой колонке и правилом условного форматирования: простую заливку затирает вставка строки с наследованием формата — это прямое продолжение темы кейса.
  • Цвета выпадающих списков. Цветовая метка привязана к точному значению, и расхождение в одну букву гасит цвет. Серых ячеек было 28 из 12 239, и у 20 отличалась ровно одна буква. Теперь бот пишет написание оператора, читая списки самой книги, а поиск нечувствителен к регистру, кириллическим гомоглифам и кратности пробелов. Заодно сняты 39 пустых кремовых пометок из 73 — подсветка снова что-то означает.
Отзыв второго пользователя

«Всё через Shopify — гораздо быстрее и удобнее, стало гораздо легче».

Операционист, подключившийся вторым — после того, как первый отработал на боевых книгах.

Отдельно отмечу срок: около пяти недель от первой рабочей версии до записи в боевые таблицы. Выгрузка заказов из Shopify делается за день; львиная доля срока ушла на то, чтобы запись в живую человеческую таблицу была безопасной.

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

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

За кейсом стоит типовая задача «источник истины в API → живая таблица, в которой работают люди». Она встречается в каждой второй компании, и почти везде её пытались решить дописыванием строк в конец листа — и отказались, потому что это ломает работу:

  • Маркетплейсы (Wildberries, Ozon) → таблицы закупок и поставок, которые ведут менеджеры
  • CRM / 1С → сводные книги руководителя с ручными комментариями и своими формулами
  • Логистика и трекинг — статусы перевозчиков в реестр отгрузок без ручного копирования
  • Платёжные и биллинговые системы → финансовые реестры, где сверка важнее скорости
  • Любая «таблица-легенда», которую годами вёл человек и которую нельзя просто заменить готовым отчётом
Что переиспользуется на следующих проектах
  • Запись через Apps Script: вставка в середину таблицы, протяжка формул и объединение правил проверки данных
  • Справочники названий по каждой таблице, которые правит сам оператор — без обновления кода
  • Ежедневная сверка с главной системой и готовый список расхождений
  • Переезд в два контура: параллельная запись в боевую и эталонную копию, откат — просто выключить расписание
  • Тест, который падает при расхождении справочника бизнес-правил с кодом — документация не устаревает молча
Похожая задача?

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

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

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

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

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

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