From 5cce0baf10032ee9a7a54da435f01d5ae584730e Mon Sep 17 00:00:00 2001 From: bot-backend Date: Sat, 4 Jul 2026 16:00:39 +0300 Subject: [PATCH] =?UTF-8?q?feat(tradein/history):=20=D0=B2=D0=B5=D1=80?= =?UTF-8?q?=D0=BD=D1=83=D1=82=D1=8C=20floor/total=5Ffloors/year=5Fbuilt/ho?= =?UTF-8?q?use=5Ftype/repair=5Fstate/has=5Fbalcony=20=D0=B2=20GET=20/histo?= =?UTF-8?q?ry?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit /history отдавал только address/rooms/area_m2 — остальные введённые параметры уже персистятся в trade_in_estimates, но не выбирались; расширяем SELECT для витрины «Предыдущие оценки» (CacheView) на фронте (follow-up фронт-PR). --- tradein-mvp/backend/app/api/v1/trade_in.py | 9 +- .../backend/tests/test_history_scope.py | 83 +++++++++++++++++++ 2 files changed, 91 insertions(+), 1 deletion(-) diff --git a/tradein-mvp/backend/app/api/v1/trade_in.py b/tradein-mvp/backend/app/api/v1/trade_in.py index 71d1b094..f4fed5c4 100644 --- a/tradein-mvp/backend/app/api/v1/trade_in.py +++ b/tradein-mvp/backend/app/api/v1/trade_in.py @@ -646,6 +646,11 @@ def estimate_history( #2043 (BE-1): в проекцию добавлен sources_used (jsonb-массив источников) — разлочивает колонку «ИСТОЧНИКОВ N/7» в CacheView (len(sources_used) на строку). + + #2417: добавлены floor/total_floors/year_built/house_type/repair_state/ + has_balcony — уже персистятся в trade_in_estimates при создании оценки + (см. TradeInEstimateInput / AggregatedEstimate), но раньше не выбирались + здесь. Nullable — legacy строки / незаполненные поля отдаются как None. """ if not x_authenticated_user: raise HTTPException( @@ -682,7 +687,9 @@ def estimate_history( f""" SELECT id, address, rooms, area_m2, median_price, confidence, n_analogs, created_at, - COALESCE(sources_used, '[]'::jsonb) AS sources_used + COALESCE(sources_used, '[]'::jsonb) AS sources_used, + floor, total_floors, year_built, + house_type, repair_state, has_balcony FROM trade_in_estimates {where} ORDER BY created_at DESC diff --git a/tradein-mvp/backend/tests/test_history_scope.py b/tradein-mvp/backend/tests/test_history_scope.py index 56a58ba2..b031f5e1 100644 --- a/tradein-mvp/backend/tests/test_history_scope.py +++ b/tradein-mvp/backend/tests/test_history_scope.py @@ -178,3 +178,86 @@ def test_admin_filters_by_account(trade_in_app: FastAPI) -> None: assert "created_by = :account" in sql assert params["account"] == "kopylov" assert "owner" not in params + + +def test_history_returns_full_input_params(trade_in_app: FastAPI) -> None: + """#2417: /history проецирует floor/total_floors/year_built/house_type/ + + repair_state/has_balcony в дополнение к базовому подмножеству, включая + NULL-passthrough для строк, где эти поля никогда не заполнялись. + """ + db_mock = _make_db_mock( + [ + { + "id": "1", + "address": "A", + "rooms": 2, + "area_m2": 50, + "median_price": 5_000_000, + "confidence": "medium", + "n_analogs": 7, + "created_at": "2026-05-29T00:00:00", + "sources_used": ["avito", "cian"], + "floor": 3, + "total_floors": 9, + "year_built": 2005, + "house_type": "panel", + "repair_state": "good", + "has_balcony": True, + }, + { + "id": "2", + "address": "B", + "rooms": 1, + "area_m2": 33, + "median_price": 3_000_000, + "confidence": "low", + "n_analogs": 2, + "created_at": "2026-05-28T00:00:00", + "sources_used": [], + # legacy row / поля никогда не заполнялись пользователем. + "floor": None, + "total_floors": None, + "year_built": None, + "house_type": None, + "repair_state": None, + "has_balcony": None, + }, + ] + ) + client = _client_with(trade_in_app, db_mock, role="admin") + resp = client.get( + "/api/v1/trade-in/history", + headers={"X-Authenticated-User": "admin"}, + ) + assert resp.status_code == 200 + + sql, _params = _captured_sql_and_params(db_mock) + for col in ( + "floor", + "total_floors", + "year_built", + "house_type", + "repair_state", + "has_balcony", + ): + assert col in sql + + body = resp.json() + assert len(body) == 2 + + filled, legacy = body[0], body[1] + assert filled["floor"] == 3 + assert filled["total_floors"] == 9 + assert filled["year_built"] == 2005 + assert filled["house_type"] == "panel" + assert filled["repair_state"] == "good" + assert filled["has_balcony"] is True + + # NULL passthrough — не должно рейзить/подменяться дефолтами. + assert legacy["floor"] is None + assert legacy["total_floors"] is None + assert legacy["year_built"] is None + assert legacy["house_type"] is None + assert legacy["repair_state"] is None + assert legacy["has_balcony"] is None