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