Сущность: PropertyObjectTypeMonthOverride
Помесячный оверрайд экономической модели для типа объекта недвижимости.
Назначение
PropertyObjectTypeMonthOverride — точечная правка коэффициентов расчёта для конкретного календарного
месяца (Year + Month) конкретного типа объекта недвижимости. Верхний уровень разрешения
коэффициентов: PropertyObjectTypeMonthOverride → PropertyObjectTypeConfig →
EconomicModelConfig → дефолты кода. Позволяет админам платформы «прибивать»
факт/план по месяцам: рыночную цену м², темпы роста, заполняемость, долю расходов, накопленный EBITDA/м²,
месячные налоговые ставки. Дочерняя сущность агрегата PropertyObjectTypeConfig (не самостоятельный
агрегат): загружается и сохраняется только через родителя — админка присылает полный список месяцев типа
(операция replace-all).
Отличие от родительских уровней: помесячный оверрайд задаёт не только коэффициенты-темпы, но и
абсолютные значения (MarketPriceM2, Ebitda), которые в расчёте замещают вычисленные величины
месяца (см. формулы в investor-series-models.md).
Классификация
| Поле | Значение |
|---|---|
| Контекст | invest-analytics |
| Kind | дочерняя сущность агрегата PropertyObjectTypeConfig; composite PK (GroupId, Year, Month) — натуральный ключ месяца |
| Source of truth | invest-analytics |
| Таблица | invest_analytics.property_object_type_month_overrides |
| Tenant isolation | не tenanted — платформенная настройка (ADR-0052 п.3) |
Аудит и конкурентность — на родителе (PropertyObjectTypeConfig.Version/UpdatedByUserId инкрементируется
replace-all-операцией); собственных audit-полей и версии у строки нет.
Поля
Все коэффициенты nullable: null = «поле месяца не оверрайдится» (берётся значение уровня типа
объекта недвижимости или платформы). Семантика/единицы наследуемых коэффициентов —
economic-model-config.md; ниже полностью описаны только поля, которых нет на
родительских уровнях.
| Поле | Тип | Обязательность | Ограничения (PK/FK/index/constraint) | Источник | Описание |
|---|---|---|---|---|---|
GroupId | long | Да | PK часть 1; FK → invest_analytics.property_object_type_config(id) on delete cascade | родитель | Тип объекта недвижимости (группа планировок). |
Year | int | Да | PK часть 2; check >= 2000 AND <= 2100 | admin API | Календарный год месяца-оверрайда. |
Month | int | Да | PK часть 3; check >= 1 AND <= 12 | admin API | Календарный месяц (1–12). |
MarketPriceM2 | decimal? | Нет | numeric(12,2); check > 0 при not null | admin API | Абсолютная рыночная цена м² в этом месяце, руб: замещает вычисленное marketM2 месяца (применяется только при значении > 0 — правило positiveOr). |
PriceGrowthMonthly | decimal? | Нет | numeric(9,6); check > -1 AND <= 1 | admin API | Темп роста цены в этом месяце (замещает уровневый темп при переходе от предыдущего месяца). |
DailyRentM2 | decimal? | Нет | numeric(12,2); check > 0 при not null | admin API | Абсолютная ставка аренды руб/м²/сутки этого месяца (замещает вычисленную, правило positiveOr). |
RentGrowthMonthly | decimal? | Нет | numeric(9,6); check > -1 AND <= 1 | admin API | Темп роста аренды в этом месяце. |
Occupancy | decimal? | Нет | numeric(9,6); check >= 0 AND <= 1 | admin API | Заполняемость этого месяца. |
ExpenseShare | decimal? | Нет | numeric(9,6); check >= 0 AND <= 1 | admin API | Доля расходов этого месяца. |
Ebitda | decimal? | Нет | numeric(12,2); check > 0 при not null | admin API | Абсолютный накопленный EBITDA/м² (руб/м²) на этот месяц: замещает вычисленное cumulativeRevenueM2 × (1 − ExpenseShare) (правило positiveOr; применяется только в месяцах после ввода в эксплуатацию). |
AppreciationTaxMonthly | decimal? | Нет | numeric(9,6); check >= 0 AND <= 1/12 | admin API | Месячная ставка налога на прирост стоимости для этого месяца; эффективная годовая ставка = значение × 12 — замещает ставку налогового режима (tax-entity-params.md). |
EbitdaTaxMonthly | decimal? | Нет | numeric(9,6); check >= 0 AND <= 1/12 | admin API | Месячная ставка налога на EBITDA для этого месяца; годовая = × 12, замещает ставку режима. |
Связи
| Связь | Тип | Ключ | Правило загрузки | Delete behavior |
|---|---|---|---|---|
PropertyObjectTypeConfig | родитель агрегата | FK GroupId | всегда в составе агрегата: в админке — вместе с родителем по GroupId; при расчёте — полный список месяцев по GroupId (без пагинации — единицы-десятки строк) | cascade от родителя |
Инварианты и переходы состояний
| Инвариант | Условие | Где проверяется | Ошибка |
|---|---|---|---|
| INV-MO1 UniqueMonth | На (GroupId, Year, Month) — не более одной строки | composite PK | нарушение невозможно на уровне БД |
| INV-MO2 ParentExists | GroupId имеет строку PropertyObjectTypeConfig (админка сперва создаёт настройку типа) | FK + проверка родителя при replace-all | INVEST_ANALYTICS_PROPERTY_OBJECT_TYPE_NOT_FOUND |
| INV-MO3 Ranges | Диапазоны из таблицы полей (месяц 1–12, год 2000–2100, доли 0..1, абсолюты > 0, месячные налоги ≤ 1/12) | валидация + check-constraints | ValidationError |
| INV-MO4 AtLeastOneValue | Строка содержит хотя бы одно not null-поле оверрайда (полностью пустой месяц не сохраняется — replace-all отбрасывает) | валидация admin-команды | ValidationError |
| INV-MO5 NoDuplicatesInPayload | В payload replace-all нет двух элементов с одинаковыми (Year, Month) | валидация | ValidationError |
| INV-MO6 ReplaceAllAtomic | Замена набора месяцев типа: удаление всех строк группы + вставка новых + инкремент Version родителя — в одном согласованном изменении | обработка admin-команды | — |
Граничные случаи replace-all
- Пустой список месяцев (payload
[]) — валиден: означает «очистить все помесячные оверрайды типа», все строки группы удаляются,Versionродителя инкрементируется, публикуетсяMonthOverridesReplaced. Отличать от полностью пустой строки месяца (все поля-оверрайдыnull) — та отбрасывается ещё валидацией (INV-MO4) и до записи не доходит. - Замена того же набора (payload идентичен текущему состоянию) — трактуется как правка: удаление +
вставка тех же строк в одном изменении,
Versionинкрементируется, событие публикуется (recompute пересчитает те же значения — идемпотентно по результату). - Порядок элементов в payload незначим; уникальность пары (
Year,Month) внутри payload обязательна (INV-MO5), иначеValidationErrorдо применения — частичной записи набора не происходит.
Доменные и интеграционные события
| Событие | Тип | Когда | Snapshot/поля |
|---|---|---|---|
PropertyObjectTypeMonthOverridesReplacedEvent | доменное | Replace-all набора месяцев типа | GroupId, число строк до/после, UpdatedByUserId — audit-лог |
Интеграционное событие: replace-all набора месяцев (PropertyObjectTypeMonthOverridesReplacedEvent)
публикует EconomicModelChangedKafkaEvent (pin.invest-analytics.model-changed.v1,
ChangeScope = PropertyObjectType, GroupId, ChangeKind = MonthOverridesReplaced) через outbox —
internal recompute job пересчитывает метрики затронутых объектов недвижимости
(../events/model-changed.md).
Индексы, хранение, безопасность
| Аспект | Значение |
|---|---|
| Индексы | composite PK (GroupId, Year, Month) — покрывает и выборку всех месяцев типа (GroupId-префикс), отдельные индексы не нужны |
| Партиционирование / retention | нет — единицы–сотни строк на всю платформу |
| Concurrency | через родителя: инкремент версии PropertyObjectTypeConfig при replace-all — конкурентные replace-all конфликтуют → 409 INVEST_ANALYTICS_PROPERTY_OBJECT_TYPE_CONFLICT |
| Permissions / PII | как у родителя: admin invest-analytics.property-object-types.view / invest-analytics.property-object-types.manage (ADR-0053). PII нет |
| Audit | через родителя (Version, actor-stamping) + PropertyObjectTypeMonthOverridesReplacedEvent |
Обратные ссылки
Автоссылки: где используется этот документ.
- Домен (CQRS) (4): Налоговые параметры расчёта: EntityKind, TaxRegime, Расчётные модели: InvestorMonth, AnalyticsSeries, AsideMetrics, Сущность / агрегат: EconomicModelConfig, Сущность / агрегат: PropertyObjectTypeConfig
- API (5): GET /api/admin/v1/property-object-types — Список настроек типов объектов недвижимости (admin), GET /api/admin/v1/property-object-types/{groupId}/month-overrides — Помесячные оверрайды типа объекта недвижимости (admin), GET /api/v1/property-objects/{propertyObjectId}/investor-series — Серии доходности объекта недвижимости, POST /api/admin/v1/investor-preview — Расчёт серий доходности по произвольным параметрам (admin preview / growth-chart), PUT /api/admin/v1/property-object-types/{groupId}/month-overrides — Replace-all помесячных оверрайдов типа объекта недвижимости (admin)
- Use Cases (3): UC-PRC-002. Расчёт доходности объекта недвижимости с выбором субъекта (юр/физ) и налогового режима, UC-PRC-003. Admin: настройка экономической модели платформы + investor-preview, UC-PRC-004. Admin: настройка типа объекта недвижимости и помесячных оверрайдов
- События (2): События invest-analytics: карта контрактов, Event Contract: pin.invest-analytics.model-changed (EconomicModelChangedKafkaEvent)
- index.md (1): InvestAnalytics Service
- registries (2): Реестр инвариантов PIN, Реестр сущностей PIN
Связанные документы
- Родитель-агрегат: property-object-type-config.md.
- Семантика коэффициентов: economic-model-config.md.
- Как оверрайды месяца входят в формулы (правила
?? / positiveOr / × 12): investor-series-models.md. - Admin-операции: ../api/admin/get-month-overrides.md, ../api/admin/put-month-overrides.md.