From e6591a450a829f94924bf85a3fa6c541ff10a9c8 Mon Sep 17 00:00:00 2001 From: bot-backend Date: Fri, 7 Aug 2026 15:31:01 +0300 Subject: [PATCH] =?UTF-8?q?fix(tradein/payments):=20=D0=BC=D0=B8=D0=B3?= =?UTF-8?q?=D1=80=D0=B0=D1=86=D0=B8=D1=8F=20234=20=E2=86=92=20240=20?= =?UTF-8?q?=E2=80=94=20=D0=BD=D0=BE=D0=BC=D0=B5=D1=80=20=D1=81=D0=BD=D0=BE?= =?UTF-8?q?=D0=B2=D0=B0=20=D0=B7=D0=B0=D0=BD=D1=8F=D1=82=20=D0=BD=D0=B0=20?= =?UTF-8?q?main?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit main уехал вперёд за сутки: 234 занял 234_scrape_runs_ban_kind_unknown.sql (0de22f4b), максимум на main сейчас 239 (235-237 — дыры). max+1=240 безопаснее дыр; ни один открытый PR номер 235-240 не занимает (сверено по forgejo/main и всем открытым веткам). Переименован файл + обновлены все 7 упоминаний "migration 234" (_manifest_applied.txt, config.py, schemas/trade_in.py, purge_expired_trade_in_data.py, test_estimate_idor.py, content.ts, types/trade-in.ts) — правки текстовые, ни один тест не читает миграцию по имени файла. --- tradein-mvp/backend/app/core/config.py | 4 ++-- tradein-mvp/backend/app/schemas/trade_in.py | 2 +- .../backend/app/tasks/purge_expired_trade_in_data.py | 2 +- ...l.sql => 240_trade_in_estimates_retain_until.sql} | 12 ++++++++---- tradein-mvp/backend/data/sql/_manifest_applied.txt | 2 +- tradein-mvp/backend/tests/test_estimate_idor.py | 2 +- tradein-mvp/frontend/src/app/mera-public/content.ts | 2 +- tradein-mvp/frontend/src/types/trade-in.ts | 2 +- 8 files changed, 16 insertions(+), 12 deletions(-) rename tradein-mvp/backend/data/sql/{234_trade_in_estimates_retain_until.sql => 240_trade_in_estimates_retain_until.sql} (87%) diff --git a/tradein-mvp/backend/app/core/config.py b/tradein-mvp/backend/app/core/config.py index eb70c787..b0f5b791 100644 --- a/tradein-mvp/backend/app/core/config.py +++ b/tradein-mvp/backend/app/core/config.py @@ -836,11 +836,11 @@ class Settings(BaseSettings): # срок — решение DPO/юриста, не инженера). ENV: TRADE_IN_LEAD_RETENTION_DAYS. trade_in_lead_retention_days: int = 180 - # ── Платный отчёт живёт год (retain_until, migration 234, PR #2754) ───── + # ── Платный отчёт живёт год (retain_until, migration 240, PR #2754) ───── # trade_in_estimates.retain_until TTL (дни ОТ ОПЛАТЫ) — срок жизни ССЫЛКИ/ # СТРОКИ для оплаченной оценки, независимый от expires_at (актуальность # расчёта, 24ч, глобальный для ВСЕХ строк). НЕ трогает expires_at — см. - # migration 234 докстринг. Отдельная колонка, а не подъём expires_at: + # migration 240 докстринг. Отдельная колонка, а не подъём expires_at: # expires_at печатается в PDF/UI как «актуальность расчёта» и одинаков # для всех строк, поднять его до года = соврать в документе клиента про # свежесть цифры + нарушить минимизацию ПДн для неоплаченных B2C-адресов. diff --git a/tradein-mvp/backend/app/schemas/trade_in.py b/tradein-mvp/backend/app/schemas/trade_in.py index c7620ece..4f3d4996 100644 --- a/tradein-mvp/backend/app/schemas/trade_in.py +++ b/tradein-mvp/backend/app/schemas/trade_in.py @@ -197,7 +197,7 @@ class AggregatedEstimate(BaseModel): actual_deals: list[AnalogLot] # реальные продажи last 12 mo expires_at: datetime # PR-D1: срок жизни ССЫЛКИ/СТРОКИ (оплаченный доступ), НЕ актуальности - # расчёта — тот остаётся expires_at (не путать, см. migration 234). + # расчёта — тот остаётся expires_at (не путать, см. migration 240). # NULL = неоплачено (весь текущий трафик, B2B pilots включительно). retain_until: datetime | None = None # ── Дополнительные метаданные ── diff --git a/tradein-mvp/backend/app/tasks/purge_expired_trade_in_data.py b/tradein-mvp/backend/app/tasks/purge_expired_trade_in_data.py index c48e8dba..4dcda362 100644 --- a/tradein-mvp/backend/app/tasks/purge_expired_trade_in_data.py +++ b/tradein-mvp/backend/app/tasks/purge_expired_trade_in_data.py @@ -99,7 +99,7 @@ _DEFAULT_MAX_BATCHES = 20 # означало бы одновременно нарушить минимизацию ПДн по 152-ФЗ и соврать в # документе клиента про срок актуальности цифры: # 1. `retain_until IS NULL` — именно IS NULL, НЕ `< NOW()`. Оплаченная -# строка (retain_until IS NOT NULL, migration 234) не удаляется джобой +# строка (retain_until IS NOT NULL, migration 240) не удаляется джобой # В ПРИНЦИПЕ, пока не поднято ослабление отдельным PR не раньше чем # через год после первой продажи. `retain_until` ставится сервисным # кодом платёжного контура (ещё не существует в этом PR) на now() + diff --git a/tradein-mvp/backend/data/sql/234_trade_in_estimates_retain_until.sql b/tradein-mvp/backend/data/sql/240_trade_in_estimates_retain_until.sql similarity index 87% rename from tradein-mvp/backend/data/sql/234_trade_in_estimates_retain_until.sql rename to tradein-mvp/backend/data/sql/240_trade_in_estimates_retain_until.sql index 912eb915..93693455 100644 --- a/tradein-mvp/backend/data/sql/234_trade_in_estimates_retain_until.sql +++ b/tradein-mvp/backend/data/sql/240_trade_in_estimates_retain_until.sql @@ -1,10 +1,14 @@ --- 234_trade_in_estimates_retain_until.sql +-- 240_trade_in_estimates_retain_until.sql -- Платёжный контур МЕРЫ, ретеншен (PR #2754): «оплаченное живёт год, purge -- его не трогает». Владелец продаёт отчёт физлицу за 150 ₽ — отчёт должен -- жить год на нашей стороне, а не 24ч (см. WHY ниже). --- Номер сверен и по `forgejo/main`, и по всем открытым PR-веткам на момент --- написания (последняя занятая — 233_payments.sql) — см. урок в шапке того --- же файла про то, как коллизия 228/229/231/232 обнаруживается поздно. +-- Номер сверен по `forgejo/main` и всем открытым PR-веткам ДВАЖДЫ: сначала +-- как 234 (последняя занятая на момент ветвления была 233_payments.sql), но +-- main уехал вперёд и 234 занял `234_scrape_runs_ban_kind_unknown.sql` +-- (коммит 0de22f4b) — переименовано в 240 (main max на момент повторной +-- сверки — 239, с дырами 235-237; max+1 безопаснее дыр). Урок пятый за +-- сутки: сверять номер нужно не только перед первым коммитом, а прямо перед +-- пушем/мержем — main не стоит на месте. -- -- ── WHY ────────────────────────────────────────────────────────────────────── -- purge_expired_trade_in_data (migration 231, seeded enabled=false) удаляет diff --git a/tradein-mvp/backend/data/sql/_manifest_applied.txt b/tradein-mvp/backend/data/sql/_manifest_applied.txt index 27b06836..72981f2c 100644 --- a/tradein-mvp/backend/data/sql/_manifest_applied.txt +++ b/tradein-mvp/backend/data/sql/_manifest_applied.txt @@ -231,4 +231,4 @@ # поддержки, #2532/#2533) откладывались до подтверждения, что они осели на # проде в финальном виде. Они в _schema_migrations — условие выполнено. 233_payments.sql -234_trade_in_estimates_retain_until.sql +240_trade_in_estimates_retain_until.sql diff --git a/tradein-mvp/backend/tests/test_estimate_idor.py b/tradein-mvp/backend/tests/test_estimate_idor.py index 7cb0e735..a9ed8d31 100644 --- a/tradein-mvp/backend/tests/test_estimate_idor.py +++ b/tradein-mvp/backend/tests/test_estimate_idor.py @@ -54,7 +54,7 @@ def trade_in_app() -> FastAPI: def _make_estimate_row(created_by: str | None, retain_until: object = None) -> SimpleNamespace: """A trade_in_estimates row with the full column set the endpoints read. - retain_until defaults to None (PR-D1, migration 234) -- unpaid, matches every + retain_until defaults to None (PR-D1, migration 240) -- unpaid, matches every row that existed before that migration; explicit param lets retention-gate tests (see test_estimate_retention_gate.py) construct a paid row. """ diff --git a/tradein-mvp/frontend/src/app/mera-public/content.ts b/tradein-mvp/frontend/src/app/mera-public/content.ts index 66772877..5e5c2937 100644 --- a/tradein-mvp/frontend/src/app/mera-public/content.ts +++ b/tradein-mvp/frontend/src/app/mera-public/content.ts @@ -94,7 +94,7 @@ export const PRIVACY_PATH = "/mera-public/privacy"; /** * Сколько месяцев на нашей стороне хранится ссылка/строка оплаченного отчёта - * после оплаты (`trade_in_estimates.retain_until`, migration 234) — НЕ срок + * после оплаты (`trade_in_estimates.retain_until`, migration 240) — НЕ срок * действия самого расчёта (тот отдельный, `expires_at`, часы). * * ЕДИНСТВЕННОЕ место, где это число хардкодится на фронте — любой другой diff --git a/tradein-mvp/frontend/src/types/trade-in.ts b/tradein-mvp/frontend/src/types/trade-in.ts index ce3282bd..94f1dea2 100644 --- a/tradein-mvp/frontend/src/types/trade-in.ts +++ b/tradein-mvp/frontend/src/types/trade-in.ts @@ -172,7 +172,7 @@ export interface AggregatedEstimate { analogs: AnalogLot[]; // top 5-10 actual_deals: AnalogLot[]; // last 12 mo expires_at: string; // ISO datetime - // PR-D1: срок жизни ссылки/строки (оплаченный доступ, backend migration 234), + // PR-D1: срок жизни ссылки/строки (оплаченный доступ, backend migration 240), // НЕ путать с expires_at (актуальность расчёта). null = неоплачено (текущий // трафик целиком). retain_until?: string | null; // ISO datetime