feat(mera/estimate): ручка фактов дома для предзаполнения формы + гейт «этаж не выше дома» #3257
No reviewers
Labels
No labels
Fable 5 ревью
GG-форсайт
admin
analytics
auth
automation
bug
business
chore
ci
compliance
data
data-moat
docs
duplicate
dx
enhancement
feedback/max
generative
needs-discussion
needs-human
observability
pause-bots
performance
priority/p0
priority/p1
priority/p2
priority/p3
scope/backend
scope/db
scope/devops
scope/frontend
scope/qa
scrapers
security
site-finder
stage/1
stage/2
status/blocked
status/done
status/needs-analysis
status/needs-fix
status/qa
status/ready
status/review
status/wip
tech-debt
tradein
ux
week ревью 1
wontfix
ИРД
вторичка
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference: lekss361/gendesign#3257
Loading…
Add table
Reference in a new issue
No description provided.
Delete branch "feat/3257-house-facts-prefill"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Summary
Пункт 5 плана сессии, backend-половина. Предзаполнение формы оценки из справочника
houses(наполнен из ГИС ЖКХ + DOM.РФ:total_floors98.3%,year_built86.8%) + гейт на невозможный ввод.1.
GET /api/v1/geocode/house-factsПараметры:
lat,lon(обязательны),fias_id(опционально).Ответ:
{found, total_floors, year_built, house_type, source}.Переиспользует
_lookup_house_factsиз estimator (#3234) — тот же поиск, что и при расчёте: поtarget_house_id, если ФИАС разрезолвился, иначе ближайший дом в 60 м. Значит форма предзаполнится ровно тем, что движок и возьмёт, — расхождения между показанным и посчитанным не будет.Дома нет в справочнике → 200 с
found=false, а не 404: это не ошибка запроса, а честный ответ об отсутствии данных.Почему отдельная ручка, а не поле в
/suggest. Suggest дёргается на каждое нажатие клавиши (пусть и с debounce), и запрос кhousesна каждый из 8 кандидатов на каждый keystroke был бы неоправданной нагрузкой на БД./house-factsвызывается ОДИН раз — после того как пользователь выбрал конкретный адрес.Резолв дома по ФИАС обёрнут в
try/exceptсrollback: предзаполнение формы не должно ронять запрос в 500, а незакрытая ошибка отравила бы сессию для последующего geo-фолбэка. Тот же best-effort, что и у_lookup_house_factsниже по цепочке.2. Гейт
floor <= total_floorsTradeInEstimateInputотклоняетfloor > total_floors, когда заданы оба поля. Частичный ввод легален — пользователь ещё печатает форму. Раньше «12 этаж в 9-этажном доме» уходил в расчёт молча и портил подбор аналогов по этажу (floor-вес в_compute_same_building_anchorсчитает относительную вертикальную позициюfloor/total_floors— при 12/9 она больше единицы).Test plan
pytest tests/test_3257_house_facts_endpoint.py tests/test_3234_house_facts_fallback.py tests/test_geocode_reverse_api.py— 24 passedfloor=12, total_floors=None)gar_house_guid,house_fias_id,zhkh_house_guid) существуют на проде — проверено запросом к information_schemaruff check+ruff format --checkчистоДальше
Фронтовая половина (дёрнуть ручку после выбора адреса, предзаполнить пустые поля с возможностью переопределить) — отдельным PR.
Две правки под предзаполнение формы оценки из справочника houses (наполнен из ГИС ЖКХ + DOM.РФ: total_floors 98.3%, year_built 86.8%). 1. GET /api/v1/geocode/house-facts?lat=&lon=[&fias_id=] отдаёт {found, total_floors, year_built, house_type, source}. Переиспользует _lookup_house_facts из estimator (#3234) — тот же поиск, что и при расчёте: по target_house_id если ФИАС разрезолвился, иначе ближайший дом в 60 м. Дома нет в справочнике → 200 с found=false, а не 404: это не ошибка запроса, а честный ответ об отсутствии данных. Отдельной ручкой, а не полем в /suggest, намеренно: suggest дёргается на каждое нажатие клавиши, и запрос к houses на каждый из 8 кандидатов на каждый keystroke был бы неоправданной нагрузкой. house-facts зовётся ОДИН раз, после выбора конкретного адреса. Резолв дома по ФИАС обёрнут в try/except с rollback — предзаполнение формы не должно ронять запрос в 500, а незакрытая ошибка отравила бы сессию для последующего geo-фолбэка. Тот же best-effort, что у _lookup_house_facts. 2. TradeInEstimateInput отклоняет floor > total_floors, когда заданы ОБА поля. Частичный ввод легален — пользователь ещё печатает форму. Раньше «12 этаж в 9-этажном доме» уходил в расчёт молча и портил подбор аналогов по этажу. Тесты: 10 штук в test_3257_house_facts_endpoint.py. Валидатор проверен вручную — отклоняет 12/9, пропускает 9/9 и частичный ввод.Уточнение к пункту 2, нашлось при разведке фронта.
Фронт уже валидирует
floor > total_floors—ParamsPanel.tsx(~строка 958), и в комментарии там прямо записано, почему: «prod incident 6d0c268e: floor=17, total_floors=9 was accepted silently — form + backend both let it through».То есть после того инцидента закрыли только форму, а бэкенд остался открыт. Этот PR закрывает вторую половину. Практически это значит, что мимо формы (прямой POST, автотест, интеграция) невозможный ввод по-прежнему проходил и портил floor-вес в
_compute_same_building_anchor, который считает относительную вертикальную позициюfloor/total_floors— при 17/9 она равна 1.89.Добавил фронтовую половину сюда же — отдельный PR не понадобился, кодогенерация типов относится к site-finder, а не к tradein-mvp, так что связки нет.
Что на фронте. После выбора адреса из автокомплита
pickSuggestionдёргает/house-factsи заполняет поля, которых пользователь не касался. Рядом с подставленным значением вместо обычной подсказки поля показывается «из справочника» — правит руками, пометка снимается.Три вещи, которые тут ломаются молча, поэтому сделаны явно:
AbortController— ответ на устаревший адрес не применяется, даже если пришёл последним.found=falseи сетевые сбои — тихий no-op.Тесты: 4 кейса, и они проверены мутацией — снятие гейта поколений и снятие условия «только пустое поле» роняют ровно по одному тесту каждое. Без этой проверки такие тесты легко оказываются пустыми.
npx tsc --noEmitиeslintчисто,vitestпо всей папкеv2/__tests__— 24 passed.