# Почему вчерашняя выручка меняется сегодня **Чему учит:** объяснять изменения уже рассчитанного отчета поступлением заказов и сменой их статусов, определять момент закрытия дня и границы доступной истории. Факультатив доступен после ступени 1. Понадобятся Airflow и DBeaver с подключением `analyst` по [инструкции](../../../README.md#подключиться-из-dbeaver). Вы будете наблюдать один день оформления заказов, продвигая мир по одному модельному дню. Новые данные останутся в стенде. ## Подготовьте первое наблюдение Поставьте `world_live` на паузу. Дождитесь завершения начатых прогонов генерации и обработки, затем запустите `etl_pipeline` и дождитесь успеха. Так исходное наблюдение будет снято после обновления витрин. Узнайте последний день событий и дату последнего полученного слепка: ```sql SELECT (SELECT max(event_date) FROM dds.event_v) AS event_day, (SELECT max(snapshot_date) FROM ods.order_v) AS snapshot_day; ``` День `event_day` обозначим **D**. Наблюдайте именно его до конца работы. Дата слепка, по которому сделано очередное наблюдение, будет **S**. Это модельные даты магазина. Календарная дата запуска Airflow день D не задает. Слепок снимается к концу суток и доставляется следующим прогоном. Поэтому покупки последнего сыгранного дня уже видны в событиях, а их заказов в хранилище еще нет. Проверьте это для D в `dds.order_v` и `dm.purchase_vs_orders_v`. ## Подготовьте запросы и сохраняйте результаты Напишите запросы для следующих наблюдений: | Что сохранить | Откуда взять | |---|---| | Дату полученного слепка S | `max(snapshot_date)` из `ods.order_v` | | Номер, статус и `items_total` каждого заказа дня D | `dds.order_v`, отбор по `order_date` | | Число заказов в каждом статусе и сумму `items_total` только у `paid` | те же заказы дня D | | Выручку дня D | сумма `revenue` из `dm.revenue_daily_v`, отбор по `report_date` | | Номера покупок, еще ожидающих заказ | строки `awaiting_order` из `dm.purchase_vs_orders_v`, отбор по `order_day` | Выручку складывайте по всем категориям. В этой витрине она означает стоимость позиций оплаченных заказов, без скидки и доставки, поэтому должна сходиться с суммой `items_total` у заказов в статусе `paid`. Сохраняйте результаты запросов отдельными файлами, например выгрузками CSV из DBeaver. Подписывайте D и S. До поступления первого слепка список заказов пуст, а выручка равна нулю; сохраните и это наблюдение. Публичное представление заказа показывает текущее состояние, а сырые слепки хранятся ограниченное время. Сохраненные выгрузки — история для расследования. ## Продвигайте мир по одному дню Для каждого следующего наблюдения: 1. Запустите `world_next_day` с `days = 1` и дождитесь успеха. 2. Запустите `etl_pipeline` и дождитесь успеха: `world_next_day` сам витрины не обновляет. 3. Повторите свои запросы и сохраните новый комплект результатов с датой S. Во всех запросах остается прежний день D. За один запуск проигрывайте один день, чтобы сохранить промежуточные состояния. Пакетный `world_next_day` отправляет события без пауз; ждать 24 минуты живого дня здесь не нужно. Продолжайте до слепка **D + 7** включительно. У заказов дня D окно состоит из семи дней, от D до D + 6. Слепок D + 6 содержит окончательное состояние, а еще один шаг позволит проверить, что оно сохранилось за пределами окна. От исходного наблюдения потребуется восемь однодневных запусков. ## Объясните изменения отчета После каждого шага сравнивайте заказы по `order_id`, а не по положению строк в выгрузке. Найдите: - заказы, отсутствовавшие в первом слепке D, но появившиеся в следующем; - переходы из `created` в `paid`; - отмены заказов, для которых раньше был виден статус `paid`. Первый слепок, полученный на следующий день, — обычная доставка заказов. Опоздавшими здесь считаются те, которых не было уже в этом слепке. Покажите на нескольких номерах, как они исчезают из `awaiting_order`. Для двух соседних наблюдений разложите изменение выручки по заказам. Вклад заказа — его `items_total`, если статус `paid`, и ноль в остальных случаях, включая отсутствие заказа. Разность вкладов объясняет, сколько каждый заказ добавил к отчету или убрал из него. Сумма этих изменений должна совпасть с изменением `revenue` до копейки. Запишите даты, между которыми выручка выросла или уменьшилась. Отдельный отмененный заказ мог убрать деньги, даже если итог дня вырос: покажите это по его вкладу. Одна общая сумма скрывает встречные изменения. ## Что известно об отменах Возьмите отмененный заказ, у которого в вашей истории был статус `paid`. Укажите два наблюдения, доказывающих, что он был оплачен, а затем отменен. Для отмененного заказа без наблюдаемой оплаты сформулируйте предел вывода. Дневные слепки могли не застать промежуточное состояние: оплата и отмена могли произойти между ними. Поэтому отсутствие `paid` в выгрузках не доказывает, что оплаты не было. Не заменяйте недостающие данные догадкой. ## Когда день закрыт Сравните сохраненные состояния при S = D + 6 и S = D + 7: состав заказов, их статусы, суммы и выручку. Объясните, почему отчет за D теперь окончательный. Основание — получен и обработан последний слепок окна изменяемости; одно совпадение сумм в соседних наблюдениях этого не доказывает. После собственных выводов сверьтесь с описанием [слепка и его доставки](../../architecture/orders/snapshot.md#слепок-и-его-доставка) и [границ истории отмен](../../architecture/orders/fate.md#судьба-заказа). ## Что должно получиться - [ ] сохранена последовательность наблюдений одного дня D с датами слепков S; - [ ] найдены опоздавшие заказы и изменения статусов; - [ ] изменение выручки между двумя наблюдениями объяснено вкладами заказов; - [ ] оплата одной из последующих отмен подтверждена двумя наблюдениями; - [ ] вы различаете доказанную оплату и отсутствие сведений о ней; - [ ] состояние дня за пределами окна совпало с окончательным, а момент закрытия обоснован правилом доставки слепков. После работы оставьте `world_live` на паузе либо верните автоматическое продвижение, если пользовались им до наблюдения.