-- Migration 189: account_estimate_usage.used >= 0 — защита от бонус-хака (negative used) -- -- WHY: -- Прежний SQL-runbook хак раздачи бонусных попыток (`UPDATE account_estimate_usage -- SET used = used - N`) уводил used в отрицательные значения. Migration 185 сбросила -- это ТОЛЬКО для user2 (used=-35 -> 0, WHERE username = 'user2'). На проде остаётся -- минимум ещё один затронутый аккаунт тем же классом порчи (praktika, период 2026-06: -- used=-3 при 42 фактических успешных оценках — расхождение объясняется именно этим -- хаком, не кодовым багом). -- -- Аудит app.services.account_quota подтверждает: декремента `used` в текущем коде -- НЕТ. increment() делает только `used + 1` под предикатом `WHERE used < :lim` -- (#747, atomic conditional increment) — этот путь не может уйти в минус. Значит -- источник отрицательных значений исключительно внешний (ручной UPDATE через -- runbook), а не баг в приложении. -- -- WHAT: -- 1. Сброс ВСЕХ оставшихся negative used -> 0 (не только user2, как в 185) — -- закрывает praktika и любой другой пропущенный аккаунт. -- 2. CHECK (used >= 0) — защита на уровне схемы: любой будущий ручной UPDATE/хак, -- уводящий used < 0, теперь падает на уровне БД вместо тихой порчи /quota -- (GET /quota мог отдать remaining > limit — «Осталось 50 из 15», см. 185). -- -- IDEMPOTENCY: -- - UPDATE ... WHERE used < 0 — no-op при повторном прогоне (после первого раза -- условие больше не матчит). -- - ADD CONSTRAINT через DO-блок с проверкой pg_constraint — Postgres не -- поддерживает `ADD CONSTRAINT IF NOT EXISTS` напрямую для CHECK, поэтому -- оборачиваем в идемпотентную проверку по имени constraint. -- -- Dependencies: 076_account_estimate_quota.sql (account_estimate_usage), -- 185_account_quota_overrides.sql (первый частичный сброс, только user2). BEGIN; UPDATE account_estimate_usage SET used = 0, updated_at = now() WHERE used < 0; DO $$ BEGIN IF NOT EXISTS ( SELECT 1 FROM pg_constraint WHERE conname = 'account_estimate_usage_used_nonnegative' ) THEN ALTER TABLE account_estimate_usage ADD CONSTRAINT account_estimate_usage_used_nonnegative CHECK (used >= 0); END IF; END $$; COMMIT;