fix(report): авторетрай §7 с densify при «не вместила ни одной секции»
All checks were successful
CI Trade-In / frontend-checks (pull_request) Has been skipped
CI / backend-tests (pull_request) Has been skipped
CI Trade-In / changes (pull_request) Successful in 7s
CI / changes (pull_request) Successful in 7s
CI Trade-In / backend-tests (pull_request) Has been skipped
CI / frontend-tests (pull_request) Successful in 53s
CI / openapi-codegen-check (pull_request) Successful in 1m51s

Автозапуск концепции (#2179) на узких/мелких участках отдавал 422 при
загрузке отчёта: дефолтная программа (3 корпуса comfort 21×18) честно не
влезает в геометрию (прод-кейс 66:41:0702033:20, найден browser-capture'ом
реального payload — фронт шлёт валидный Polygon, extractPolygon ни при чём).

Одноразовый авторетрай с densify=true (greedy max-FAR — движок сам вмещает,
что физически влезает); ручные запуски не ретраятся, ошибка честно
показывается если не влезло и так.
This commit is contained in:
bot-backend 2026-07-03 05:28:32 +05:00
parent 1bba3db7eb
commit 15db2b07c6

View file

@ -212,6 +212,14 @@ export function Section7Concept({ analysis }: Props) {
// <details> via the summary; onToggle keeps `expanded` in sync when reopened.
const [expanded, setExpanded] = useState(true);
// Одноразовый авторетрай (fix 2026-07-03): на узких/мелких участках дефолтная
// программа (3 корпуса comfort 21×18) честно не влезает — движок отвечает 422
// «программа застройки не вместила ни одной секции». Показывать это ошибкой
// при ЗАГРУЗКЕ отчёта нельзя (прод-кейс 66:41:0702033:20) — пробуем один раз
// densify=true: greedy-раскладка сама вмещает, что физически влезает.
// Не влезло и так → честная ошибка остаётся. Ручные запуски не ретраятся.
const autoRetriedRef = useRef(false);
useEffect(() => {
if (autoRanRef.current || !expanded) return;
if (!polygon || catalog.length === 0) return;
@ -233,6 +241,27 @@ export function Section7Concept({ analysis }: Props) {
});
}, [expanded, polygon, catalog, concept, seeded, handleGenerate]);
useEffect(() => {
if (!autoRanRef.current || autoRetriedRef.current) return;
if (!concept.isError || !polygon || catalog.length === 0) return;
if (!/не вместила/i.test(concept.error.message)) return;
const def = pickDefaultType(catalog);
if (!def) return;
autoRetriedRef.current = true;
handleGenerate({
corpuses: CORPUSES_DEFAULT,
floors: def.default_floors > 0 ? def.default_floors : FLOORS_FALLBACK,
floor_height_m: 3.0,
section_type: def.section_type,
footprint_w_m: def.footprint_w_m,
footprint_d_m: def.footprint_d_m,
housing_class: seeded.housing_class,
development_type: seeded.development_type,
land_cost_rub: seeded.land_cost_rub,
densify: true, // greedy max-FAR: вместить, что физически влезает
});
}, [concept, polygon, catalog, seeded, handleGenerate]);
const handleToggle = useCallback(
(e: React.SyntheticEvent<HTMLDetailsElement>) => {
if (e.currentTarget.open) setExpanded(true);