feat(mera/b2c): правовая рамка — согласие до сохранения, удаление по сроку и по запросу — этап 4 из 8 #2547

Open
lekss361 wants to merge 1 commit from feat/mera-b2c-privacy into main
Owner

Четвёртый этап плана B2C. Наружу ничего не открывает, мержится независимо от DNS.

Три дефекта, каждый блокировал легальный публичный запуск

Адрес физлица сохранялся до любого согласия. Согласие фиксировалось только на форме заявки — то есть после записи адреса в базу. Для пилота с договором это терпимо, для человека с улицы нет. Теперь проверка стоит первой строкой расчёта, до геокодирования и до обоих мест записи адреса.

Срок жизни оценки не приводил к удалению. Поле срока применялось только как фильтр при чтении, физического удаления не было ни в одной из 23 фоновых задач — данные жили вечно вопреки декларации. У заявок срока не было вовсе.

Пути «удалите мои данные» не существовало.

Решения, которые стоит отметить

Хранение согласия — колонками на самой оценке, 1:1 с уже работающим прецедентом для заявок: IP клиента, версия политики, дословный снимок текста. Отдельную таблицу событий не заводили сознательно — согласие даётся ровно на создание этой строки, и когда строка удаляется по сроку, исчезновение доказательства вместе с данными логично: персональных данных больше нет, свидетельствовать не о чем.

Проверка не выводится из пустого имени пользователя. Первая версия так и делала — и сломала 92 несвязанных теста оценщика, которые вызывают расчёт без имени, проверяя ценовую логику. Заменено на явный флаг, который выставляет единственный боевой вызывающий. B2B-поток не тронут: поле согласия опционально, иначе сломались бы пилоты, чей фронт его вообще не шлёт.

Задача удаления засеяна выключенной. Это первая автоматическая задача в trade-in, которая удаляет персональные данные — первый прогон должен пройти под наблюдением, а не по расписанию. Тот же приём уже применялся в проекте. Включается одной командой после проверки.

Честно зафиксированные ограничения

Аноним без ссылки на оценку, без телефона и без обращения в поддержку — неидентифицируем. Удалить его данные без дополнительной идентификации невозможно, схема этого не позволяет. Записано в докстринге, а не умолчано.

Удаление чистит только копию в базе. Зеркало переписки в Telegram-топике не удаляется ничем в кодовой базе — нужен отдельный ручной шаг через API бота. Зафиксировано в коде.

События аудита этим механизмом не чистятся — там в полезной нагрузке есть адрес. Является ли журнал аудита законным основанием пережить запрос на удаление — вопрос к юристу, не инженерное решение.

Что за юристом

Конкретные сроки хранения — инженерное предложение с обоснованием, не юридический вывод. Для сконвертированных заявок нужен другой, договорной срок, и механизма пометки «сконвертирован» в схеме сейчас нет.

Test plan

  • uv run pytest -q2775 passed, 9 skipped
  • ruff check чисто, проверка на ловушку :x::type чисто
  • Тесты: согласие фиксируется до создания оценки, задача удаления реально удаляет и идемпотентна, расхождение текста согласия ловится, B2B-поток не сломан
  • Тест синхронности текста согласия фронт↔бэк — теперь настоящий, а не комментарий
  • Первый прогон задачи удаления — под наблюдением, вручную
  • Миграции против живой базы не применялись
Четвёртый этап плана B2C. Наружу ничего не открывает, мержится независимо от DNS. ## Три дефекта, каждый блокировал легальный публичный запуск **Адрес физлица сохранялся до любого согласия.** Согласие фиксировалось только на форме заявки — то есть **после** записи адреса в базу. Для пилота с договором это терпимо, для человека с улицы нет. Теперь проверка стоит первой строкой расчёта, до геокодирования и до обоих мест записи адреса. **Срок жизни оценки не приводил к удалению.** Поле срока применялось только как фильтр при чтении, физического удаления не было ни в одной из 23 фоновых задач — данные жили вечно вопреки декларации. У заявок срока не было вовсе. **Пути «удалите мои данные» не существовало.** ## Решения, которые стоит отметить **Хранение согласия — колонками на самой оценке**, 1:1 с уже работающим прецедентом для заявок: IP клиента, версия политики, дословный снимок текста. Отдельную таблицу событий не заводили сознательно — согласие даётся ровно на создание этой строки, и когда строка удаляется по сроку, исчезновение доказательства вместе с данными логично: персональных данных больше нет, свидетельствовать не о чем. **Проверка не выводится из пустого имени пользователя.** Первая версия так и делала — и сломала **92 несвязанных теста** оценщика, которые вызывают расчёт без имени, проверяя ценовую логику. Заменено на явный флаг, который выставляет единственный боевой вызывающий. B2B-поток не тронут: поле согласия опционально, иначе сломались бы пилоты, чей фронт его вообще не шлёт. **Задача удаления засеяна выключенной.** Это первая автоматическая задача в trade-in, которая удаляет персональные данные — первый прогон должен пройти под наблюдением, а не по расписанию. Тот же приём уже применялся в проекте. Включается одной командой после проверки. ## Честно зафиксированные ограничения **Аноним без ссылки на оценку, без телефона и без обращения в поддержку — неидентифицируем.** Удалить его данные без дополнительной идентификации невозможно, схема этого не позволяет. Записано в докстринге, а не умолчано. **Удаление чистит только копию в базе.** Зеркало переписки в Telegram-топике не удаляется ничем в кодовой базе — нужен отдельный ручной шаг через API бота. Зафиксировано в коде. **События аудита этим механизмом не чистятся** — там в полезной нагрузке есть адрес. Является ли журнал аудита законным основанием пережить запрос на удаление — вопрос к юристу, не инженерное решение. ## Что за юристом Конкретные сроки хранения — инженерное предложение с обоснованием, не юридический вывод. Для сконвертированных заявок нужен другой, договорной срок, и механизма пометки «сконвертирован» в схеме сейчас нет. ## Test plan - [x] `uv run pytest -q` — **2775 passed**, 9 skipped - [x] `ruff check` чисто, проверка на ловушку `:x::type` чисто - [x] Тесты: согласие фиксируется до создания оценки, задача удаления реально удаляет и идемпотентна, расхождение текста согласия ловится, B2B-поток не сломан - [x] Тест синхронности текста согласия фронт↔бэк — теперь настоящий, а не комментарий - [ ] Первый прогон задачи удаления — под наблюдением, вручную - [ ] Миграции против живой базы не применялись
lekss361 added 1 commit 2026-07-28 12:25:34 +00:00
feat(mera/b2c): правовая рамка — согласие до сохранения, удаление по сроку и по запросу (этап 4 из 8)
All checks were successful
CI Trade-In / changes (pull_request) Successful in 11s
CI / changes (pull_request) Successful in 12s
CI Trade-In / frontend-checks (pull_request) Has been skipped
CI / backend-tests (pull_request) Has been skipped
CI / openapi-codegen-check (pull_request) Has been skipped
CI / frontend-tests (pull_request) Has been skipped
CI Trade-In / backend-tests (pull_request) Successful in 1m11s
5626d9e720
Три дефекта, каждый блокировал легальный публичный запуск.

1. Адрес физлица сохранялся в базу ДО любого согласия: согласие фиксировалось
   только на форме заявки, то есть ПОСЛЕ записи адреса. Для пилота с договором
   терпимо, для человека с улицы — нет. Проверка согласия поставлена первой
   строкой расчёта, до геокодирования и до обоих мест записи адреса.

   Хранение — колонками на самой оценке, 1:1 с уже работающим прецедентом для
   заявок (миграция 182): IP клиента, версия политики, дословный снимок текста.
   Отдельная таблица событий не заводилась: согласие даётся ровно на создание
   этой строки, и когда строка удаляется по сроку, исчезновение доказательства
   вместе с данными логично.

   Enforcement НЕ выводится из пустого created_by — первая версия так и делала
   и сломала 92 несвязанных теста оценщика, которые зовут расчёт без имени
   пользователя, проверяя ценовую логику. Вместо этого явный флаг, который
   выставляет единственный боевой вызывающий. B2B-поток не тронут: поле
   согласия опционально, иначе сломались бы пилоты, чей фронт его не шлёт.

2. Срок жизни оценки применялся только как фильтр при чтении — физического
   удаления не было ни в одной фоновой задаче, данные жили вечно вопреки
   декларированному сроку. Заведена задача удаления пачками с ограничением на
   прогон и коммитом после каждой пачки, идемпотентная. В расписании она
   ВЫКЛЮЧЕНА: это первая автоматическая задача, удаляющая персональные данные,
   и первый прогон должен быть под наблюдением.

3. Пути «удалите мои данные» не было. Добавлен сервис удаления и админская
   ручка. Ключи: имя пользователя, идентификатор оценки, телефон, чат в
   телеграме.

   Честно зафиксировано в коде: аноним без ссылки на оценку, без оставленного
   телефона и без обращения в поддержку неидентифицируем — удалить его данные
   без дополнительной идентификации нельзя. Отдельно: удаление чистит только
   копию в базе, зеркало переписки в телеграм-топике не удаляется ничем в
   кодовой базе, нужен ручной шаг.

4. Соответствие текста согласия на фронте и снимка на бэке держалось на
   комментарии. Теперь есть тест, который ловит расхождение.

Сроки хранения вынесены в настройки. Значение для заявок предложено инженерно
(типичный отраслевой диапазон), юридически обоснованный срок — за юристом, и
это записано в коде.

Тесты: 2775 passed.
All checks were successful
CI Trade-In / changes (pull_request) Successful in 11s
CI / changes (pull_request) Successful in 12s
CI Trade-In / frontend-checks (pull_request) Has been skipped
CI / backend-tests (pull_request) Has been skipped
CI / openapi-codegen-check (pull_request) Has been skipped
CI / frontend-tests (pull_request) Has been skipped
CI Trade-In / backend-tests (pull_request) Successful in 1m11s
This pull request can be merged automatically.
You are not authorized to merge this pull request.
View command line instructions

Checkout

From your project repository, check out a new branch and test the changes.
git fetch -u origin feat/mera-b2c-privacy:feat/mera-b2c-privacy
git checkout feat/mera-b2c-privacy

Merge

Merge the changes and update on Forgejo.

Warning: The "Autodetect manual merge" setting is not enabled for this repository, you will have to mark this pull request as manually merged afterwards.

git checkout main
git merge --no-ff feat/mera-b2c-privacy
git checkout feat/mera-b2c-privacy
git rebase main
git checkout main
git merge --ff-only feat/mera-b2c-privacy
git checkout feat/mera-b2c-privacy
git rebase main
git checkout main
git merge --no-ff feat/mera-b2c-privacy
git checkout main
git merge --squash feat/mera-b2c-privacy
git checkout main
git merge --ff-only feat/mera-b2c-privacy
git checkout main
git merge feat/mera-b2c-privacy
git push origin main
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference: lekss361/gendesign#2547
No description provided.