fix(generator): исправлен расчёт скидки после складской дельты

- Зачем:
  - слепок D7 содержал отрицательный итог заказа и попадал в брак ODS.
- Что:
  - скидка пересчитана от корзины после удаления отсутствующей позиции.
  - сериализатор отклоняет отрицательные деньги через ValueError.
  - добавлены проверки восьми стартовых дней и обновлена опись мира.
- Проверка:
  - make lint, make typecheck и make test в generator/.
  - живой world_next_day: 1694 строки приняты, таблица ошибок пуста.
This commit is contained in:
2026-08-20 13:13:14 +03:00
parent 7b89ccb529
commit 64bcd3d596
8 changed files with 123 additions and 72 deletions
+20 -16
View File
@@ -78,22 +78,26 @@
**Дельта суммы (C) — вычеркнутая позиция.** Товара не оказалось в наличии,
позицию сняли: у заказа на одну позицию меньше, чем в клиентских массивах, а
`items_total` меньше на её стоимость. Уменьшаются ровно на полную стоимость
вычеркнутой строки и `items_total`, и `total`; скидка остаётся рассчитанной от
исходной клиентской покупки и после складского вычёркивания не пересчитывается.
Момента у неё нет — заказ приезжает
урезанным во всех своих слепках: первый слепок снимается на границе суток,
когда склад заказ уже собрал. Заказ из одной позиции дельты не получает —
пустых заказов не бывает. Позиция выбирается равновероятно: корреляция со
спросом на доле 1–2% статистически ненаблюдаема — менти платил бы за неё
таблицей чисел мира, а увидеть не мог бы ничем. Доводы за вычёркивание:
остаток — сотни рублей, он торчит в витрине сверки сам; расхождение
объясняется сравнением позиций — разбором вложенного JSON и `ARRAY JOIN`,
ровно тем навыком, ради которого позиции разбираются; история рассказывается
словами без легенды про генератор. Отклонено: *переоценка позиции* и *другое
количество* — дельта в десятки рублей, её надо захотеть заметить; *чистая
дельта без истории* — тупик, объяснить нечем; *врёт клиент, а не бэкенд*
заказ у нас проекция той же корзины.
`items_total` меньше на её полную стоимость. После вычёркивания деньги заказа
считаются от оставшейся корзины по [общему
правилу](snapshot.md#откуда-берётся-заказ), поэтому `total` остаётся
неотрицательным по построению. Момента у дельты нет — заказ приезжает урезанным
во всех своих слепках: первый слепок снимается на границе суток, когда склад
заказ уже собрал. Заказ из одной позиции дельты не получает — пустых заказов
не бывает. Позиция выбирается равновероятно: корреляция со спросом на доле
1–2% статистически ненаблюдаема — менти платил бы за неё таблицей чисел мира,
а увидеть не мог бы ничем. Доводы за вычёркивание: остаток — сотни рублей, он
торчит в витрине сверки сам; расхождение объясняется сравнением позиций —
разбором вложенного JSON и `ARRAY JOIN`, ровно тем навыком, ради которого
позиции разбираются; история рассказывается словами без легенды про генератор.
Отклонено: *сохранять скидку исходной корзины и отбирать только безопасные
позиции* — отсутствие товара стало бы зависеть от купона и доставки, а выбор
позиции перестал бы быть равновероятным; *разрешить отрицательный `total` или
ослабить строгий приём* — ошибка источника превратилась бы в брак, который
молча терпит хранилище; *переоценка позиции* и *другое количество* — дельта в
десятки рублей, её надо захотеть заметить; *чистая дельта без истории*
тупик, объяснить нечем; *врёт клиент, а не бэкенд* — заказ у нас проекция той
же корзины.
**Потеря события (B) — точечная.** Уходит строка `purchase`, просмотр
`/confirmation` остаётся: события уезжают разными запросами, потерять один и