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

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

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

Заказы из 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 приводит названия к единому виду

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

Стандартному способу записи не хватало памяти

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

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

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

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

Четыре защиты, которые не дают боту испортить работу человека

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

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

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

Сверка с магазинами только читает таблицу

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

Правила выпадающих списков приходится объединять

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

Восемь таймеров вокруг обмена

Обмен работает без присмотра, поэтому вокруг него настроены задачи по расписанию на таймерах systemd:

  • Запись заказов — каждые 30 минут, новые и изменившиеся заказы
  • Дневной файл для партнёра — проверка каждые 5 минут, ожидание файла сократилось примерно с 50 минут до 15
  • Приём файлов от партнёра по отгрузке — каждые 2 минуты с 7 до 22 по Москве, файлы забираются и из темы группы, и из личной переписки
  • Обновление листа с названиями товаров — около 187 успешных сборок в сутки
  • Проверка работоспособности — работает ли система и доходят ли записи
  • Ежедневная сверка — построчное сравнение таблицы с 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 + flock

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

Telegram Bot API

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

pytest

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

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

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

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

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

Новых ошибок в таблице
0

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

Задачи по расписанию
8

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

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

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

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

Решения оператора доходят до таблицы

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

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

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

Оператор, который перешёл на новую схему работы вторым.

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

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

Где ещё работает тот же подход

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

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

Обновление работы в октябре 2026 года

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

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

Похожая задача?

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

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