Отрицательный total после складской дельты #102

Open
opened 2026-08-18 22:54:55 +03:00 by ddmitry · 0 comments
Owner

Part of #5.

Цель

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

Подтверждённые факты

Замерено 18 августа 2026 года при исполнении #94, зерно CANONICAL_SEED = 20260601.

  • День 7, заказ 20260608-0007: items_total = 549000, discount = 666900, delivery = 29900, total = -88000 копеек. Восемь версий заказа из сырья попадают в ods.order_snapshot_errors классом field_invalid.
  • _delta вычёркивает позицию и уменьшает items_total, а _discount оставляет скидку от исходной клиентской выручки. Поэтому скидка может стать больше оставшейся корзины.
  • _money неверно пишет отрицательные копейки: -101 превращается в -2.99, -1 — в -1.99. Нынешние -88000 выглядят верно только потому, что это целое число рублей.
  • Проверка money >= 0 идёт на дне 2, а набор дней в соседних тестах заканчивается днём 6. Дефект живёт на непокрытом дне 7.

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

  • Скидка и складская дельта согласованы так, что total неотрицателен по построению, а не обрезкой результата; правило записано в docs/architecture/orders/.
  • Поведение _money на отрицательном входе определено явно: корректная запись либо явный отказ; есть красный до исправления тест.
  • Проверка денежных инвариантов проходит по всем восьми дням стартового мира и до исправления краснеет именно на дне 7.
  • Скидка сверяется с корзиной заказа после складской дельты.
  • Опись мира пересобрана; make lint, make typecheck, make test в generator/ зелёные.
  • Честный прогон на стенде оставляет ods.order_snapshot_errors пустой; оговорка в docs/architecture/orders/ingestion.md снята.

Границы

  • Строгий приём #94 не ослаблять и проверки сумм в SQL ODS не добавлять.
  • Модель DDS не затрагивать.
  • Не маскировать причину через max(0, total) или безусловное обрезание скидки.
  • Конкретное правило согласования скидки и дельты сначала выбрать отдельной развилкой: пересчёт скидки, ограничение дельты или другое доменно объяснимое решение.

Сначала прочитать

  • generator/src/clickstream_generator/orders.py: _delta, _discount, сборка Orders.
  • generator/src/clickstream_generator/serialize.py: orders, _money.
  • generator/tests/test_orders.py и generator/tests/test_snapshot.py.
  • docs/architecture/orders/snapshot.md, docs/architecture/orders/fate.md, docs/architecture/orders/ingestion.md.
  • docs/specs/2026-08-01-generator.md, раздел «Чем меряется генератор».

Проверка

cd generator
make lint
make typecheck
make test

После пересборки стартового мира — честный прогон orders_ingest и сверка числа годных строк и брака с числом отправленных сообщений.

Part of #5. ## Цель Заказ не уезжает на провод с отрицательным `total`: арифметика скидки и складской дельты согласована по явному правилу, а стартовый мир снова проходит строгий приём без брака. Учебный результат — менти видит, что ошибку источника чинят в источнике, а не ослаблением ODS. ## Подтверждённые факты Замерено 18 августа 2026 года при исполнении #94, зерно `CANONICAL_SEED = 20260601`. - День 7, заказ `20260608-0007`: `items_total = 549000`, `discount = 666900`, `delivery = 29900`, `total = -88000` копеек. Восемь версий заказа из сырья попадают в `ods.order_snapshot_errors` классом `field_invalid`. - `_delta` вычёркивает позицию и уменьшает `items_total`, а `_discount` оставляет скидку от исходной клиентской выручки. Поэтому скидка может стать больше оставшейся корзины. - `_money` неверно пишет отрицательные копейки: `-101` превращается в `-2.99`, `-1` — в `-1.99`. Нынешние `-88000` выглядят верно только потому, что это целое число рублей. - Проверка `money >= 0` идёт на дне 2, а набор дней в соседних тестах заканчивается днём 6. Дефект живёт на непокрытом дне 7. ## Критерии приёмки - [ ] Скидка и складская дельта согласованы так, что `total` неотрицателен по построению, а не обрезкой результата; правило записано в `docs/architecture/orders/`. - [ ] Поведение `_money` на отрицательном входе определено явно: корректная запись либо явный отказ; есть красный до исправления тест. - [ ] Проверка денежных инвариантов проходит по всем восьми дням стартового мира и до исправления краснеет именно на дне 7. - [ ] Скидка сверяется с корзиной заказа после складской дельты. - [ ] Опись мира пересобрана; `make lint`, `make typecheck`, `make test` в `generator/` зелёные. - [ ] Честный прогон на стенде оставляет `ods.order_snapshot_errors` пустой; оговорка в `docs/architecture/orders/ingestion.md` снята. ## Границы - Строгий приём #94 не ослаблять и проверки сумм в SQL ODS не добавлять. - Модель DDS не затрагивать. - Не маскировать причину через `max(0, total)` или безусловное обрезание скидки. - Конкретное правило согласования скидки и дельты сначала выбрать отдельной развилкой: пересчёт скидки, ограничение дельты или другое доменно объяснимое решение. ## Сначала прочитать - `generator/src/clickstream_generator/orders.py`: `_delta`, `_discount`, сборка `Orders`. - `generator/src/clickstream_generator/serialize.py`: `orders`, `_money`. - `generator/tests/test_orders.py` и `generator/tests/test_snapshot.py`. - `docs/architecture/orders/snapshot.md`, `docs/architecture/orders/fate.md`, `docs/architecture/orders/ingestion.md`. - `docs/specs/2026-08-01-generator.md`, раздел «Чем меряется генератор». ## Проверка ```sh cd generator make lint make typecheck make test ``` После пересборки стартового мира — честный прогон `orders_ingest` и сверка числа годных строк и брака с числом отправленных сообщений.
ddmitry added the needs-triage label 2026-08-18 22:54:55 +03:00
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Reference: ddmitry/clickstream-data-platform#102