Order breakdown for Webflow SONR Music Shop. Data is based on the selected date range above.
Gross Revenue
$0.00
Total customer paid · incl. shipping & accessories
0
Orders
$0.00
SONR Music revenue
0
SONR Music q-ty
$0.00
Shipping
$0.00
Discount Shipping
$0.00
Tax
$0.00
Discounts
$0.00
Accessories revenue
0
Accessories q-ty
0% fulfilled
0 fulfilled0 unfulfilled
2
Product SONR Music 2
Order breakdown for SONR Music 2. Data is based on the selected date range above.
Gross Revenue
$0.00
Total customer paid · incl. shipping & accessories
0
Orders
$0.00
SONR Music 2 revenue
0
SONR Music 2 q-ty
$0.00
Shipping
$0.00
Discount Shipping
$0.00
Tax
$0.00
Discounts
$0.00
Accessories revenue
0
Accessories q-ty
0% fulfilled
0 fulfilled0 unfulfilled
3
Product SONR Music Multisport
Order breakdown for SONR Music Multisport. Data is based on the selected date range above.
Gross Revenue
$0.00
Total customer paid · incl. shipping & accessories
0
Orders
$0.00
SONR Music Multisport revenue
0
SONR Music Multisport q-ty
$0.00
Shipping
$0.00
Discount Shipping
$0.00
Tax
$0.00
Discounts
$0.00
Accessories revenue
0
Accessories q-ty
0% fulfilled
0 fulfilled0 unfulfilled
4
SONR Accessories
Order breakdown for orders containing Accessories SKUs. Data is based on the selected date range above.
Gross Revenue
$0.00
Total customer paid · orders containing Accessories
0
Orders
$0.00
Accessories revenue
0
Accessories q-ty
$0.00
Shipping
$0.00
Discount Shipping
$0.00
Tax
$0.00
Discounts
0% fulfilled
0 fulfilled0 unfulfilled
5
Other Orders
Orders that did not match any product type in blocks 1–4. May contain untagged SKUs or mixed orders.
6
Products Webflow SONR Coach
Coach-storefront orders flagged as Products In Order = SONR Music on the Coach → Orders Channels tab. Metrics & SKU breakdown count only Music items (and accessories that came along). PIO=Mix orders are excluded.
Gross Revenue
$0.00
Music-items only · prorated shipping/tax/discount
0
Orders
$0.00
SONR Music revenue
0
SONR Music q-ty
$0.00
Shipping
$0.00
Discount Shipping
$0.00
Tax
$0.00
Discounts
$0.00
Accessories revenue
0
Accessories q-ty
0% fulfilled
0 fulfilled0 unfulfilled
7
SONR Orders export
Download a CSV report of Webflow SONR Music orders for a custom date range and selected product types.
Product types
0
Total Orders
0
Fulfilled
0%
0
Unfulfilled
0%
$0.00
Total Revenue
8
SONR Orders export
Download a CSV report of Coach orders for a custom date range and selected channels.
Channels
1
Unassigned Orders
Coach orders without a channel assigned. Select a channel in the dropdown — it will be saved to order_type and moved to the Assigned block below. Click a row to view SKU details.
0 orders
no pending changes
2
Assigned Orders Coach ▸
Coach orders that already have a channel — 0 orders. Click to expand.
3
Assigned Orders SONR Music ▸
Coach orders with Products In Order = SONR Music — 0 orders. Click to expand.
4
Assigned Orders SONR Mix ▸
Coach orders with Products In Order = Mix — 0 orders. Click to expand.
0
Total Orders
0
Fulfilled
0%
0
Unfulfilled
0%
$0.00
Total Revenue
7
SONR Orders export
Download a CSV report of Shopify orders for a custom date range and selected product types.
Product types
0
Total Orders
0
Fulfilled
0%
0
Unfulfilled
0%
$0.00
Total Revenue
7
SONR Orders export
Download a CSV report of Amazon orders for a custom date range and selected product types.
Product types
SONR Orders
Sync
Manage data synchronization from Webflow, Shopify, and Amazon
0
Total Orders
0
Fulfilled
0%
0
Unfulfilled
0%
$0.00
Total Revenue
SONR Music
Revenue$0.00
Orders0
0% fulfilled
0 fulfilled0 unfulfilled
SONR Coach
Revenue$0.00
Orders0
0% fulfilled
0 fulfilled0 unfulfilled
SONR Shopify
Revenue$0.00
Orders0
0% fulfilled
0 fulfilled0 unfulfilled
SONR Amazon
Revenue$0.00
Orders0
0% fulfilled
0 fulfilled0 unfulfilled
Loading orders...
Order ID
Customer
Date
Status
Items
Total
📦
No orders found. Import from Webflow first.
SONR Music
Webflow Website
1. Quick Sync
Fetch latest orders from Webflow, transform and update orders.json
2. Auto Sync
Server-side background sync
3. Sync Products
Fetch products from Webflow and save to products.json
SONR Coach
Webflow Website
1. Quick Sync
Fetch latest orders from Webflow, transform and update orders.json
2. Auto Sync
Server-side background sync
3. Sync Products
Fetch products from Webflow and save to products.json
Fetch raw orders from Amazon SP-API into raw_amazon.json
2. Quick Sync
Fetch latest orders from Amazon, transform and update orders.json
3. Auto Sync
Server-side background sync
4. Sync Products
Fetch products from Amazon and save to raw_products (source: Amazon)
SONR Orders
Statistics
Webflow SONR Music · Sales reports for the selected period
—
0
Orders in Range
$0
Total Revenue
$0
Average Order
0
Avg Items / Order
1
Reports
Analytics by product, color, status, country and more
Select order Status:
Product
Orders
Units
Revenue
Status
Orders
Items
%
Revenue
Country
Code
Orders
%
Items
Revenue
Shipping Method
Orders
Items
Total Shipping Cost
Avg Shipping
Payment
Orders
Items
Revenue
SONR Orders
Settings
Manage users, countries & regions, and product catalog
Users
Username
Password
Name
Email
Phone
Role
Add New User
Create a new user account. Username and password are required. The user will be able to log in immediately after creation.
Unique login identifier
Login password
Display name (optional)
Contact email (optional)
Contact phone (optional)
Admin: full access. User: read-only orders
Countries & Regions
Manage shipping regions and their countries. Click a region name to rename it.
Sync from Webflow
Reads raw_products.json and imports products with source Webflow SONR Music into products.json.
New products are added, existing ones are updated. Your category assignments (skuid_product_type) are preserved.
Sync from Webflow — SONR Coach
Reads raw_products and imports products with source Webflow SONR Coach into products.
New products are added, existing ones are updated.
Sync from Shopify
Reads raw_products and imports products with source Shopify into products.
New products are added, existing ones are updated.
Sync from Amazon
Reads raw_products and imports products with source Amazon into products.
New products are added, existing ones are updated. Your skuid_product_type assignments are preserved.
Products
SONR Orders
Wiki
Документация по страницам и фильтрам
≡
Оглавление
Все статьи документации SONR Orders
1
SONR Database Structure
Структура MySQL базы данных — таблицы orders и app_data, поля, логика хранения, API endpoints и примеры данных.
2a
Dashboard · Webflow SONR Music
Аналитика продаж SONR Music Shop — источники данных, KPI-карточки, 6 секций по продуктам (Music, Music 2, Multisport, Accessories, Other, Coach-PIO=Music) + CSV-экспорт.
2b
Dashboard · Webflow SONR Coach
Сводная аналитика по заказам SONR Coach Shop — 4 KPI-карточки: Total Orders, Fulfilled, Unfulfilled, Total Revenue за выбранный период.
3
Sync
Управление синхронизацией данных из Webflow, Shopify и Amazon. Вкладки: Orders (таблица заказов), Webflow, Shopify, Amazon.
4
Statistics
Аналитические отчёты за выбранный период — по продуктам, цвету, статусу, стране, методу доставки и оплате. KPI карточки с агрегатами.
5
Settings
Управление пользователями, регионами и каталогом SKU-продуктов. Три вкладки: Users, Countries & Regions, Products Settings.
1
SONR Database Structure
Структура MySQL базы данных — таблицы, поля, логика хранения и примеры данных.
Обзор
База данных MySQL 8.0 содержит две таблицы:
Таблица
Назначение
orders
Все заказы из всех источников (Webflow, Shopify, Amazon). Каждая строка — один заказ.
app_data
Key-value хранилище для конфигурации приложения: пользователи, каталог продуктов, регионы.
Подключение настраивается через env-переменные. Локально — Docker Compose (docker-compose.yml). На Railway — встроенный MySQL plugin с переменными MYSQLHOST, MYSQLPORT, MYSQLUSER, MYSQLPASSWORD, MYSQLDATABASE. Поддерживается также MYSQL_URL / DATABASE_URL как connection string.
Таблица orders
Хранит все заказы. Индексированные колонки позволяют быстро фильтровать по источнику, статусу и дате. Полный JSON объекта заказа хранится в колонке data.
Колонка
Тип
Описание
order_id
VARCHAR(255) PK
Уникальный идентификатор заказа. Совпадает с orderId в JSON. Primary key — защищает от дублей при upsert.
status
VARCHAR(50)
Статус заказа: fulfilled / unfulfilled. Индексируется для быстрой фильтрации.
Дата принятия заказа в формате ISO 8601, обрезанная до 19 символов (YYYY-MM-DDTHH:MM:SS). Индексируется для фильтрации по периоду.
order_products_in_type
VARCHAR(20)
Тип продуктового состава заказа. Допустимые значения: "SONR Coach", "SONR Music", "Mix". Заполняется приложением (не upstream-синком), сохраняется при ресинке через PRESERVED_ORDER_FIELDS. Индексируется.
data
LONGTEXT
Полный JSON объекта заказа. Все поля, используемые в интерфейсе, читаются отсюда.
Структура JSON объекта заказа (data)
Поле
Тип
Описание
orderId
string
Уникальный ID заказа. Совпадает с order_id в таблице.
status
string
"fulfilled" или "unfulfilled"
webflow
string
Источник заказа
shop
string
Название магазина
acceptedOn
ISO 8601
Дата и время заказа в UTC
customerPaid.value
integer (cents)
Сумма оплаченная клиентом в центах. 499 = $4.99
total
integer (cents)
Fallback для customerPaid.value (используется для Amazon/Shopify заказов)
subtotal
integer (cents)
Сумма позиций после скидок. Может быть меньше суммы item.price × count если скидка встроена напрямую
customer.name
string
Имя покупателя
customer.email
string
Email покупателя
items[]
array
Список позиций заказа (см. ниже)
extras[]
array
Доставка, скидки, налоги (см. ниже)
shipping
object
Адрес доставки, провайдер, трекинг
Структура items[]
Поле
Тип
Описание
variantId
string
ID варианта SKU. Используется как ключ связки с app_data['products']
product
string
Название продукта, напр. "SONR Music x2"
variant
string
Название варианта, напр. "Color: Coral"
price
integer (cents)
Цена за единицу
count
integer
Количество единиц позиции
variantImage.url
string
URL изображения варианта (показывается в SKU списке Dashboard)
Структура extras[]
type
Знак
Описание
"shipping"
+
Стоимость доставки
"discount-shipping"
−
Скидка на доставку (отрицательное значение)
"tax"
+
Налог (всегда положительный)
"discount"
−
Явная скидка на заказ (отрицательное значение)
Пример строки таблицы orders
SQL row example
order_id : "abc123xyz"
status : "fulfilled"
webflow : "SONR Music"
shop : "SONR Music Shop"
accepted_on: "2026-03-15T10:42:00"
data : {"orderId":"abc123xyz","status":"fulfilled","webflow":"SONR Music",
"acceptedOn":"2026-03-15T10:42:00Z","customerPaid":{"value":19900},
"items":[{"variantId":"abc","product":"SONR Music x2",
"variant":"Color: Coral","price":16800,"count":1}],
"extras":[{"type":"shipping","price":3100}],...}
Таблица app_data
Key-value хранилище. Ключ — строка, значение — JSON сериализованный в TEXT. Все операции через ON DUPLICATE KEY UPDATE — безопасный upsert.
Колонка
Тип
Описание
key_name
VARCHAR(100) PK
Имя ключа. Primary key — один ключ одна запись.
value
LONGTEXT
JSON значение. Читается и пишется через JSON.parse / JSON.stringify.
Ключи app_data
key_name
Структура значения
Назначение
users
[{ id, name, email, phone, username, password, role }]
Список пользователей. role: "admin" или "user". Пароли хранятся как есть (без хэширования). Управляется через Settings → User Management.
Каталог продуктов с пользовательскими настройками. skuid_product_type — тип SKU для классификации в Dashboard. skuid_product_color — цвет варианта (Blue/Yellow/Graphite/Coral/Black). skuid_main_product_qty — множитель количества (x2=2, x3=3). Настраивается в Settings → Products Settings.
raw_products
[{ id, name, skus: [{ skuId, ... }] }]
Сырые данные продуктов из Webflow API. Обновляется при "Sync Webflow SONR Music products". Не содержит пользовательских настроек — служит источником для обновления products.
regions
[{ name, countries: [] }]
Список регионов и стран. Используется на странице Statistics для группировки заказов по географии. Если ключ отсутствует при старте — инициализируется дефолтными значениями из server.js.
Пример строки таблицы app_data
SQL row example — ключ regions
key_name: "regions"
value : [{"name":"Europe","countries":["Austria","Belgium","Bulgaria",...]},
{"name":"North America","countries":["United States","Mexico",...]},
...]
Операции с базой данных
Все операции реализованы в db.js через пул соединений (mysql2/promise, лимит 10 соединений).
Функция
Описание
getPool()
Создаёт пул при первом вызове, инициализирует схему таблиц. Используется во всех остальных функциях.
getAllOrders()
Возвращает все заказы из таблицы orders, отсортированные по accepted_on DESC. Парсит data JSON каждой строки.
getOrderCount()
Возвращает количество заказов в таблице. Используется при старте сервера для логирования.
upsertOrders(orders[])
Вставляет или обновляет массив заказов в транзакции. affectedRows=1 → новый заказ, =2 → обновлён существующий. Возвращает { added, updated }.
getData(key)
Читает значение из app_data по ключу. Возвращает распарсенный объект или null.
setData(key, value)
Записывает значение в app_data (upsert). Сериализует через JSON.stringify.
migrateFromFiles(dataDir)
Первый запуск: если таблицы пустые — импортирует данные из JSON файлов в data/. Запускается однократно при старте сервера.
Endpoints API
Endpoint
Метод
Описание
/api/orders
GET
Возвращает все заказы из DB как JSON массив. Основной источник данных для всех страниц.
/data/orders.json
GET
Алиас для /api/orders — для обратной совместимости.
/products
GET
Возвращает app_data['products'].
/save-orders
POST
Принимает массив заказов, выполняет upsert через db.upsertOrders().
/users
GET
Возвращает app_data['users'].
/save-users
POST
Сохраняет массив пользователей в app_data['users'].
2
Dashboard
Страница аналитики продаж. Содержит две вкладки: Webflow SONR Music и Webflow SONR Coach.
Структура страницы
В шапке — hero-баннер с выбором периода (From / To + кнопка Apply). Под баннером — переключатель вкладок. Apply применяет выбранный период к активной вкладке.
Вкладка
Источник данных
Содержимое
Webflow SONR Music
webflow === "SONR Music" & shop === "SONR Music Shop" + одна доп. секция из Coach orders с order_products_in_type === "SONR Music"
KPI-карточки + 6 секций по продуктам + SKU-детализация + CSV-экспорт
Аналитика продаж SONR Music Shop — источники данных, KPI-карточки, 6 секций по продуктам (последняя — Coach-storefront заказы помеченные как PIO=SONR Music), SKU-детализация, CSV-экспорт.
Источники данных
MySQL · таблица orders
Основное хранилище заказов. Каждая строка — один заказ. Данные загружаются через GET /api/orders. Колонка data содержит полный JSON объекта заказа.
Дата заказа — фильтрация по периоду (берётся slice 0–10, UTC)
webflow
string
Источник — фильтр "SONR Music"
shop
string
Магазин — фильтр "SONR Music Shop"
status
string
Статус заказа — fulfilled / unfulfilled
customerPaid.value
integer (cents)
Сумма оплаты клиентом — Total Revenue, Gross Revenue
items[].variantId
string
ID варианта SKU — связка с каталогом продуктов в DB
items[].price
integer (cents)
Цена позиции (line total) — SONR Music revenue, Accessories revenue
items[].count
integer
Количество единиц позиции
extras[].type
string
Тип позиции — "shipping", "discount-shipping", "tax", "discount"
extras[].price
integer (cents)
Стоимость позиции. Скидки отрицательные. Налоги всегда положительные
subtotal
integer (cents)
Сумма позиций после скидок
MySQL · app_data['products']
Каталог продуктов с пользовательскими настройками. Загружается через GET /products при старте страницы. Используется для классификации SKU в заказах.
Поле JSON
Тип
Где используется
skus[].skuId
string
Совпадает с items[].variantId в заказе — ключ связки
skus[].skuid_product_type
string
Тип SKU — фильтр секций и группировка
skus[].skuid_main_product_qty
integer
Количество устройств в одном SKU — множитель для подсчёта Units
Настраивается на странице Settings → Products Settings.
Выбор периода
Поля From и To в hero-баннере. По умолчанию — 2026-03-01 → 2026-03-31. После изменения нажмите Apply — все блоки пересчитываются.
Фильтрация по UTC-дате: из acceptedOn берётся slice(0, 10). Базовый фильтр: webflow === "SONR Music" и shop === "SONR Music Shop".
KPI-карточки
Четыре карточки — сводка по всем заказам SONR Music Shop за период.
Карточка
Логика расчёта
Total Orders
Количество заказов за период
Fulfilled
Заказы со status === "fulfilled" + % от Total Orders
Unfulfilled
Остальные заказы + % от Total Orders
Total Revenue
Сумма customerPaid.value — включает товары, доставку и налоги
Расчёт блоков 1–4 · item-by-item с пропорциональным распределением
Блоки SONR Music, SONR Music 2, SONR Music Multisport, SONR Accessories используют единую формулу, реализованную в computeMusicBlockMetrics(orders, mainType):
Quaiifying orders — заказы где есть хотя бы один SKU нужного типа (Music / Music 2 / Multisport / Accessories соответственно)
In-block items для блока 1–3 = items типа mainType + items типа "Accessories". Для блока 4 — только "Accessories"
Block share для каждого заказа = (Σ price in-block items) / (Σ price всех items). Если все items в заказе принадлежат блоку — share=1, иначе меньше
Product / Accessories revenue, q-ty — только in-block items
Shipping / Discount Shipping / Tax / Discounts — соответствующий extras[].price × share для каждого заказа
Если в заказе есть позиция другого типа (например, регулярный SONR Music внутри Music 2-заказа) — её цена и доля extras уйдут в свой блок, а в текущий не попадут. Items без типа в каталоге попадут в Block 5 (Other Orders).
Секция 1 · Product SONR Music
Включает заказы с хотя бы одним SKU типа "SONR Music". Связь: items[].variantId = skus[].skuId в каталоге.
Заказы с хотя бы одним SKU типа "SONR Music Multisport". Логика идентична Секциям 1 и 2.
Секция 4 · SONR Accessories
Заказы с типом "Accessories" и без SONR Music / Music 2 / Multisport SKU — чисто аксессуарные заказы.
Метрика
Логика расчёта
Gross Revenue
Сумма customerPaid.value
Orders
Количество уникальных заказов
Accessories revenue / q-ty
Сумма item.price / item.count для типа "Accessories"
Shipping / Discount Shipping / Tax / Discounts
Аналогично Секции 1
Секция 5 · Other Orders
Все items из musicShopOrders, чей тип не входит в блоки 1–4 ("SONR Music", "SONR Music 2", "SONR Music Multisport", "Accessories"). Сюда попадают незатегованные SKU и SKU других линеек. Один заказ может одновременно быть в Block 1 и в Block 5.
Зачем: это музыкальные продажи, которые физически прошли через Coach storefront. Видеть их хочется на Music-дашборде вместе с обычными Music-продажами.
Метрики: переиспользуют тот же computeMusicBlockMetrics(coachMusicOrders, "SONR Music") что и Секция 1 — то есть в Product Revenue / Product q-ty попадают только items с skuid_product_type === "SONR Music" (Coach- / Mix- / прочие SKU в этих заказах игнорируются). Tile Accessories revenue / q-ty учитывает аксессуары пришедшие в тех же заказах. Shipping / tax / discount / discount-shipping проратируются по доле Music-items в заказе.
PIO=Mix заказы намеренно исключены — попадают только заказы с PIO ровно равным "SONR Music".
SKU breakdown: две группы — "SONR Music (from Coach orders)" и "SONR Accessories (from Coach orders)". Уникальные groupId и offset чтобы не было коллизий с Секцией 1.
Секция 7 · SONR Orders export
CSV-экспорт по типу заказа. Диапазон дат берётся из top-of-page dashboard-хедера (dashDateFrom / dashDateTo) — отдельных date-input'ов в форме нет, чтобы пользоваться одним общим источником истины и не дублировать UI. Если в хедере не выбран период — кнопка возвращает status-ошибку.
Параметры формы:
Types — 6 chip-чекбоксов (SONR Music / Music 2 / Multisport / Accessories / Other / Coach (Music)) + мастер All
Источники заказов:
webflow="SONR Music" && shop="SONR Music Shop" — основная Music-storefront выборка (первые 5 типов)
webflow="SONR Coach" && shop="SONR Coach Shop" && order_products_in_type="SONR Music" — попадают в выборку только когда активен chip Coach (Music) (data-type=__coach_music__)
Тип заказа = синтетический __coach_music__ для Coach-PIO=Music, иначе тип первого counted item, иначе __other__. В CSV-колонке Type: "Coach (Music)" / "Other" / "SONR Music" и т.д. Колонки: Order ID, Date, Status, Type, Customer, Email, Items, Promo, Subtotal, Total, Shipping, Tax, Discount, Payment. Имя файла: sonr-music-orders-{from}-{to}.csv.
Блок SKU — структура
После каждой секции метрик — два сворачиваемых списка SKU (основной продукт + аксессуары из тех же заказов). Кликните на заголовок группы чтобы раскрыть, на строку SKU — чтобы развернуть таблицу заказов.
Колонка
Описание
Units
skuid_main_product_qty × item.count для основного продукта; item.count для аксессуаров
Tax
Сумма extras[type="tax"] из всех заказов SKU; — если нет
Revenue
Сумма item.price — line total, count не применяется
SKU summary row (7 tile'ов справа от названия): Units · Total paid · Price (product) · Discount · Disc. Shipping · Tax · Shipping. Каждое значение — сумма соответствующего поля по всем item-rows этого SKU (та же логика что у detail-rows). Discount/Disc. Shipping показывают "—" если сумма ≥ 0.
Таблица заказов (12 колонок): Order ID · Customer · Variant · Status · Date · Units · Total paid · Price (product) · Discount · Discount Shipping · Tax · Shipping. Семантика:
Units — item.count × (skuid_main_product_qty ?? 1)
Total paid — order-level customerPaid.value (одно значение на весь заказ — для всех item-rows одно)
Price (product) — item.price + prorated_discount (нетто после прорированной доли order-level discount по `item.price / Σ item.price`)
Поля From и To в hero-баннере общие для обоих под-вкладок. По умолчанию — 2026-03-01 → 2026-03-31. После изменения нажмите Apply — все блоки и список Orders Channels пересчитываются.
Фильтрация по UTC-дате: acceptedOn.slice(0, 10). Базовый фильтр всех блоков: webflow === "SONR Coach" AND shop === "SONR Coach Shop".
Под-вкладки
Страница Coach разделена на 2 под-вкладки (pill-tabs):
2 блока для ручного присвоения order_type Coach-заказам
Sub-tab: Overview — KPI-карточки
Четыре карточки — сводка по всем Coach-заказам за период.
Карточка
Логика расчёта
Total Orders
Количество заказов с webflow === "SONR Coach" AND shop === "SONR Coach Shop" за период
Fulfilled
Заказы со status === "fulfilled" + % от Total Orders
Unfulfilled
Остальные заказы + % от Total Orders
Total Revenue
Σ customerPaid.value — включает товары, доставку и налоги
Sub-tab: Overview — Классификация в 7 блоков
Каждый Coach-заказ попадает ровно в один блок. Алгоритм классификации (приоритет сверху вниз) реализован в classifyCoachOrder(order):
Если всеitems в заказе имеют skuid_product_type === "Accessories" → блок 6 SONR Accessories
Иначе если order_type в [Direct, Direct Customers, B2B, B2B Customers, Dealers] → соответствующий блок 1–5
Иначе → блок 7 Other Orders (без order_type или со значением вне списка 5 каналов)
Сумма заказов всех 7 блоков = Total Orders из KPI-карточек.
#
Блок
Условие попадания
1
Direct
order_type === "Direct" и хотя бы один не-Accessories item
2
Direct Customers
order_type === "Direct Customers"
3
B2B
order_type === "B2B"
4
B2B Customers
order_type === "B2B Customers"
5
Dealers
order_type === "Dealers"
6
SONR Accessories
Все items имеют тип "Accessories" (приоритет над order_type)
7
Direct Customers + SONR Accessories
Combined view — заказы из блоков 2 и 6, метрики просуммированы. Заказы остаются в исходных блоках
8
Other Orders
Без order_type или со значением вне 5 каналов
Combined views (#7) — это read-only агрегация. Заказы из Direct Customers (блок 2) и SONR Accessories (блок 6) собираются вместе и через тот же computeCoachBlockMetrics считаются общие метрики. Orders count = sum обоих блоков. SKU-разбивка показывает все SKU (Coach + Accessories вместе).
Метрики каждого блока
В каждом блоке отображается единый набор тайлов (некоторые скрыты в блоках 6 и 7 — см. ниже). Расчёт реализован в computeCoachBlockMetrics(blockOrders).
Тайл
Формула
Gross Revenue (hero)
Σ customerPaid.value — полная сумма заказа
Orders
Количество заказов в блоке
Product q-ty
Σ item.count × skuid_main_product_qty для всех items с типом ≠ "Accessories" (если skuid_main_product_qty не задан — берётся 1)
SONR Coach revenue only
Σ item.price где SKU имеет source === "Webflow SONR Coach" AND skuid_product_type === "SONR Coach" — узкий фильтр, только настоящие Coach-продукты
SONR Receiver q-ty
Σ item.count × skuid_receivers_count для SKU с source === "Webflow SONR Coach"
SONR Radio q-ty
Σ item.count × skuid_radio_count для SKU с source === "Webflow SONR Coach"
Shipping
Σ extras[].price где type.includes("shipping") — net (включает discount-shipping)
Discount Shipping
Σ extras[].price где type === "discount-shipping" (отображается отдельно, уже учтено в Shipping)
Tax
Σ extras[].price где type === "tax"
Discounts
Σ extras[].price где type === "discount"
Accessories revenue
Σ item.price для items с skuid_product_type === "Accessories"
SONR Coach revenue only, SONR Receiver q-ty, SONR Radio q-ty
Все остальные тайлы (Coach-only метрики бессмысленны для этого блока)
Прогресс-бар
Внизу каждого блока — процент fulfilled. Справа абсолютные значения fulfilled и unfulfilled.
SKU-разбивка
Под прогресс-баром — сворачиваемый список SKU из заказов блока. Для блока 6 SONR Accessories — только Accessory SKU. Для блоков 1–5 и 7 — non-Accessory SKU. Клик на строку SKU разворачивает список заказов с Order ID, Customer, Variant, Status, Date, Price.
Блок 8 · SONR Orders export
Под 7 блоками — секция для выгрузки CSV-отчёта по Coach-заказам. Диапазон дат берётся из top-of-page dashboard-хедера (dashDateFrom / dashDateTo) — отдельных date-input'ов в форме нет, чтобы не дублировать UI и иметь один источник истины. Если в хедере не выбран период — кнопка возвращает status-ошибку.
Параметры формы
Channels — 7 chip-чекбоксов (Direct / Direct Customers / B2B / B2B Customers / Dealers / SONR Accessories / Other Orders) + мастер All
All — заполненный navy-чип, переключает все 7 chip'ов разом. Если отжать любой chip — All автоматически отжимается
Логика фильтра
Заказ попадает в выгрузку если: webflow === "SONR Coach" AND shop === "SONR Coach Shop" AND acceptedOn в диапазоне AND classifyCoachOrder(order) ∈ выбранные каналы.
CSV-колонки
Колонка
Источник
Order ID
orderId
Date
acceptedOn (форматированная)
Status
status
Channel
Результат classifyCoachOrder(order) (Direct / SONR Accessories / Other Orders / etc.)
Customer / Email
customer.name / customer.email
Items
Конкатенация "Product (variant) ×count" через "; "
Promo
Извлекается из extras[].name — формат "Discount (CODE)" → CODE
Subtotal / Total
В формате $X.XX
Shipping
Net shipping (Σ extras с type.includes("shipping"))
Tax / Discount
Σ соответствующих extras[]
Payment
order.payment
Имя файла: sonr-coach-orders-{from}-{to}.csv. Если фильтры не дали результатов — статус "No orders match the selected filters", файл не скачивается.
Sub-tab: Orders Channels
Позволяет вручную присвоить канал каждому Coach-заказу. Значение сохраняется в поле order_type через POST /orders/set-order-type (surgical UPDATE — поле сохраняется при последующих Quick/Auto Sync через db.upsertOrders preservation). Список фильтруется по дате из Dashboard-хедера.
Возможные значения order_type
Channel
Описание
Direct
Прямые продажи
Direct Customers
Прямые клиенты
B2B
B2B-продажи
B2B Customers
B2B-клиенты
Dealers
Дилеры
none
Канал явно не применим. В отличие от пустого значения — заказ помечается как "проверено и канала нет", уходит из Unassigned в Assigned Orders Coach. На Overview-вкладке такие заказы попадают в блок "Other Orders" (т.к. "none" не входит в COACH_KNOWN_CHANNELS).
Блок 1 · Unassigned Orders
Список заказов у которых не задано ниorder_type, ниorder_products_in_type. Колонки: Order ID · Date · Status · Customer · Promo · Amount · Channel · Products In Order. Promo извлекается из extras[].name (regex \(([^)]+)\)).
Batch-режим: в отличие от блоков 2/3/4 (где dropdown сохраняет значение сразу), правки в Unassigned накапливаются в pending state — строка подсвечивается тёплым жёлтым, в тулбаре над списком обновляется счётчик "N pending changes". Кнопка Submit changes сохраняет все изменения параллельно (Promise.all) через POST /orders/set-order-type и POST /orders/set-products-in-type, после чего вызывается renderCoachChannels() и заказы автоматически переезжают в Блок 2 (если задан Channel), Блок 3 (если PIO=SONR Music) или Блок 4 (если PIO=Mix). Если задано и то и другое — заказ появится сразу в нескольких блоках, это разные ортогональные классификации.
Блок 2 · Assigned Orders Coach
Свёрнут по умолчанию — кликом на заголовок раскрывается. Показывает заказы у которых order_type уже задан. В dropdown можно изменить канал или сбросить (выбрать "— select —" — в этом случае поле полностью удаляется из заказа через db.setOrderField).
Блок 3 · Assigned Orders SONR Music
Свёрнут по умолчанию. Фильтр: order_products_in_type === "SONR Music". Заказ может одновременно присутствовать в Блоке 2 (если задан order_type) и в Блоке 3 — это разные ортогональные классификации.
Блок 4 · Assigned Orders SONR Mix
Свёрнут по умолчанию. Фильтр: order_products_in_type === "Mix". Та же логика пересечения с Блоком 2 что и у Блока 3.
Products In Order — допустимые значения
Value
Блок
Описание
SONR Coach
—
В заказе только Coach-продукты (отдельного блока пока нет)
SONR Music
Блок 3
В заказе только Music-продукты
Mix
Блок 4
В заказе и Coach, и Music продукты одновременно
Row expand · детали заказа
Клик по строке (вне dropdown) разворачивает панель с деталями:
Shipping — полный адрес (line1, city, state, postalCode, country), метод доставки
Promo code — извлекается из extras[].name (формат Discount (CODE))
Subtotal · Total — суммы по заказу
Extras — все записи extras[] (shipping / tax / discount / discount-shipping) с ценами; отрицательные — красным
Повторный клик на строку сворачивает панель. Доступно с клавиатуры: Enter / Space на сфокусированной строке.
API
Endpoint
Body
Эффект
POST /orders/set-order-type
{ orderId, order_type }
Surgical UPDATE поля order_type в JSON-data заказа. Пустое значение — JSON_REMOVE (поле удаляется). 404 если заказ не найден.
POST /orders/set-products-in-type
{ orderId, order_products_in_type }
Surgical UPDATE поля order_products_in_type + одноимённой физической колонки в одной транзакции. Допустимые значения: "SONR Coach", "SONR Music", "Mix" (валидация на сервере, иначе 400). Пустое значение — поле удаляется и колонка обнуляется. 404 если заказ не найден.
Сохранение через re-sync
Поля order_type и order_products_in_type входят в PRESERVED_ORDER_FIELDS в db.js и sync-db.js. При Quick / Auto Sync с Webflow существующие пользовательские поля автоматически re-attach'атся к incoming payload — так что заказы не теряют каналы и тип-продуктов при обновлении статуса с upstream.
2c
Dashboard · Shopify
Аналитика продаж SONR Shopify Store — KPI, 5 блоков по типам продуктов + Other Orders + CSV-экспорт.
Источник данных
Заказы с webflow === "SONR Shopify" AND shop === "SONR Shopify Store". Дата заказа: acceptedOn.slice(0,10). По умолчанию открывается за текущий месяц.
Поле JSON
Тип
Заметка
orderId
string
Формат "shopify-{id}"
status
string
"fulfilled" / "pending"
acceptedOn
ISO 8601
Дата создания заказа
customerPaid.value / total
integer (cents)
Gross — Shopify заказы используют total (не customerPaid)
items[].variantId
string
Формат "shopify-{variant_id}" — связка со skus[].skuId в каталоге продуктов
items[].price
integer (cents)
Line total (per-unit × quantity) — единый формат с Webflow
items[].count
integer
Quantity
extras[]
array
Синтезируется из Shopify полей shipping_lines, total_tax, total_discounts в формат {type, name, price}
Важно: новые поля variantId и extras[] в Shopify заказах появились вместе с этой страницей. Существующие заказы получат их только после Quick Sync через Sync → Shopify → 2. Quick Sync.
KPI-карточки
Четыре карточки — сводка по всем Shopify-заказам за период.
Карточка
Логика
Total Orders
Количество заказов
Fulfilled
status === "fulfilled" + % от Total
Unfulfilled
Остальные + % от Total
Total Revenue
Σ customerPaid.value ?? total
Блоки 1–5 · по типу продукта
Каждый блок использует общий computeMusicBlockMetrics(orders, mainType) — тот же что для Music: per-item revenue + extras prorated by block share. Tile balance сохраняется (Gross = Product + Accessories + Shipping + Tax + Discounts).
#
Блок
mainType
Условие попадания
1
SONR Music
"SONR Music"
Заказ содержит ≥1 item с этим типом
2
SONR Music 2
"SONR Music 2"
...
3
SONR Music Multisport
"SONR Music Multisport"
...
4
SONR Coach
"SONR Coach"
Заказ содержит ≥1 SONR Coach SKU
5
SONR Accessories Only
"Accessories"
Все items — Accessories AND нет SONR Music/Music 2/Multisport/Coach
Для блоков 1–4 in-block items = {mainType, "Accessories"} — accessories из тех же заказов учитываются в этих блоках. Для блока 5 — только Accessories. Items, не попавшие ни в один блок, идут в Block 6 Other Orders.
Items из Shopify-заказов, чей skuid_product_type не входит в {SONR Music, SONR Music 2, SONR Music Multisport, SONR Coach, Accessories} или вообще отсутствует в каталоге. Колонки: Order ID · Date · Status · Product · Variant · Type · Qty · Revenue. Клик по строке раскрывает полные детали заказа.
Чтобы убрать item из Block 6 — задай ему skuid_product_type в Settings → Products Settings → Shopify. Если SKU вообще нет в каталоге — сначала Sync → Shopify → 4. Sync Products чтобы подтянуть его в raw_products, потом Settings → Products Settings → Sync Shopify products.
Блок 7 · SONR Orders export
CSV-экспорт по типу продукта. Диапазон дат — из top-of-page dashboard-хедера (dashDateFrom / dashDateTo); отдельных date-input'ов в форме нет.
Types — 6 chip-чекбоксов (SONR Music / Music 2 / Multisport / Coach / Accessories / Other) + мастер All
Канал заказа определяется как тип первого counted item (или "Other" если такого нет). CSV-колонки: Order ID, Date, Status, Type, Customer, Email, Items, Subtotal, Total, Shipping, Tax, Discount, Payment. Имя файла: sonr-shopify-orders-{from}-{to}.csv.
2d
Dashboard · Amazon
Аналитика продаж SONR Amazon Store — KPI, 5 блоков по типам продуктов + Other Orders + CSV-экспорт.
Источники данных
Все 7 блоков работают над одним базовым набором: orders в MySQL, отфильтрованные по:
o.webflow === "SONR Amazon"
&& o.shop === "SONR Amazon Store"
&& o.acceptedOn в диапазоне дат из top-of-page header'а
Этот набор передаётся в renderAmazonBlocks(amzOrders), который строит все 6 блоков с метриками и SKU breakdown.
Items в Amazon-заказах
Amazon SP-API возвращает orders и orderItems отдельно. При Quick Sync для каждого заказа дополнительно вызывается /orders/v0/orders/{AmazonOrderId}/orderItems (rate-limit 0.5 req/sec, burst 30). Items маппятся в наш формат через transformAmazonOrder:
Поле
Источник Amazon
productId
"amazon-" + ASIN (например amazon-B0F9FRTLDY)
variantId
"amazon-" + SellerSKU — это ключ для матчинга с catalog
variantSKU
Сырой SellerSKU
asin
Сырой ASIN
product
Item.Title
count
QuantityOrdered
price
ItemPrice.Amount × 100 (центы)
Backfill для исторических заказов с пустым items[] — endpoint POST /orders/amazon-backfill-items. Тянет orderItems для каждого пустого заказа, идемпотентный (повторный запуск пропускает уже заполненные), incremental persist каждые 25 заказов, retry-on-error с экспоненциальным backoff'ом. Для 643 заказов — ~18-20 минут.
Каталог продуктов
Амазон-листинги хранятся в app_data['products'] с source: "Amazon". skuId в каталоге равен variantId в order items, что позволяет dashboard'у через skuMap[item.variantId]?.skuid_product_type определить тип продукта. Если у каталог-SKU не задан skuid_product_type — item уйдёт в Block 6 "Other Orders".
KPI Row · 4 карточки
Считаются один раз для всех Amazon-заказов в выбранном диапазоне (до фильтрации по типам).
Карточка
Формула
Total Orders
amzOrders.length
Fulfilled
Заказы с status === "fulfilled"; справа — % от total
Unfulfilled
Total − Fulfilled; справа — %
Total Revenue
Σ (customerPaid.value ?? total) — сумма customerPaid (если есть, иначе fallback на total)
Блок 1 · SONR Music
Фильтр заказа: заказ попадает в блок если в его items[] есть хотя бы один item чей skuMap[item.variantId]?.skuid_product_type === "SONR Music".
Σ item.price и Σ count для Accessories items в этих же заказах (т.е. accessories которые приехали вместе с Music-продуктом)
Прорация extras: shipping/tax/discount/discount-shipping order-level. Доля блока = (Σ price items типа SONR Music + Accessories) / (Σ price всех items в заказе). То есть если в заказе SONR Music + Coach по 50% стоимости — каждый блок получает 50% доставки.
Progress bar
Под метриками — % fulfilled (fulfilled / blockOrders.length × 100) с цветными счётчиками N fulfilled / N unfulfilled.
SKU breakdown
Под progress-bar — два сворачиваемых списка SKU:
SONR Music — все items типа "SONR Music" из заказов блока
Accessories — все Accessories items из заказов блока
Каждая SKU-строка раскрывается по клику и показывает все item-rows этого SKU с 12 колонками (Order ID · Customer · Variant · Status · Date · Units · Total paid · Price (product) · Discount · Discount Shipping · Tax · Shipping). Подробнее — статья про Music dashboard.
Блок 2 · SONR Music 2
Идентичная Блоку 1 логика, только type = "SONR Music 2". Заказ попадает если ≥1 item с skuid_product_type === "SONR Music 2". Метрики через тот же computeMusicBlockMetrics(blockOrders, "SONR Music 2"). SKU breakdown показывает Music 2 SKUs + accessories.
Блок 3 · SONR Music Multisport
То же что Блок 1/2 для type "SONR Music Multisport". Метрики и SKU breakdown аналогичны.
Блок 4 · SONR Accessories
Этот блок ведёт себя иначе — он показывает заказы где аксессуары пришли сами по себе, без основного продукта.
Где AMAZON_PRODUCT_MAIN_TYPES = {"SONR Music", "SONR Music 2", "SONR Music Multisport", "SONR Coach"}.
Зачем такой фильтр
Аксессуары которые приехали с основным продуктом (Music/Coach/etc) уже учитываются в блоке этого продукта (tile "Accessories revenue/q-ty"). Block 4 показывает только чистые accessory-only заказы — кто-то купил исключительно аксессуар.
Метрики
computeMusicBlockMetrics(blockOrders, "Accessories") с isAccBlock = true:
Product Revenue / q-ty tiles скрыты (для accessory-only они бессмысленны)
Tile "Accessories revenue/q-ty" тоже скрыт — здесь Accessories и есть основной продукт
SKU breakdown показывает только Accessories SKUs.
Блок 5 · SONR Coach
Идентичная Блоку 1 логика для type = "SONR Coach". Заказ попадает если ≥1 item с skuid_product_type === "SONR Coach". Метрики через computeMusicBlockMetrics(blockOrders, "SONR Coach"). SKU breakdown — Coach SKUs + accessories.
Блок 6 · Other Orders
Это item-level dump — не агрегированные заказы, а конкретные позиции которые "потерялись".
Что туда попадает
Items из всех Amazon-заказов где:
const type = skuMap[item.variantId]?.skuid_product_type;
if (!type || !AMAZON_COUNTED_TYPES.has(type)) {
uncountedItems.push({ order, item, type: type || "—" });
}
То есть item попадает если:
SKU нет в каталоге (skuMap[item.variantId] = undefined) — typically значит что нужно сделать Sync → Amazon → Sync Products чтобы подтянуть свежие листинги, потом Settings → Products → Sync Amazon products
SKU есть, но без типа (skuid_product_type = пусто) — нужно зайти в Settings → Products → Amazon и проставить тип
SKU с типом вне 5 counted-types (например "Music Streaming" или "Others") — будет в Block 6 пока не переклассифицирован
AMAZON_COUNTED_TYPES = {"SONR Music", "SONR Music 2", "SONR Music Multisport", "SONR Coach", "Accessories"}.
Отображение
Используется тот же helper renderDashOtherSkus что у Music Block 5. Это таблица с колонками: Order ID · Date · Status · Product · Variant · Type · Qty · Revenue. Клик по строке раскрывает полные детали заказа (items, customer, shipping, extras).
Settings → Products Settings → Sync Amazon products — перенести в рабочий products
Settings → Products → Amazon — выбрать skuid_product_type для каждого ASIN. После save — заказы автоматически переедут в нужный блок 1–5 при следующем рендере dashboard'а
Блок 7 · SONR Orders export
CSV-экспорт по типу продукта. Диапазон дат — из top-of-page dashboard-хедера (dashDateFrom / dashDateTo); отдельных date-input'ов в форме нет.
Types — 6 chip-чекбоксов (SONR Music / SONR Music 2 / SONR Music Multisport / SONR Accessories / SONR Coach / Other) + мастер All (заполненный navy при активности)
Логика фильтра экспорта
Заказ попадает в выгрузку если выполняется ОБА условия:
И один из двух матчей: (а) хотя бы один item имеет skuid_product_type ∈ выбранные chip'ы, ИЛИ (б) primary type заказа (тип первого counted item, иначе __other__) ∈ выбранные chip'ы
CSV-колонки:Order ID, Date, Status, Type, Customer, Email, Items, Subtotal, Total, Shipping, Tax, Discount, Payment. Type = primary type заказа (или "Other" для __other__). Items = склейка "Product (variant) ×count" через "; ". Filename:sonr-amazon-orders-{from}-{to}.csv.
Per-block Export-кнопки
В шапке каждого блока 1–6 — outlined-navy Export кнопка с download-иконкой (рядом с Wiki-кнопкой). Клик скачивает CSV только этого блока за период из dashboard-header'а. Реализована через delegated event handler с уникальными data-amazon-export-type / data-amazon-export-slug атрибутами (не конфликтует с Music dashboard'ом который использует data-export-type).
Settings → Products → Amazon tab — для каждого ASIN dropdown'ом проставить skuid_product_type: SONR Music / SONR Music 2 / Multisport / Accessories / SONR Coach
Save changes
Открыть Dashboard → Amazon — все заказы автоматически распределены по блокам
Если в БД уже есть Amazon-заказы с пустым items[] (импорт делался до фикса import flow), нужно дополнительно дёрнуть POST /orders/amazon-backfill-items — иначе заказы попадут в Block 6 (нет items для матчинга). Backfill идемпотентный, безопасно повторно запускать.
3
Sync
Управление синхронизацией данных из всех источников.
Обзор
Страница Sync управляет импортом и синхронизацией заказов из всех подключённых источников данных. Состоит из четырёх вкладок:
Вкладка
Назначение
Orders
Сводная таблица всех заказов из всех источников. Статистика, фильтры, экспорт CSV.
Webflow
Синхронизация SONR Music и SONR Coach из Webflow E-commerce API.
Shopify
Подключение и синхронизация магазина Shopify через OAuth 2.0.
Amazon
Синхронизация заказов Amazon через SP-API.
Подробное описание каждой вкладки — в отдельных статьях (см. боковое меню).
Sync
Sync · Orders
Сводная таблица всех заказов — просмотр, фильтры, экспорт.
Статистика (Stats Bar)
В верхней части — 4 KPI-карточки по всем источникам (Coach excluded не применяется — учитываются все):
Карточка
Что показывает
Total Orders
Общее число заказов во всей базе
Fulfilled
Количество + % выполненных заказов
Unfulfilled
Количество + % невыполненных заказов
Total Revenue
Суммарная выручка по всем заказам (customerPaid.value)
Ниже — карточки по каждому источнику: SONR Music, SONR Coach, SONR Shopify, SONR Amazon. Каждая карточка показывает Revenue, Orders, прогресс-бар fulfilled % и счётчики fulfilled/unfulfilled.
Таблица заказов
Основной контент вкладки — таблица всех заказов с колонками:
Колонка
Источник данных
Order ID
order.orderId
Customer
order.customer.name
Date
order.acceptedOn — отображается в формате DD.MM.YYYY
Status
order.status — цветная метка (fulfilled, unfulfilled и др.)
Items
Список товаров в заказе (order.items[])
Total
order.customerPaid.value в центах → форматируется через formatCurrency()
↗
Кнопка открытия модального окна с деталями заказа
Фильтры и поиск
Элемент
Назначение
All Sites
Фильтр по источнику: All / SONR Music / SONR Coach / SONR Shopify / SONR Amazon
Search
Текстовый поиск по Order ID и имени покупателя
All Statuses
Фильтр по статусу: All / Fulfilled / Unfulfilled
Page size
50 / 100 / 200 / All orders — количество строк на странице
From / To + Apply
Фильтр по дате принятия заказа (acceptedOn). UTC строковое сравнение.
Экспорт CSV
Кнопка Export CSV доступна только администраторам (role === "admin"). Экспортирует все заказы с применёнными фильтрами. Имя файла включает диапазон дат. Валюта в CSV записывается без пробелов (formatCurrencyRaw()).
Sync
Sync · Webflow
Синхронизация заказов SONR Music и SONR Coach из Webflow E-commerce API.
Источники
Вкладка содержит два блока — по одному на каждый Webflow сайт:
Сайт
Site ID
Поле webflow
SONR Music
635937ab62b5fe11f4a2284a
SONR Music
SONR Coach
5dae68cb7b316e4ed4f61201
SONR Coach
1. Quick Sync
Забирает заказы из Webflow API за выбранный период, трансформирует в унифицированный формат и сохраняет в MySQL через upsert.
Параметр
Описание
From (дата)
Нижняя граница выборки — acceptedOn >= date
Count
All (все за период) / Last 50 / 100 / 200
Sync (кнопка)
Запускает POST /sync-webflow → сервер делает запросы к Webflow API
Статус операции отображается под toolbar. При успехе показывается количество добавленных/обновлённых заказов.
2. Auto Sync
Серверный фоновый синк — работает без участия пользователя по расписанию. Управляется через toggle-переключатель.
Параметр
Значения
Count
50 / 100 / 200 последних заказов на каждом цикле
Interval
Every 1 hour / Every 2 hours
Toggle
Вкл/выкл фоновый синк. Сохраняется на сервере до перезапуска.
3. Sync Products
Забирает каталог продуктов из Webflow API и сохраняет в app_data['raw_products']. Доступно для обоих сайтов (SONR Music и SONR Coach). Это сырые данные без пользовательских настроек.
После выполнения — перейдите в Settings → Products Settings и нажмите соответствующую кнопку Sync (SONR Music или SONR Coach), чтобы обновить рабочий каталог app_data['products']. Пользовательские поля (skuid_product_type, skuid_product_color, skuid_main_product_qty, skuid_receivers_count, skuid_radio_count) при этом сохраняются.
Sync
Sync · Shopify
Подключение и синхронизация заказов из Shopify через OAuth 2.0.
Подключение (OAuth)
При первом входе отображается блок Connect Shopify с кнопкой «Connect Shopify Account». Поток авторизации:
Кнопка → GET /shopify/auth → редирект на Shopify OAuth.
После подтверждения Shopify редиректит на /shopify/callback.
Сервер обменивает code на access token, сохраняет в памяти.
Страница-подтверждение показывает токен с кнопкой Copy.
На Railway токен нужно вставить вручную в Railway Variables → SHOPIFY_ACCESS_TOKEN.
После подключения блок Connect скрывается и появляется блок синхронизации.
1. Import from Shopify
Первичный импорт — загружает сырые заказы из Shopify Admin API (2024-01) в app_data['raw_shopify']. Использует cursor-based пагинацию через Link header. Параметр — дата From.
2. Quick Sync
Трансформирует сырые данные Shopify в унифицированный формат и делает upsert в MySQL. Аналогично Webflow Quick Sync — параметры From и Count.
3. Auto Sync
Аналогичен Webflow Auto Sync. Работает на сервере в фоне по расписанию (1 или 2 часа). Управляется toggle-переключателем.
4. Sync Products
Пулит все продукты из Shopify Admin API (GET /admin/api/2024-01/products.json?limit=250) с cursor-based пагинацией (Link header) и сохраняет в raw_products с source: "Shopify".
Трансформация transformShopifyProduct:
Наше поле
Источник в Shopify
productId
"shopify-" + raw.id
source
"Shopify"
name
raw.title
slug
raw.handle
description
raw.body_html (HTML-теги удаляются)
shippable
variants[0].requires_shipping
skus[].skuId
"shopify-" + variant.id
skus[].price
round(variant.price × 100) (в центах)
skus[].image
variant image по image_id, иначе fallback на product.image.src
skus[].variant
{option1, option2, option3} — для отображения различий между вариантами
Импорт — только в raw_products. Чтобы применить в каталог — используйте Settings → Products Settings → Sync from Shopify.
Синхронизация заказов Amazon US через Amazon Selling Partner API (SP-API).
Подключение
Amazon SP-API не использует OAuth flow из браузера. Credentials задаются вручную через ENV переменные (локально в .env, на Railway — в Variables).
Переменная
Описание
AMAZON_LWA_CLIENT_ID
LWA Application Client ID
AMAZON_LWA_CLIENT_SECRET
LWA Application Client Secret
AMAZON_REFRESH_TOKEN
Refresh Token (содержит символ | — при вставке в Railway убедитесь что значение не обрезается)
AMAZON_MARKETPLACE_ID
ATVPDKIKX0DER (Amazon US)
AMAZON_SELLER_ID
Selling Partner ID (e.g. A5K41I1GYC008) — нужен только для 4. Sync Products
Если credentials не заданы — отображается блок-заглушка «Add your Amazon credentials to .env». После добавления и перезапуска появляется блок синхронизации.
Access token кешируется в памяти сервера и автоматически обновляется за 5 минут до истечения.
1. Import from Amazon
Загружает сырые заказы из Amazon Orders API (/orders/v0/orders) в app_data['raw_amazon']. Items каждого заказа загружаются отдельно через /orders/v0/orders/{orderId}/orderItems с rate limiting. Параметр — дата From (обязательная для SP-API, CreatedAfter).
2. Quick Sync
Трансформирует сырые данные Amazon в унифицированный формат и делает upsert в MySQL. Параметры — From и Count.
3. Auto Sync
Фоновый серверный синк по расписанию (1 или 2 часа). Управляется toggle-переключателем. Аналогичен Webflow Auto Sync.
4. Sync Products
Тянет все listings продавца через SP-API GET /listings/2021-08-01/items/{sellerId} и сохраняет в app_data['raw_products'] с source: "Amazon". Постранично через pagination.nextToken (pageSize=20). Каждый listing маппится в product-record c одним SKU внутри (Amazon не моделирует варианты как Webflow/Shopify — каждый ASIN/SKU отдельный листинг). Поля product: productId: "amazon-{sku}", name (из summaries[].itemName), один SKU с sku, asin, image (summaries[].mainImage.link), price (если в offers[] есть запись для текущего marketplace).
Merge в raw_products идёт по ключу productId:source — повторный sync обновляет записи in-place, не дублирует. Endpoint: POST /sync-products-amazon.
4
Statistics
Аналитические отчёты по заказам SONR Music за выбранный период.
Источники данных
Страница работает с заказами из MySQL, отфильтрованными по условию webflow === "SONR Music" и shop === "SONR Music Shop". Используются те же данные что в Dashboard Block 1.
Фильтр по дате
В верхней части страницы — поля From и To + кнопка Apply. По умолчанию выставляется первый день текущего месяца → сегодня (UTC). Все отчёты пересчитываются при нажатии Apply.
KPI карточки
Карточка
Формула
Orders in Range
Количество заказов за выбранный период
Total Revenue
Сумма customerPaid.value по всем заказам периода
Average Order
Total Revenue / Orders in Range
Avg Items / Order
Среднее количество позиций на заказ
Фильтр по статусу
Перед таблицами — строка chip-фильтров: Unfulfilled, Fulfilled, Refunded, Disputed, Dispute-lost. Можно выбрать несколько. Все активные чипы применяются к отчётам одновременно. Если ни один не выбран — показываются все статусы.
Вкладки отчётов
Вкладка
Колонки
Описание
Orders
Карточки по типу продукта
Продукты сгруппированы по типу (skuid_product_type): SONR Music, SONR Music 2, SONR Music Multisport, SONR Coach, Accessories. Каждая группа показывает заголовок с суммарными units + revenue, затем строки по названию продукта (Product · Units Sold · Revenue). Клик на строку раскрывает SKU-детализацию.
Products
Dashboard-карточки по типу продукта
Разбивка по 5 типам: SONR Music, SONR Music 2, SONR Music Multisport, SONR Coach, Accessories. Каждая карточка показывает: Revenue (hero), Orders, Units Sold, Avg Order Revenue, прогресс fulfilled/unfulfilled. Ниже карточки — список SKU с количеством заказов, юнитов и выручкой. Классификация по skuid_product_type из skuMap.
Color
Product · Orders · Units · Revenue
Группировка по цвету варианта (item.variant). Под таблицей — цветовые чарты с долями.
Управление пользователями, регионами и каталогом SKU-продуктов.
Обзор
Страница Settings доступна только администраторам. По умолчанию открывается вкладка Overview с карточками-навигаторами к трём разделам:
Вкладка
Назначение
Overview
Стартовая вкладка. Три карточки-ссылки на остальные вкладки с кратким описанием.
Users
Список пользователей и кнопка «+ Add User» для раскрытия формы создания.
Countries & Regions
Управление регионами доставки и их странами (сетка из двух колонок).
Products Settings
Каталог SKU-продуктов: категории, цвет и количество для Dashboard.
Settings
Settings · Users
Управление учётными записями пользователей.
Таблица пользователей
По умолчанию отображается список всех пользователей. Нажмите + Add User в правом углу заголовка — форма создания раскроется ниже. Нажмите Cancel или снова «+ Add User» чтобы свернуть.
Все поля в таблице редактируемые — кнопка Edit справа. Правила безопасности:
Нельзя удалить себя
Нельзя удалить последнего администратора
Нельзя понизить роль последнего администратора
Форма Add New User
Раскрывается по кнопке + Add User. Обязательные поля — Username и Password. После создания форма автоматически сворачивается.
Поле
Обязательное
Описание
Username
Да
Уникальный идентификатор для входа
Password
Да
Пароль для входа
Full Name
Нет
Отображаемое имя
Email
Нет
Контактный email
Phone
Нет
Контактный телефон
Role
Да
user (только просмотр заказов) или admin (полный доступ)
Хранение
Пользователи хранятся в app_data['users'] (таблица app_data, ключ users). Пароли хранятся в открытом виде в JSON — не используйте критичные пароли.
Settings
Settings · Countries & Regions
Управление регионами доставки и их составом по странам.
Назначение
Регионы используются на странице Statistics в отчёте Country — страны группируются по регионам для отображения на SVG-чарте.
Управление регионами
Кнопка + Add Region создаёт новый регион. Клик по названию региона — переименование. Под каждым регионом список стран с возможностью добавления (+ Add Country) и удаления (×).
Данные хранятся в app_data['regions'].
Settings
Settings · Products Settings
Настройка SKU-каталога — категории, цвет и количество продуктов для Dashboard.
Sync from Webflow / Shopify / Amazon
Четыре кнопки для импорта продуктов из app_data['raw_products'] в app_data['products']:
Кнопка
Источник
Endpoint
Sync Webflow SONR Music products
source === "Webflow SONR Music"
POST /products/sync-from-raw
Sync Webflow SONR Coach products
source === "Webflow SONR Coach"
POST /products/sync-from-raw-coach
Sync Shopify products
source === "Shopify"
POST /products/sync-from-raw-shopify
Sync Amazon products
source === "Amazon"
POST /products/sync-from-raw-amazon
Все четыре сохраняют пользовательские настройки SKU (skuid_product_type, skuid_product_color, skuid_main_product_qty и пр.) при обновлении.
Перед sync нужно сначала получить сырые данные — через Sync → Webflow → Sync Products для Music/Coach, Sync → Shopify → Sync Products для Shopify, или Sync → Amazon → Sync Products для Amazon.
Переключение источника
Над списком продуктов расположены четыре переключателя — SONR Music, SONR Coach, Shopify, Amazon. Они фильтруют список по полю product.source на клиенте. Все продукты хранятся в одной таблице app_data['products'], переключение не меняет данные.
Список продуктов
Список всех SKU выбранного источника, сгруппированных по skuid_product_type. Набор полей зависит от активного источника.
SONR Music:
Поле
Значения
Как используется
skuid_product_type
SONR Music / SONR Music 2 / SONR Music Multisport / Accessories / и др.
Определяет в какой блок Dashboard попадает SKU. Если не задан — SKU попадает в Block 5 (Other).
skuid_product_color
Blue / Yellow / Graphite / Coral / Black
Цвет варианта. Доступен в skuMap, используется в Statistics → Color.
skuid_main_product_qty
1 / 2 / 3 (число)
Множитель количества. Например SONR Music x2 → 2. Применяется при подсчёте Q-ty в Dashboard.
SONR Coach:
Поле
Значения
Как используется
skuid_product_type
SONR Coach / Accessories / и др.
Категория SKU для классификации в отчётах.
skuid_product_color
Blue / Yellow / Graphite / Coral / Black
Цвет варианта.
skuid_main_product_qty
1 / 2 / … (число)
Количество основных устройств в наборе.
skuid_receivers_count
число
Количество приёмников (receivers) в наборе.
skuid_radio_count
число
Количество радиомодулей (radios) в наборе.
Изменения не сохраняются автоматически. После редактирования одного или нескольких SKU появляется кнопка Save changes — нажмите её чтобы сохранить все изменения разом. Данные отправляются одним запросом POST /products/save-skus, который обновляет только явно изменённые поля, не трогая остальные.
Sync from Webflow — сохранение настроек
При повторной синхронизации SONR Music продуктов из Webflow все три поля (skuid_product_type, skuid_product_color, skuid_main_product_qty) сохраняются — они не затираются сырыми данными из Webflow.
Связь с Dashboard
При загрузке страницы Dashboard строится skuMap — словарь variantId → { skuid_product_type, skuid_product_color, skuid_main_product_qty }. Все блоки Dashboard используют этот словарь для классификации товаров в заказах. После изменения настроек SKU нужно перезагрузить страницу Dashboard для применения изменений.