feat(generator): заказы дня — проекция покупок с деньгами магазина #98

Merged
ddmitry merged 2 commits from feat/90-order-projection into main 2026-08-18 15:19:17 +03:00
Owner

Тикет #90: заказ — вторая проекция покупки, а не второе порождение.

Что внутри

  • Второй выход commerce — покупки дня готовой структурой: номер
    заказа, корзина, выручка клиента, промокод, человек за кукой. Сумма
    позиций считается один раз на день и уезжает в обе стороны: в событие
    как purchaseRevenue, в заказ как items_total — разойтись им негде.
    Новых бросков в подпоток COMMERCE не добавилось.
  • Заказная сторона — подпоток ORDERS на позиции 2 дерева зерна
    (прежнее мёртвое имя DISCREPANCIES), ветвление по дню рождения заказа.
    LATECOMERS не тронут: его наполнит этап 6.
  • orders.py — деньги заказа целыми копейками: discount по таблице
    «код → скидка» (округление вниз), delivery броском по таблице весов на
    полную длину дня, total = items_totaldiscount + delivery.
  • person_id — последним броском когорты, после паспортов: вторая кука
    пары повторяет ID своего человека, заказ показывает его как user_id, в
    событие Метрики он не попадает.
  • ORDER_WINDOW_DAYS = 7 рядом с D0 и поясом; первым читателем станет
    команда слепка (#92).

Критерии приёмки

  • у каждого заказа дня ровно одно событие purchase, order_id равен
    purchaseID, корзина и суммы совпадают с торговой половиной;
  • события дня не изменились байт в байт — опись мира не покраснела;
  • на каждом заказе total = items_totaldiscount + delivery;
  • обе куки пары дают один user_id, в событие он не попадает;
  • повторный прогон того же дня даёт те же заказы.

Ревью

Три линии: две Клод-линии (стандарты и уместность, спека и критерии) и
холодное ревью Кодексом свежим тредом (дефекты, инварианты, честность
тестов) — APPROVED_WITH_MINORS. Пересечений между семействами не было.

Принято: слепок дня в тестах сторожил только события — теперь сторожит и
заказы; снят лишний сторож таблицы доставки; снят дубль теста чистоты.
Находка Кодекса (второй коммит): тест анонимности сверял только числовые
колонки, и личность, уехавшая строкой в сыром ecommerce, проходила мимо.
Дыра воспроизведена мутацией, правка проверена ею же — до правки тест
зелёный, после красный.

Отклонено с доводом: возить промокод индексом вместо кода (модель в том,
что бэкенд читает код по общей таблице мира); заводить тип Stream вместо
тройки columns/page/product (клубок был до тикета, а person едет только
на вход).

Открытые вопросы владельцу

  • заказ не несёт купон: в таблице слепка мастер-спеки такого поля нет,
    поэтому сверка идёт через discount. Если купон в заказе нужен — правка
    на строчку.
  • числа таблицы доставки черновые, финальная калибровка — этап 7.

Проверка

Из generator/: make lint, make typecheck, make test (417 тестов).
В корне: make lint.

Closes #90

Тикет #90: заказ — вторая проекция покупки, а не второе порождение. ## Что внутри - **Второй выход `commerce`** — покупки дня готовой структурой: номер заказа, корзина, выручка клиента, промокод, человек за кукой. Сумма позиций считается один раз на день и уезжает в обе стороны: в событие как `purchaseRevenue`, в заказ как `items_total` — разойтись им негде. Новых бросков в подпоток `COMMERCE` не добавилось. - **Заказная сторона** — подпоток `ORDERS` на позиции 2 дерева зерна (прежнее мёртвое имя `DISCREPANCIES`), ветвление по дню рождения заказа. `LATECOMERS` не тронут: его наполнит этап 6. - **`orders.py`** — деньги заказа целыми копейками: `discount` по таблице «код → скидка» (округление вниз), `delivery` броском по таблице весов на полную длину дня, `total` = `items_total` − `discount` + `delivery`. - **`person_id`** — последним броском когорты, после паспортов: вторая кука пары повторяет ID своего человека, заказ показывает его как `user_id`, в событие Метрики он не попадает. - **`ORDER_WINDOW_DAYS` = 7** рядом с D0 и поясом; первым читателем станет команда слепка (#92). ## Критерии приёмки - у каждого заказа дня ровно одно событие `purchase`, `order_id` равен `purchaseID`, корзина и суммы совпадают с торговой половиной; - события дня не изменились байт в байт — опись мира не покраснела; - на каждом заказе `total` = `items_total` − `discount` + `delivery`; - обе куки пары дают один `user_id`, в событие он не попадает; - повторный прогон того же дня даёт те же заказы. ## Ревью Три линии: две Клод-линии (стандарты и уместность, спека и критерии) и холодное ревью Кодексом свежим тредом (дефекты, инварианты, честность тестов) — `APPROVED_WITH_MINORS`. Пересечений между семействами не было. Принято: слепок дня в тестах сторожил только события — теперь сторожит и заказы; снят лишний сторож таблицы доставки; снят дубль теста чистоты. Находка Кодекса (второй коммит): тест анонимности сверял только числовые колонки, и личность, уехавшая строкой в сыром `ecommerce`, проходила мимо. Дыра воспроизведена мутацией, правка проверена ею же — до правки тест зелёный, после красный. Отклонено с доводом: возить промокод индексом вместо кода (модель в том, что бэкенд читает код по общей таблице мира); заводить тип `Stream` вместо тройки `columns/page/product` (клубок был до тикета, а `person` едет только на вход). ## Открытые вопросы владельцу - заказ не несёт купон: в таблице слепка мастер-спеки такого поля нет, поэтому сверка идёт через `discount`. Если купон в заказе нужен — правка на строчку. - числа таблицы доставки черновые, финальная калибровка — этап 7. ## Проверка Из `generator/`: `make lint`, `make typecheck`, `make test` (417 тестов). В корне: `make lint`. Closes #90
ddmitry added 2 commits 2026-08-18 15:15:59 +03:00
- Зачем:
  - тикет #90: второй источник должен смотреть на торговую половину дня
    глазами бэкенда — заказ есть проекция покупки, а не второе порождение;
    учебный результат — два источника согласованы по построению, а не
    сверкой.
- Что:
  - `commerce` отдаёт вторым выходом покупки дня: номер заказа, корзину,
    выручку клиента, промокод и человека за кукой; новых бросков в
    подпоток `COMMERCE` не добавилось, события дня не сдвинулись.
  - заведена заказная сторона: подпоток `ORDERS` на позиции 2 дерева зерна
    (прежнее мёртвое имя `DISCREPANCIES`), ветвление — по дню рождения
    заказа; `LATECOMERS` не тронут, его наполнит этап 6.
  - новый модуль `orders.py`: деньги заказа целыми копейками —
    `items_total` из корзины, `discount` по таблице «код → скидка»
    (округление вниз), `delivery` броском по таблице весов на полную длину
    дня, `total` = `items_total` − `discount` + `delivery`.
  - план состава завёл `person_id` — последним броском когорты, после
    паспортов: вторая кука пары повторяет ID своего человека, заказ
    показывает его как `user_id`, в событие Метрики он не попадает.
  - `world`: окно изменяемости `ORDER_WINDOW_DAYS` = 7 рядом с D0 и поясом,
    черновая таблица стоимости доставки.
  - слепок дня в тестах сторожит обе половины: заказы сравниваются наравне
    с потоком событий.
  - доки: имя подпотока заказной стороны и состав денег заказа записаны в
    `docs/architecture/orders`, README генератора знает про новый модуль.
- Проверка:
  - из `generator/`: make lint, make typecheck, make test (417 тестов);
    в корне — make lint.
  - опись мира не покраснела: байты восьми дней те же, заказная сторона
    события не сдвинула.

Closes #90

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
- Зачем:
  - холодное ревью Кодексом (линия дефектов) показало дыру в проверке
    анонимности: личность, уехавшая строкой в сыром `ecommerce`, проходила
    мимо сравнения числовых колонок, и тест оставался зелёным. Дыра
    воспроизведена: user_id, дописанный в блок покупки, тест не замечал.
- Что:
  - тест анонимности спрашивает двумя способами: числовые колонки дня — со
    всеми личностями аудитории, строки покупок целиком — с личностями
    покупателей. Сырой блок собирается руками, и лишнее поле в нём теперь
    красит проверку.
- Проверка:
  - из `generator/`: make lint, make typecheck, make test (417 тестов).
  - мутация «дописать user_id в ecommerce покупки»: до правки тест зелёный,
    после — красный.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
ddmitry merged commit 62941d2ee5 into main 2026-08-18 15:19:17 +03:00
ddmitry deleted branch feat/90-order-projection 2026-08-18 15:19:17 +03:00
Sign in to join this conversation.
No Reviewers
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: ddmitry/clickstream-data-platform#98