From 36fe33750f12654e18045c08875dd5aa945ae5be Mon Sep 17 00:00:00 2001 From: bot-backend Date: Fri, 3 Jul 2026 23:44:19 +0000 Subject: [PATCH] =?UTF-8?q?fix(report):=20=D0=B4=D0=B0=D1=82=D0=B0=20?= =?UTF-8?q?=D1=81=D0=B1=D0=BE=D1=80=D0=BA=D0=B8=20=D0=B2=20POST-=D0=BE?= =?UTF-8?q?=D1=82=D0=B2=D0=B5=D1=82=D0=B5=20=D0=BD=D0=B0=20fast-path=20?= =?UTF-8?q?=D0=B8=D0=B7=20=D0=BA=D1=8D=D1=88=D0=B0=20(=D0=BF=D0=BE=D1=81?= =?UTF-8?q?=D0=BB=D0=B5=D0=B4=D0=BD=D0=B8=D0=B9=20=D1=85=D0=B2=D0=BE=D1=81?= =?UTF-8?q?=D1=82=20R2=20#2259)=20(#2347)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/app/api/v1/parcels.py | 11 ++++++++--- backend/app/schemas/parcel.py | 3 +++ backend/tests/api/v1/test_parcel_report.py | 7 ++++++- frontend/src/hooks/useFullReport.ts | 4 +++- frontend/src/lib/api-types.ts | 2 ++ 5 files changed, 22 insertions(+), 5 deletions(-) diff --git a/backend/app/api/v1/parcels.py b/backend/app/api/v1/parcels.py index da20595d..bc3f5f43 100644 --- a/backend/app/api/v1/parcels.py +++ b/backend/app/api/v1/parcels.py @@ -1626,10 +1626,15 @@ def build_parcel_report( analyze_at = _iso_or_none(getattr(analyze_run, "created_at", None)) forecast_at = _iso_or_none(getattr(forecast_run, "created_at", None)) - # Готовый кэш → 200 ready (не enqueue'им повторно). - if _cached_report_result(db, cad_num) is not None: + # Готовый кэш → 200 ready (не enqueue'им повторно). Отдаём и дату генерации PDF из + # метадаты рана — чтобы fast-path кнопка показала дату без отдельного GET /status. + cached = _cached_report_result(db, cad_num) + if cached is not None: return ReportBuildResponse( - status="ready", analyze_run_at=analyze_at, forecast_run_at=forecast_at + status="ready", + analyze_run_at=analyze_at, + forecast_run_at=forecast_at, + report_generated_at=cached.get("generated_at"), ) # Enqueue фоновой сборки. Lazy import таски (как forecast в analyze_parcel) — атрибут diff --git a/backend/app/schemas/parcel.py b/backend/app/schemas/parcel.py index 9f48e48b..d8a3988a 100644 --- a/backend/app/schemas/parcel.py +++ b/backend/app/schemas/parcel.py @@ -270,6 +270,9 @@ class ReportBuildResponse(BaseModel): # Даты базовых ранов (ISO) — контекст, по каким данным собирается/собран отчёт. analyze_run_at: str | None = None forecast_run_at: str | None = None + # Дата генерации готового PDF (ISO) — только при status="ready" (кэш-хит); на + # building-ветке None (файла ещё нет). Даёт fast-path кнопке дату без GET /status. + report_generated_at: str | None = None class ReportStatusResponse(BaseModel): diff --git a/backend/tests/api/v1/test_parcel_report.py b/backend/tests/api/v1/test_parcel_report.py index deb74db4..af2cb810 100644 --- a/backend/tests/api/v1/test_parcel_report.py +++ b/backend/tests/api/v1/test_parcel_report.py @@ -95,6 +95,8 @@ def test_post_report_enqueues_and_returns_202() -> None: body = resp.json() assert body["status"] == "building" assert body["analyze_run_at"] is not None + # Отчёт ещё не собран → даты генерации PDF нет. + assert body["report_generated_at"] is None delay_mock.assert_called_once_with(_CAD) finally: app.dependency_overrides.clear() @@ -127,7 +129,10 @@ def test_post_report_cache_hit_returns_200_ready(tmp_path: Path) -> None: client = TestClient(app) resp = client.post(f"/api/v1/parcels/{_CAD}/report") assert resp.status_code == 200, resp.text - assert resp.json()["status"] == "ready" + body = resp.json() + assert body["status"] == "ready" + # Fast-path: POST-ответ несёт дату генерации PDF из метадаты рана (без GET /status). + assert body["report_generated_at"] == "2026-07-03T09:00:00+00:00" delay_mock.assert_not_called() # кэш есть → не enqueue'им finally: app.dependency_overrides.clear() diff --git a/frontend/src/hooks/useFullReport.ts b/frontend/src/hooks/useFullReport.ts index bfa0cce7..6482c46e 100644 --- a/frontend/src/hooks/useFullReport.ts +++ b/frontend/src/hooks/useFullReport.ts @@ -294,7 +294,9 @@ export function useFullReport(cad: string): UseFullReportResult { } const reportGeneratedAt = - (uiState === "ready" ? statusData?.report_generated_at : null) ?? null; + (uiState === "ready" + ? (statusData?.report_generated_at ?? enqueue.data?.report_generated_at) + : null) ?? null; return { uiState, diff --git a/frontend/src/lib/api-types.ts b/frontend/src/lib/api-types.ts index e1d545ab..780690bb 100644 --- a/frontend/src/lib/api-types.ts +++ b/frontend/src/lib/api-types.ts @@ -5255,6 +5255,8 @@ export interface components { analyze_run_at?: string | null; /** Forecast Run At */ forecast_run_at?: string | null; + /** Report Generated At */ + report_generated_at?: string | null; }; /** * ReportStatusResponse