(high) page.tsx / KpiCard «Sold % (квартиры)»: вердикт «выше/ниже среднего рынка» считается по собственному sold% объекта без рыночных данных #1407

Closed
opened 2026-06-15 05:53:37 +00:00 by bot-backend · 1 comment
Collaborator

Severity: high · Категория: report · Файл: frontend/src/app/analytics/objects/[id]/page.tsx:147-164

page.tsx:147-164 (KpiCard «Sold % (квартиры)»). delta.value вычисляется так: apartments.perc >= 50 ? 'выше среднего рынка' : apartments.perc >= 30 ? 'около среднего' : 'ниже среднего', positive: apartments.perc >= 30. apartments берётся из agg = useObjectSalesAgg(objId), а тип ObjectSalesAggRow (frontend/src/types/analytics.ts:176-182) содержит только {type, name, total, realised, perc} по ОДНОМУ объекту — никакого городского/районного среднего sold% в компонент не приходит. perc — это собственная распроданность объекта (realised/total из DOM.РФ), а подпись интерпретирует абсолютное значение как позицию ОТНОСИТЕЛЬНО рынка. Пороги 30/50 захардкожены и не привязаны к распределению сделок. Импакт: объект с 55% на рынке со средним 80% получит зелёный вердикт «выше среднего рынка» — ложь в клиентском дашборде для финдиректора (нарушает ui-microcopy: caveat должен опираться на реальное распределение). Как чинить: либо убрать формулировку про рынок и показывать нейтральную метрику (delta.positive: null, текст без «рынка»), либо реально подтянуть рыночное среднее sold% (city/district avg) из бэкенда и сравнивать с ним.

Почему баг: perc — это собственный sold% объекта (realised/total из DOM.РФ), а подпись интерпретирует его как позицию ОТНОСИТЕЛЬНО рынка («выше/около/ниже среднего рынка»). Это не математика, а подмена: объект с распроданностью 55% на горячем рынке, где среднее 80%, получит зелёный вердикт «выше среднего рынка», что прямо ложь в клиентском дашборде для финдиректора. Пороги 30/50 произвольны и не привязаны ни к какому распределению сделок. По правилам проекта (ui-microcopy: caveat про «распределение сделок») такие утверждения должны опираться на реальные данные.

Как чинить: Убрать псевдо-рыночную дельту либо заменить её на честную: тянуть городское/районное среднее sold% (есть в analytics_queries: prinzip/districts, sold_perc по region) и сравнивать с ним, выводя реальную разницу в п.п. Если рыночного среднего нет — показывать только нейтральный sold% без слова «рынка».


deep-audit «армия ботов 2» · verify+harden, confidence 0.9 · unit U74

**Severity:** high · **Категория:** report · **Файл:** `frontend/src/app/analytics/objects/[id]/page.tsx:147-164` page.tsx:147-164 (KpiCard «Sold % (квартиры)»). delta.value вычисляется так: apartments.perc >= 50 ? 'выше среднего рынка' : apartments.perc >= 30 ? 'около среднего' : 'ниже среднего', positive: apartments.perc >= 30. apartments берётся из agg = useObjectSalesAgg(objId), а тип ObjectSalesAggRow (frontend/src/types/analytics.ts:176-182) содержит только {type, name, total, realised, perc} по ОДНОМУ объекту — никакого городского/районного среднего sold% в компонент не приходит. perc — это собственная распроданность объекта (realised/total из DOM.РФ), а подпись интерпретирует абсолютное значение как позицию ОТНОСИТЕЛЬНО рынка. Пороги 30/50 захардкожены и не привязаны к распределению сделок. Импакт: объект с 55% на рынке со средним 80% получит зелёный вердикт «выше среднего рынка» — ложь в клиентском дашборде для финдиректора (нарушает ui-microcopy: caveat должен опираться на реальное распределение). Как чинить: либо убрать формулировку про рынок и показывать нейтральную метрику (delta.positive: null, текст без «рынка»), либо реально подтянуть рыночное среднее sold% (city/district avg) из бэкенда и сравнивать с ним. **Почему баг:** perc — это собственный sold% объекта (realised/total из DOM.РФ), а подпись интерпретирует его как позицию ОТНОСИТЕЛЬНО рынка («выше/около/ниже среднего рынка»). Это не математика, а подмена: объект с распроданностью 55% на горячем рынке, где среднее 80%, получит зелёный вердикт «выше среднего рынка», что прямо ложь в клиентском дашборде для финдиректора. Пороги 30/50 произвольны и не привязаны ни к какому распределению сделок. По правилам проекта (ui-microcopy: caveat про «распределение сделок») такие утверждения должны опираться на реальные данные. **Как чинить:** Убрать псевдо-рыночную дельту либо заменить её на честную: тянуть городское/районное среднее sold% (есть в analytics_queries: prinzip/districts, sold_perc по region) и сравнивать с ним, выводя реальную разницу в п.п. Если рыночного среднего нет — показывать только нейтральный sold% без слова «рынка». --- <sub>deep-audit «армия ботов 2» · verify+harden, confidence 0.9 · unit U74</sub>
bot-backend added the
week ревью 1
label 2026-06-15 05:53:37 +00:00
bot-backend added the
scope/frontend
label 2026-06-15 11:01:59 +00:00
Owner

🔍 week ревью 1 — верификация (ПОДТВЕРЖДЕНО)

Текущее состояние кода: frontend/src/app/analytics/objects/[id]/page.tsx:139-156 (KpiCard «Sold % (квартиры)»); тип — frontend/src/types/analytics.ts:176-182 (ObjectSalesAggRow).

Корень: delta.value (page.tsx:147-152) интерпретирует apartments.perc (собственный sold% объекта = realised/total из DOM.РФ) как позицию относительно рынка: >=50 → «выше среднего рынка», >=30 → «около среднего», иначе «ниже среднего», positive: perc>=30. В компонент приходит только per-object {type,name,total,realised,perc} — никакого городского/районного среднего sold% нет. Пороги 30/50 захардкожены и не привязаны к распределению сделок. Объект с 55% на рынке со средним 80% получает зелёный «выше среднего рынка» — ложь в дашборде для финдиректора (нарушает ui-microcopy: caveat должен опираться на реальное распределение).

Что править:

  • Минимальный вариант: убрать формулировку про «рынок» — delta.positive: null (нейтрально, как для средней цены по ui-conventions) или вовсе убрать delta, оставив нейтральный sold%.
  • Полный вариант: тянуть city/district avg sold% с бэкенда (источник: district_economics.real_sold_pct в site-finder, либо analytics_queries districts/region) и выводить реальную разницу в п.п.

Что посмотреть:

  • Код: page.tsx:139-156; useObjectSalesAgg (хук, отдаёт ObjectSalesAggRow); компонент components/analytics/KpiCard.tsx (семантика delta.positive: null).
  • Vault: релевантных заметок не найдено.
**🔍 week ревью 1 — верификация (ПОДТВЕРЖДЕНО)** **Текущее состояние кода:** `frontend/src/app/analytics/objects/[id]/page.tsx:139-156` (KpiCard «Sold % (квартиры)»); тип — `frontend/src/types/analytics.ts:176-182` (`ObjectSalesAggRow`). **Корень:** `delta.value` (page.tsx:147-152) интерпретирует `apartments.perc` (собственный sold% объекта = realised/total из DOM.РФ) как позицию относительно рынка: `>=50 → «выше среднего рынка»`, `>=30 → «около среднего»`, иначе «ниже среднего», `positive: perc>=30`. В компонент приходит только per-object `{type,name,total,realised,perc}` — никакого городского/районного среднего sold% нет. Пороги 30/50 захардкожены и не привязаны к распределению сделок. Объект с 55% на рынке со средним 80% получает зелёный «выше среднего рынка» — ложь в дашборде для финдиректора (нарушает ui-microcopy: caveat должен опираться на реальное распределение). **Что править:** - Минимальный вариант: убрать формулировку про «рынок» — `delta.positive: null` (нейтрально, как для средней цены по ui-conventions) или вовсе убрать `delta`, оставив нейтральный sold%. - Полный вариант: тянуть city/district avg sold% с бэкенда (источник: district_economics.real_sold_pct в site-finder, либо `analytics_queries` districts/region) и выводить реальную разницу в п.п. **Что посмотреть:** - Код: `page.tsx:139-156`; `useObjectSalesAgg` (хук, отдаёт `ObjectSalesAggRow`); компонент `components/analytics/KpiCard.tsx` (семантика `delta.positive: null`). - Vault: релевантных заметок не найдено.
bot-backend added the
needs-human
label 2026-06-15 16:44:06 +00:00
Sign in to join this conversation.
No milestone
No project
No assignees
2 participants
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference: lekss361/gendesign#1407
No description provided.