Перейти к содержанию

Бизнес-день

Бизнес-день (операционный день) — это учётная дата, к которой привязываются все денежные операции балльного леджера: начисления, списания, переводы, обмены и сгорания. Штамп бизнес-дня фиксирует, в рамках какого операционного периода была проведена операция, и служит границей для дневной отчётности и корректировок.

В каждый момент времени открыт ровно один текущий бизнес-день. По завершении операционных суток оператор закрывает текущий день — при этом автоматически открывается следующий. Закрытый день переоткрыть нельзя: исправление операции прошлого дня проводится встречной операцией текущим днём (см. «Исправление операции прошлого дня»).

Жизненный цикл операционного дня

stateDiagram-v2
    [*] --> OPEN: открыт текущий день
    OPEN --> CLOSED: закрытие (открывается следующий день)
    CLOSED --> [*]
Статус Значение
OPEN Текущий операционный день. Новые операции получают штамп этого дня.
CLOSED День закрыт окончательно. Новые операции получают штамп следующего дня.

Один открытый день

Закрытие текущего дня атомарно открывает следующий, поэтому система всегда располагает ровно одним открытым операционным днём. Отдельная операция «открыть день» не требуется.

Это не соглашение, а ограничение базы данных: второй открытый день отвергается уникальным индексом. Учётная дата операции читается как «единственный открытый день», и при нарушении инварианта операция завершится ошибкой, а не тихо возьмёт день наугад.

Контур без открытого дня

Если открытого дня нет вовсе (справочник рабочих дней пуст), денежная операция не отклоняется: она датируется календарной датой в поясе программы (program.timezone, см. «Конфигурация» → «Часовой пояс программы»). Пояс берётся из переопределения в операционной конфигурации, иначе из LOYALTY_PROGRAM_TIMEZONE, иначе Europe/Moscow. Тем же поясом датируется день, который заводится первым обращением, — например, первым закрытием на пустом контуре: около полуночи операция и заведённый день не оказываются в разных сутках. Учтите, что первое закрытие на пустом контуре заводит сегодняшний день, сразу закрывает его и открывает следующий.

Текущий день и недавняя история

GET /v1/business-day возвращает текущий операционный день и список недавних дней (последние 14) для контроля непрерывности учёта.

{
  "current": {
    "businessDate": "2026-06-25",
    "status": "OPEN",
    "openedAt": "2026-06-25T00:05:00+03:00",
    "closedAt": null
  },
  "recent": [
    {
      "businessDate": "2026-06-24",
      "status": "CLOSED",
      "openedAt": "2026-06-24T00:05:00+03:00",
      "closedAt": "2026-06-25T00:05:00+03:00"
    }
  ]
}
Поле Описание
businessDate Учётная дата операционного дня.
status Статус дня (OPEN / CLOSED).
openedAt Момент открытия дня (с таймзоной).
closedAt Момент закрытия; null для открытого дня.

Закрытие дня

POST /v1/business-day/close закрывает текущий операционный день и открывает следующий. В ответе возвращается представление только что закрытого дня.

curl -X POST https://<host>/v1/business-day/close \
  -H "Authorization: Bearer <token>"

Закрытие необратимо

После закрытия дня новые операции получают штамп уже следующего дня. Вернуть контур в закрытый день нельзя.

Исправление операции прошлого дня

Ошибка, допущенная в закрытом дне, исправляется встречной операцией текущим днём — ручным списанием или начислением, — а не изменением прошлого. Проводки неизменяемы: история не переписывается, а дополняется.

Что это значит на практике:

  • учётная дата исправления — сегодняшний открытый день, а не дата ошибки;
  • дневной отчёт закрытого дня остаётся ровно таким, каким его сдали;
  • в причине операции указывается, что именно исправляется, — по ней исправление связывается с исходной операцией при разборе.

Почему не переоткрытие

Раньше существовала операция переоткрытия дня. Она не решала задачу: программа лояльности работает круглосуточно, и «возврат» контура в прошлый день означал бы, что прошлую дату получает вся живая выручка — покупки, начисления внешнего движка правил, команды из очереди приёма. Так же устроены и другие платформы лояльности: исправление проводится текущей датой, а дата исходной операции служит лишь параметром расчёта.

Дневной отчёт

GET /v1/business-day/{date}/summary формирует сводный отчёт по операциям за указанный операционный день.

{
  "businessDate": "2026-06-24",
  "accruedMinor": 1540000,
  "redeemedMinor": 320000,
  "operations": 1287
}
Поле Описание
businessDate Учётная дата отчётного дня.
accruedMinor Суммарно начислено за день, в минимальных единицах баллов.
redeemedMinor Суммарно списано за день, в минимальных единицах баллов: траты клиентов вместе со сгоранием баллов (плановым и ручным). Сторно начислений (например, при возврате по чеку) и корректировки оператора сюда не входят.
operations Общее число записей журнала бонусных операций, отнесённых к этому дню.

Сгорание в дневном отчёте не отделяется

В отличие от сводки мониторинга и отчётов, где сгоревшие баллы показываются отдельно от трат клиентов, дневной отчёт и сверка итогов с внешней системой по-прежнему включают сгорание в redeemedMinor: эти величины сравниваются с итогами внешней системы, и их трактовка — часть интеграционного контракта сверки.

Минимальные единицы

Суммы accruedMinor и redeemedMinor выражены в минимальных единицах баллов (minor units), что исключает погрешности округления при агрегировании.

Та же выборка — в сверке итогов

Собственные итоги дня для приёма итогов внешней системы (см. «Сверка итогов и контроль целостности») считаются той же выборкой, что и дневной отчёт: расхождение с контрагентом не может объясняться тем, что отчёт и сверка смотрят на разные данные.

Операционный день есть у каждой записи журнала операций

Учётная дата хранится не только у проводки леджера, но и у каждой записи журнала бонусных операций, по которому считаются дневной отчёт и сверка итогов. Записи, сделанные до появления этой колонки, заполнены задним числом — днём связанной проводки, а если связи нет — календарной датой в UTC; новые записи без явно указанного дня получают текущий операционный день. Поэтому старые операции больше не выпадают из отчёта и сверки за свой день.

Эндпоинты и доступ

Метод и путь Назначение Требуемая роль
GET /v1/business-day Текущий день и недавняя история LOYALTY_MONITORING_READ
POST /v1/business-day/close Закрыть текущий день, открыть следующий LOYALTY_BUSINESS_DAY_WRITE
GET /v1/business-day/{date}/summary Дневной отчёт по операциям LOYALTY_MONITORING_READ

Безопасность

Все эндпоинты защищены OAuth2 (resource server на Keycloak, JWT) с принципом deny-by-default. Чтение текущего дня и отчётов доступно роли мониторинга, а закрытие дня — отдельной роли управления бизнес-днём.