Dev Specs

Spec · E01 — Properties & Units [desktop]

Epic E01 — Properties & Units [desktop]

Назначение эпика: реестр объектов (Units) и карточка юнита с вкладками — единая точка управления свойством: профиль, зоны, задачи/события/проблемы по объекту, инвентарь и его движения, инфраструктура, дефолтные назначения по департаментам. Участвует в use-cases: UC-1 (recalc engine — Assignments/Services как вход), UC-2 (task lifecycle — Tasks/Issues/History-вкладки), UC-3 (инвентарь и его движения). Сущности (owns/touches): owns — Unit, UnitArea, InventoryItem, InventoryMove, InfraItem, AssignmentDefault. touches (read-only внутри эпика) — Task, Issue, Service, Reservation. Экраны: S08 (Property Card — реестр), S10 + S56 (Unit Profile/Details/Areas), S12 (Tasks-вкладка), S16 (History-вкладка), S06 + S17 (Issues-вкладка), S09 + S27 (Inventory + Moves), S18 (Infrastructure), S39 (Assignments / Department Defaults). «Copy of …»-дубли сведены в единые экраны с вкладками/состояниями. Открытые findings: F (NIT) filter-hierarchy-assignee — каскад фильтра назначения Department > Cluster > Assignee (задевает Assignments + фильтры таблиц); F (NIT) units-scope-live-only — показывать в реестре все юниты или только «live» (задевает S08).

Карта вкладок карточки юнита (единый tab-bar на всех экранах юнита): Profile · Areas · Tasks · Issues · History · Inventory · Infrastructure · Assignments. Header каждой вкладки: Unit Name + tab-bar. В прототипе вкладки IInventory/Infrustructure — опечатки исходника; здесь нормализованы как Inventory / Infrastructure.


S08 — Property Card (Units Registry) [desktop]

  • Назначение: реестр всех юнитов; точка входа в карточку объекта.
  • Источник (Miro): Property Card (id 3458764671288294568). controls: input_button×9, input_text×1, input_toggle×9.
  • Layout: header с fast filters + filters → data-table (строка = юнит). Toggle в каждой строке (×9 = ряды таблицы) — переключатель видимости/активности юнита (см. 🟡).
  • Данные на экране (колонки): Unit, Group, Location, ID, Status, Lark ID, Location Link, Vacancy. Пример строки: Villa Alaya / Cluster 1 / Ubud / 123456 / … / OCCUPIED|VACANT.
  • Контролы и действия:
    • fast filters (filter) — быстрые предустановленные фильтры.
    • filters (filter) — расширенные/сохранённые фильтры.
    • search (input_text) — поиск по таблице.
    • row click (button) → открыть карточку юнита на вкладке Profile (S10).
    • Location Link (button) — открыть Google Map локацию объекта.
    • per-row toggle (toggle ×9) — активность/видимость юнита в операционных списках (🟡).
  • Состояния/вкладки: default (список), filtered, empty (нет юнитов / фильтр пуст).
  • Связи (data-flow): читает Unit (+ геоиерархия Region→Area→SubArea→Cluster через Group/Location, текущая Vacancy из активных Reservation). Открывает карточку юнита (вкладочные экраны эпика).
  • Мутации: toggle активности юнита (update Unit.active, 🟡). Создание/редактирование юнита в прототипе не выявлено — read+navigate.
  • Права: Operations/Coordinators — все (department-scoped); Accounting/Owner Services — read; Field staff — нет доступа к desktop-реестру.
  • 🟡 [не было додумано]: (1) семантика per-row toggle не подписана в прототипе — трактуем как «активен/виден в операциях» (Unit.active), т.к. это единственное row-level действие и связано с findings units-scope-live-only. (2) + add unit — не было в прототипе; не добавляем (юниты, вероятно, заводятся извне/при онбординге), отмечаем как пробел.
  • Edge cases: пустой реестр; длинное Unit/Location — усечение с tooltip; Vacancy отсутствует (нет брони) → пусто/; нет прав → реестр скрыт.

S10 — Unit Profile (Profile · Details · Areas) [desktop]

  • Назначение: карточка объекта — паспорт юнита: реквизиты, геопривязка, перечень зон, привязанные сервисы.
  • Источник (Miro): Profile (id 3458764671312528295) — содержит под-секции Details и Areas; S56 (id 3458764671315309813) — «скелет» той же вкладки Profile (пустое состояние / каркас tab-bar). Сведено: S10 = заполненный Profile, S56 = его loading/empty-каркас.
  • Layout: header (Unit Name + tab-bar) → две колонки: левая Details (форма-паспорт), правая Areas (список зон с количеством) + блок Services.
  • Данные на экране:
    • Details: Property Name (Alaya), Lark ID (12345), Region (Bali), Area (Seminyak), Sub-Area (Legian), Cluster (1), Google Map Location, Tag, Priority, Keybox, Wifi Login, Wifi Password, PLN Meter.
    • Areas (UnitArea, name + count): General 1, Entrance 1, Pool 1, Bedroom 2, Bathroom 2, Living Room 2, Kitchen 1, Rooftop 0, Outside 1, Parking 1.
    • Services (привязанные сервисы объекта): Check-In, AC Maintenance.
  • Контролы и действия: tab-bar (button) → переключение вкладок юнита; Google Map Location (link) → карта. Прочее — отображение полей. Edit-режим полей в прототипе явно не показан → read (🟡).
  • Состояния/вкладки: активная вкладка Profile; S56 = empty/loading-каркас (поля ещё не загружены).
  • Связи (data-flow): читает Unit, UnitArea, и список Service где connected_properties содержит юнит (вход для recalc-движка UC-1). Areas — справочник зон для Requirements задач и фото-отчётов (E06).
  • Мутации: в прототипе — нет (просмотр). 🟡 редактирование паспорта — вероятная потребность фазы реализации.
  • Права: Operations/Coordinators — read (+edit на фазе реализации); Accounting/Owner Services — read; чувствительные поля (Wifi Password, Keybox) — ограничить по роли (🟡).
  • 🟡 [не было додумано]: (1) edit-форма паспорта не была в прототипе — отмечаем как пробел, не достраиваем UI. (2) Маскирование Wifi Password/Keybox для не-Operations ролей — безопасность; добавлено как требование, не как экран.
  • Edge cases: Rooftop 0 (зона декларирована, но count=0) — показывать; отсутствие сервисов → блок Services пуст; длинный Google Map Location — усечение/ссылка.

S39 — Assignments (Department Defaults) [desktop]

  • Назначение: дефолтные исполнители/ответственные по департаментам для объекта — кто по умолчанию получает задачи каждого департамента; плюс привязка Items/Systems/Services к назначениям.
  • Источник (Miro): Assignments / Department Defaults (id 3458764671318828829).
  • Layout: header (Unit Name + tab-bar, активна Assignments) → секция Department Defaults (список департаментов с + назначить) + секции Items, Systems, Services (тоже с +).
  • Данные на экране:
    • Department Defaults (департаменты): Cleaning, Inspection, Maintenance, Accounting, Property Services, Owner Services, Vendors → у каждого default-исполнитель.
    • Services (с привязкой назначения): Check-In, AC Maintenance.
    • блоки Items, Systems — привязка дефолтных назначений к инвентарю/инфраструктуре.
  • Контролы и действия: + (button, ×3+) рядом с Department Defaults / Services / Items-Systems → добавить назначение (выбрать assignee/vendor для департамента или объекта). tab-bar (button) → вкладки.
  • Состояния/вкладки: активная Assignments; empty (нет дефолтов — все департаменты без назначенного исполнителя).
  • Связи (data-flow): owns AssignmentDefault (department → assignee/vendor). Кормит recalc-движок (UC-1): при генерации service-task назначение берётся из Department Defaults юнита (см. 00-overview §5 «assign by Department Defaults»). Departments-список = роли из 00-overview §3.
  • Мутации: create/update/delete AssignmentDefault через + и редактирование строки.
  • Права: Operations/Coordinators — full; остальные — read.
  • 🟡 [не было додумано]: каскад выбора назначения Department > Cluster > Assignee (открытый finding filter-hierarchy-assignee) — структуру каскада подтвердить у заказчика; здесь фиксируем как «department → assignee/vendor», cluster-уровень помечен открытым.
  • Edge cases: департамент без дефолта → service-task создаётся без назначения (нужен fallback/предупреждение, 🟡); удаление assignee, у которого есть открытые задачи — поведение уточнить (🟡).

S12 — Tasks tab (per-unit) [desktop]

  • Назначение: все задачи конкретного юнита (плановые сервисные, прямые, issue-производные).
  • Источник (Miro): Tasks per-unit (id 3458764671290782337). controls: input_text×1, input_button×5.
  • Layout: header (Unit Name + tab-bar, активна Tasks) → fast filters + filters → data-table.
  • Данные на экране (колонки): ID, Task Name, Task, Status, Task Type, Created, Due, Finished, Assignment, Department, Sub-Department, Report. Примеры: 1 / AC Maintenance / Planned / … / Sarah Martinez / Operations; 2 / Check Wifi / Direct; 3 / Plumbing Repair / Maintenance Issue / Issue.
  • Контролы и действия: fast filters/filters (filter); search (input_text); row click (button) → Task Detail (экран E03); Report (button) → связанный Maintenance/Area report (E06). Кнопки (×5) = фильтры/действия таблицы.
  • Состояния/вкладки: активная Tasks; filtered (по Status/Type/Department); empty (нет задач у юнита).
  • Связи (data-flow): читает Task где unit = текущий (тип Direct|Schedule|Issue|Reservation, статусы Planned|New|In Progress|Finished). Источник задач — recalc-движок (Service+Template), прямое создание, Issue. Уходит в Task Detail (E03), Reports (E06).
  • Мутации: в этом экране — нет (список+навигация). Создание/правка — в E03.
  • Права: department-scoped: Operations/Coordinators видят задачи своих департаментов; Accounting — финансовые поля задач; Field staff — desktop недоступен.
  • 🟡 [не было додумано]: нет.
  • Edge cases: пусто (новый юнит без сервисов); длинные имена задач; Finished пуст для не завершённых; задача без Assignment (см. S39 fallback).

S16 — History tab (per-unit) [desktop]

  • Назначение: хронология событий по юниту — reservation-события, завершённые задачи, заведённые issue (единая лента).
  • Источник (Miro): History per-unit (id 3458764671310179054). controls: input_text×1, input_dropdown×1, input_button×1.
  • Layout: header (Unit Name + tab-bar, активна History) → фильтр-дропдаун (тип события) → data-table.
  • Данные на экране (колонки): Event Date, Event Type, Event, Task Type, Assignment, Department, Sub-Department, Created, Due, Finished, Report. Примеры строк: Reservation / Check Out; Task / Task Finished / Direct; Issue / Issue Reported / Maintenance Issue.
  • Контролы и действия: event-type dropdown (dropdown) → фильтр по типу события; row click (button) → к источнику (Task Detail / Issue detail / Reservation); Report (button) → отчёт.
  • Состояния/вкладки: активная History; filtered (по Event Type); empty (нет событий).
  • Связи (data-flow): агрегирует прошедшие факты: Reservation (check-in/out), Task (finished), Issue (reported) для данного юнита. Read-only лента (события — immutable past-tense, см. 00-overview §5).
  • Мутации: нет (журнал, только чтение).
  • Права: Operations/Coordinators — read; Owner Services/Accounting — read.
  • 🟡 [не было додумано]: нет (модель события вытекает из TaskEvent + reservation/issue фактов).
  • Edge cases: пустая история (новый юнит); большой объём → пагинация/виртуализация (🟡 для реализации); событие без Report → пусто.

S06 + S17 — Issues tab (per-unit) [desktop]

  • Назначение: проблемы, заведённые по юниту, и их связь с задачами-исполнителями. Сведены два представления одной вкладки.
  • Источник (Miro): S17 Issues per-unit (id 3458764672842040021) — issue-ориентированный список; S06 (id 3458764672829442950) — расширенная вкладка Tasks/Issues/History с задаче-ориентированными колонками. Сведено: одна вкладка Issues с двумя представлениями (Issues-view = S17, Linked-Tasks-view = S06).
  • Layout: header (Unit Name + tab-bar, активна Issues; видны под-табы Tasks · Issues · History) → fast filters + filters → data-table.
  • Данные на экране (колонки, Issues-view / S17): ID, Issue, Description, Reported at, Department, Associated Task, Task Status, Task Report. Примеры: 1 / AC Maintenance / Operations; 2 / Check Wifi / Direct; 3 / Plumbing Repair / Maintenance Issue.
    • Linked-Tasks-view (S06): ID, Task Name, Task, Status, Task Type, Created, Due, Finished, Assignment, Department, Sub-Department, Report (= задачи, порождённые issue).
  • Контролы и действия: fast filters/filters (filter); search (input_text); row click (button) → Issue detail (S40, эпик E05) или Associated Task (E03); Task Report (button) → отчёт. Кнопки (×4) = действия/фильтры таблицы.
  • Состояния/вкладки: активная Issues, два представления (Issues / Linked Tasks); filtered; empty (нет проблем у юнита).
  • Связи (data-flow): читает Issue где unit = текущий (+ Associated Task через Task.issue_id). Создание/детали issue — эпик E05 (S40). Здесь — список+навигация в контексте юнита.
  • Мутации: на вкладке — нет (детали/создание issue в E05).
  • Права: Operations/Coordinators — read (department-scoped); создание issue — на исполнении задачи (mobile, E04) / в E05.
  • 🟡 [не было додумано]: сведение S06+S17 в одну вкладку с переключателем представления — в прототипе это были разные «Copy of»-кадры одной вкладки; объединяем во избежание дублирования экранов.
  • Edge cases: issue без Associated Task (ещё не назначена) → Task Status пуст; пустой список; длинный Description — усечение.

S09 + S27 — Inventory tab + Moves (per-unit) [desktop]

  • Назначение: инвентарь объекта (предметы по комнатам) и журнал движений каждого предмета (приход/списание/корректировки с балансами).
  • Источник (Miro): S27 Inventory per-unit (id 3458764671316196158) — список предметов; S09 (id 3458764672820350781) — журнал InventoryMove (движения с балансами). Сведено: вкладка Inventory = список предметов (S27) + drill-in в журнал движений предмета (S09).
  • Layout: header (Unit Name + tab-bar, активна Inventory) → fast filters + filters → data-table предметов; клик по предмету → таблица движений (S09).
  • Данные на экране:
    • Items (S27): Item, Item Type (Furniture/Kitchenware/…), Status, Amount, Room, Created, Written Off, Photos, Comments. Примеры: Chair / Furniture / 1 / Kitchen 1; Table / Furniture / 0 / Living Room 1; Spoon / Kitchenware / 10 / Kitchen 1.
    • Moves (S09): Move ID, Item, Item ID, Item Type, Status, Action, Amount, Balance Before, Balance After, Created, Voided, Reason, Created by, Voided by, Photos, Comments. Actions: New Item Purchase, Entry Error (= void/correction).
  • Контролы и действия: fast filters/filters (filter); search (input_text); кнопки (×6–8) = действия/фильтры; < / > (button) — навигация назад/детали; row click (item) → журнал движений предмета; Photos/Comments (button) → вложения/комментарии.
  • Состояния/вкладки: активная Inventory; under-view: Items / Moves; filtered; empty (нет инвентаря).
  • Связи (data-flow): owns InventoryItem + InventoryMove. Текущий Amount = Balance After последнего непогашенного move. Voided-движения (Entry Error) не меняют баланс задним числом — корректируются новым move (append-only журнал).
  • Мутации: create InventoryMove (New Item Purchase, списание); void move (Voided/Voided by/Reason = Entry Error); create InventoryItem. Прямое редактирование Amount запрещено — только через move (🟡).
  • Права: Operations/Coordinators — full; Accounting — read (+стоимость, если есть); Field staff — нет (desktop).
  • 🟡 [не было додумано]: (1) правило «баланс меняется только через move, прямого редит Amount нет» — append-only журнал движений вытекает из колонок Balance Before/After+Voided, но прямо не подписано; фиксируем как инвариант. (2) + add item / + new move — кнопки-действия подразумеваются (input_button×6–8), точные лейблы в прототипе не текстованы.
  • Edge cases: Amount 0 (Table) — показывать как «out of stock», не скрывать; voided move → отдельная пометка, исключён из текущего баланса; предмет без движений (только заведён); длинный Reason/Comments — усечение.

S18 — Infrastructure tab (per-unit) [desktop]

  • Назначение: инфраструктурные/инженерные элементы объекта (системы безопасности, пул, CCTV и т.п.) с привязкой к комнате, статусом и документами.
  • Источник (Miro): Infrastructure per-unit (id 3458764671317560393). controls: input_button×5, input_text×1.
  • Layout: header (Unit Name + tab-bar, активна Infrastructure) → fast filters + filters → data-table.
  • Данные на экране (колонки): Item, System (Fire Safety/Pool/CCTV), Status, UID, Room, Created, Written Off, Photos, Documents. Примеры: Smoke Detector / Fire Safety / Kitchen 1 / uid 12333534234; Pool Pump / Pool / Pool; Camera / CCTV / Entrance.
  • Контролы и действия: fast filters/filters (filter); search (input_text); кнопки (×5) = действия/фильтры (+ add item, фильтры по System/Status); row click (button) → деталь инфра-элемента (вложения/документы); Photos/Documents (button) → файлы.
  • Состояния/вкладки: активная Infrastructure; filtered (по System/Status); empty (нет инфра-элементов).
  • Связи (data-flow): owns InfraItem (per-unit). Связь с Issue (S40 поле Infrastructure Item:) — issue может ссылаться на инфра-элемент; связь с Service/Template (системы → TaskTemplate sections). Read+manage в контексте юнита.
  • Мутации: create/update InfraItem; Written Off (списание/вывод из эксплуатации); attach Photos/Documents.
  • Права: Operations/Coordinators — full; Maintenance — read/update своих систем; Accounting/Owner Services — read.
  • 🟡 [не было додумано]: Written Off как переход состояния (а не просто дата) — трактуем как вывод из эксплуатации (аналог inventory void); прямо подписана только колонка-дата.
  • Edge cases: элемент без Room; Written Off заполнен → показывать приглушённо/в отдельном фильтре «decommissioned»; нет фото/документов → пустые ячейки; длинный UID — моноширинный/усечение.

Cross-screen relations (внутри эпика)

S08 Property Card (registry)
   └─ row click ─► Unit Card (общий tab-bar):
        Profile (S10 / S56 каркас)
        Areas        (часть S10, правая колонка)
        Tasks        (S12) ──► Task Detail (E03)  /  Report (E06)
        Issues       (S06+S17) ──► Issue detail (E05·S40) / Associated Task (E03)
        History      (S16) ──► источник события (Task/Issue/Reservation)
        Inventory    (S27 items) ──► Moves journal (S09)
        Infrastructure (S18) ──► item detail (photos/documents) ; ←→ Issue.Infrastructure Item (E05)
        Assignments  (S39) ──► Department Defaults кормят recalc-движок (UC-1, E02)

Profile.Services (S10) ──► Service (E02) ──recalc──► Tasks (S12)
Assignments.Department Defaults (S39) ──► assignee для сгенерированных service-tasks

Решения заказчика (2026-06-22)

Ответы со встречи Akira × Sergey (реестр 00-client-questions.md). Перекрывают соответствующие 🟡-допущения ниже. Перенос в вёрстку — отдельными PDD-проходами.

  • Q-12 · units-scope-live-only — РЕШЕНО (resolves §5 per-row toggle + finding units-scope-live-only):
    • Источник истины юнитов — Lark. Импортируем все PM-юниты: всё, где pmStatus != "not in management" (статусы launching / live / terminated).
    • Хранить ВСЕ юниты (нужна история); реестр S08 по умолчанию фильтрует только live (активные).
    • В модель Unit добавить поле larkStatus (pmStatus) в дополнение к внутреннему status/active — две раздельные колонки Status (внутр.) и Lark Status (из Lark).
    • Синхронизация: первичный bulk-импорт из Lark, далее push из Lark при изменении статуса юнита (НЕ поллинг — pm-статус меняется ~раз в неделю на юнит). Бэкенд-механика.
    • Дельта к вёрстке (S08): дефолт-фильтр все → live; добавить поле/колонку Lark Status. → Tier B (Properties).
  • Q-10 · occupancy (деталь — в E02) — РЕШЕНО: колонка Vacancy (S08) = производная Occupied/Vacant из броней Hostify (ровно два значения). Подтверждает текущие OCCUPIED/VACANT.
  • Q-07 · auto-assignment (S39 Department Defaults) — ЧАСТИЧНО: иерархия = department → cluster → property (полное «качественное описание эпика» — в E03-решениях, action Сергея). S39 — носитель property-override; cluster-уровень и edge-case «проперти в кластере, но исполнитель не в команде кластера» учесть. Edge-cases — Akira с Соей. Каскад фильтра Department > Cluster > Assignee (finding filter-hierarchy-assignee) — та же иерархия.

Дозумано на уровне эпика 🟡

  1. Нормализация tab-bar — единый набор вкладок Profile · Areas · Tasks · Issues · History · Inventory · Infrastructure · Assignments на всех экранах юнита; исправлены опечатки прототипа IInventoryInventory, InfrustructureInfrastructure. Отклонение от прототипа: косметика, состав вкладок не меняется.
  2. Сведение «Copy of»-дублей — S06+S17 → одна вкладка Issues (2 представления); S09+S27 → Inventory (items + drill-in moves); S56 → empty/loading-каркас Profile (S10). Цель: убрать дублирующие кадры, не плодить экраны.
  3. Inventory append-only инвариант — баланс меняется только через InventoryMove (включая void Entry Error), прямого редактирования Amount нет. Вытекает из колонок Balance Before/After+Voided, прямо не подписано.
  4. Безопасность чувствительных полей — маскирование Wifi Password / Keybox Code для не-Operations ролей (паспорт юнита). Требование, не экран.
  5. Per-row toggle в реестре (S08) — трактуем как активность/видимость юнита; связано с открытым finding units-scope-live-only (все юниты vs только «live»).
  6. Каскад назначения (S39)Department > Cluster > Assignee помечен открытым (finding filter-hierarchy-assignee); в спеке зафиксировано минимальное department → assignee/vendor, cluster-уровень требует подтверждения.
  7. Edit-формы паспорта/создание юнита — в прототипе не показаны; помечены как пробелы скелета, UI не достраивается (заведение юнитов, вероятно, на онбординге/извне).