fix(tradein-api): PlacementHistoryEntry schema + None checks (PR #528 follow-up) #532
No reviewers
Labels
No labels
admin
analytics
auth
automation
bug
business
chore
ci
compliance
data
data-moat
docs
duplicate
dx
enhancement
Fable 5 ревью
feedback/max
generative
GG-форсайт
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#532
Loading…
Add table
Reference in a new issue
No description provided.
Delete branch "fix/tradein-placement-history-schema"
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?
Context
Follow-up to PR #528 review-bot APPROVE with 2 non-blocking Medium findings.
Changes
1.
PlacementHistoryEntryPydantic schemaschemas/trade_in.py— добавлена 17-field BaseModel matchinghouse_placement_historycolumns (id, source, house_id, ext_item_id, title, rooms, area_m2, floor, total_floors, start_price/date, last_price/date, removed_date, exposure_days, notes). Размещена междуIMVBenchmarkResponseиScheduleConfig.api/v1/trade_in.py:@router.get("/estimate/{estimate_id}/placement-history", response_model=list[PlacementHistoryEntry])— былоlist[dict]без типа[PlacementHistoryEntry(**dict(r)) for r in history](Pydantic validation + OpenAPI doc)Эффект: consistent с
/houses(list[HouseInfoForEstimate]) и/imv-benchmark(IMVBenchmarkResponse). UI получает typed response.2. Truthy lat/lon →
is not Noneapi/v1/trade_in.py:421, 493— былоif target.lat and target.lon:, теперьis not None. Defensive противlat=0.0(EKB lat≈56.8 безопасен, но при region expansion в Калининград/Сахалин 0 теоретически возможен).Roadmap
Follow-up к PR #528. Не блокер был, no production impact. Полная цепочка backfill+UI: #527 → #528 → #529 → this PR.
Verify
Deep Code Review — verdict: APPROVE
Summary
Verified
PlacementHistoryEntryschema cross-check — все 16 полей matcheshouse_placement_history(017_house_placement_history.sql + 045_extend.sql):id bigserial→int✓source text NOT NULL→str(required) ✓house_id bigint REFERENCES houses(nullable per DDL) →int | None✓ext_item_id text NOT NULL→str(required) ✓area_m2 numeric(8,2)→float | None✓ (Pydantic v2 lax mode coerces Decimal→float)start_price/last_price bigint→int | None✓start_price_date/last_price_date/removed_date date→date | None✓ (psycopg v3 returns nativedatetime.date)exposure_days int,rooms,floor,total_floors,title,notes— nullable correctly ✓response_model=list[PlacementHistoryEntry]правильно подключён. JSON shape backward-compatible — keys те же, dates сериализуются в ISO, Decimal→float. FrontendPlacementHistoryCard(#529) gracefully handles все nullable поля (fmtPrice(null) → "—",lotLabeldefaults).is not Nonefix — корректное закрытие edge case lat=0.0/lon=0.0:trade_in_estimates.lat/lon=double precision(per 001_trade_in_estimates.sql)if x and y:падал в false-branch (skip geo fallback) — bugis not Noneтеперь корректно разрешает 0.0 (хоть и не realistic для ЕКБ, но семантика правильная)Query безопасность —
WHERE house_id = ANY(:house_ids)фильтрует NULL house_id, поэтому frontend никогда не получит null house_id (de-facto backward-compat сPlacementHistoryItem.house_id: numberв #529).No CAST/IS NULL trap —
is not Noneтут в Python-коде, не в SQL bound params; psycopg v3 AmbiguousParameter не релевантен.Notes (non-blocking, follow-up если захотите)
PlacementHistoryItem.house_id: number(#529) можно ослабить доnumber | nullдля type-parity с backendint | None. Не влияет на runtime (SQL фильтрует NULL)./placement-historyendpoint отсутствует — consistent с codebase style (tests/ покрывают только parsers), OK как nit.Risk / blast radius
Verdict
Merge ready.