From 2a73ee0cfe519bb4f09fbb865512db1ea8877527 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=94=D0=BC=D0=B8=D1=82=D1=80=D0=B8=D0=B9?= Date: Wed, 20 May 2026 09:31:30 +0300 Subject: [PATCH] =?UTF-8?q?feat(finance):=20billing-audit=20skill=20?= =?UTF-8?q?=E2=80=94=20money=20invariants=20C6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 4.7 (1M context) --- .claude/skills/billing-audit/SKILL.md | 43 +++++++++++++++++ .claude/skills/billing-audit/evals/evals.json | 22 +++++++++ .../billing-audit/references/invariants.md | 46 +++++++++++++++++++ cspell-words.txt | 6 +++ 4 files changed, 117 insertions(+) create mode 100644 .claude/skills/billing-audit/SKILL.md create mode 100644 .claude/skills/billing-audit/evals/evals.json create mode 100644 .claude/skills/billing-audit/references/invariants.md diff --git a/.claude/skills/billing-audit/SKILL.md b/.claude/skills/billing-audit/SKILL.md new file mode 100644 index 00000000..5ffffc8f --- /dev/null +++ b/.claude/skills/billing-audit/SKILL.md @@ -0,0 +1,43 @@ +--- +name: billing-audit +description: Аудит денежной корректности биллинг-кода Лидерры — money-инварианты при правке/ревью списаний, тарифов и баланса. Используй при «проверь списание», «аудит биллинга», «не теряются ли копейки», «идемпотентно ли списание», «корректна ли тарифная ступень», «что значит дрейф CsvReconcile», «провенанс charge_source». НЕ для моделирования процесса (process-modeling), поиска узких мест (process-analysis), security-аудита (D3), РСБУ/налогов (ru-tax-accounting), метрик выручки (product-management). +--- + +# Billing Audit — аудит денежной корректности биллинга Лидерры + +Проектный скил раздела C6 карты «Финансы — биллинг и тарификация». Проверяет +**денежные инварианты** биллинг-подсистемы при правке или ревью кода. Объект — +корректность *начисления* (не процесс, не безопасность, не учёт/налоги). + +## Когда использовать + +- Правка/ревью кода в `app/app/Services/Billing/**`, `app/app/Jobs/Supplier/CsvReconcileJob.php`, + моделей `PricingTier`/`LeadCharge`, контроллеров биллинга. +- Вопрос «безопасно ли это денежно?» по списанию, тарифу, балансу, сверке. + +## Процедура аудита (5 инвариантов) + +Полный чек-лист с проверками и ссылками на файлы — `references/invariants.md`. + +1. **Сохранение суммы** — все денежные операции через `bcmath` (bcadd/bcsub/bcmul/bcdiv, + scale фиксирован), никаких float; prepaid→₽ конвертация без потери копеек. +2. **Идемпотентность списания** — один лид = одно списание; повтор/ретрай джоба + не дублирует начисление (проверить уникальный ключ / advisory-lock / upsert). +3. **Корректность тарифной ступени** — `PricingTierResolver` выбирает верную из 7 + ступеней по объёму; границы ступеней (включительно/исключительно) однозначны. +4. **Дрейф сверки** — `CsvReconcileJob` порог >5%: что сравнивается, что значит дрейф, + куда смотреть (рассинхрон поставки vs ошибка тарифа). +5. **Провенанс charge_source** — каждое списание имеет прослеживаемый источник + (`charge_source`); ручные/авто/CSV-восстановленные различимы. + +## Границы + +- ≠ `process-modeling` #52 / `process-analysis` #53 — те про *поток/процесс*; billing-audit про *деньги в коде*. +- ≠ D3 audit-security (#39/#40) — те про *безопасность*; billing-audit про *денежную корректность*. +- ≠ `ru-tax-accounting` #63 — тот про *учёт/налоги* (выход биллинга → налоговая база); billing-audit про *начисление*. +- ≠ `product-management:metrics-review` #42 — тот про *метрики выручки*; billing-audit про *корректность*. + +## Связано + +- Reuse: Boost #10 (модели), Pest #18 (тесты инвариантов), Larastan #12 (bcmath/без float), Sentry #34 / Redis #35 (runtime/очередь). +- ADR-012 (граница finance-tooling C6/C7). diff --git a/.claude/skills/billing-audit/evals/evals.json b/.claude/skills/billing-audit/evals/evals.json new file mode 100644 index 00000000..7f84383f --- /dev/null +++ b/.claude/skills/billing-audit/evals/evals.json @@ -0,0 +1,22 @@ +{ + "skill": "billing-audit", + "positive": [ + "проверь корректность списания за лид", + "аудит денежной логики биллинга", + "не теряются ли копейки в prepaid→рублёвом балансе", + "идемпотентно ли списание при ретрае", + "правильно ли резолвится тарифная ступень", + "что значит дрейф >5% в CsvReconcile", + "проверь провенанс charge_source", + "ревью PricingTierResolver на ошибки округления", + "ledger двойной баланс — где может утечь сумма", + "audit charge invariants before merge" + ], + "near_miss": [ + {"prompt": "смоделируй BPMN процесса списания", "expect": "process-modeling #52"}, + {"prompt": "где узкое место в воронке оплат", "expect": "process-analysis #53"}, + {"prompt": "security-аудит платёжного эндпоинта", "expect": "D3 audit-security / Semgrep"}, + {"prompt": "посчитай РСБУ-проводки по выручке", "expect": "ru-tax-accounting #63"}, + {"prompt": "метрика MRR за месяц", "expect": "product-management metrics-review #42"} + ] +} diff --git a/.claude/skills/billing-audit/references/invariants.md b/.claude/skills/billing-audit/references/invariants.md new file mode 100644 index 00000000..5f729ab0 --- /dev/null +++ b/.claude/skills/billing-audit/references/invariants.md @@ -0,0 +1,46 @@ +# Денежные инварианты биллинга Лидерры — чек-лист аудита + +Объект-файлы (на момент 20.05.2026): + +- `app/app/Services/Billing/PricingTierResolver.php` — резолюция 7 ступеней (pure). +- `app/app/Services/Billing/LedgerService.php` — двойной баланс prepaid→₽ (bcmath). +- `app/app/Services/Billing/BillingTopupService.php` — пополнение. +- `app/app/Services/Billing/ChargeResult.php` — DTO результата списания. +- `app/app/Models/PricingTier.php`, `app/app/Models/LeadCharge.php`. +- `app/app/Repositories/PricingTierRepository.php`. +- `app/app/Jobs/Supplier/CsvReconcileJob.php` — hourly сверка, алерт дрейфа >5%. +- `app/app/Http/Controllers/Api/{AdminPricingTiersController,AdminBillingController,BillingController,TenantChargesController}.php`. + +## I1. Сохранение суммы (bcmath, без float) + +- [ ] Все арифметические операции с деньгами — `bcadd`/`bcsub`/`bcmul`/`bcdiv`/`bccomp` с явным `scale`. +- [ ] Нет `+`/`-`/`*`/`/` над денежными значениями (Larastan/grep на float-арифметику в Billing). +- [ ] prepaid→₽: конвертация округляет детерминированно (TRUNC/округление вниз в пользу tenant — свериться с кодом), сумма prepaid + ₽ не «исчезает». +- [ ] Денежные колонки — целочисленные копейки или DECIMAL, не float/double. + +## I2. Идемпотентность списания + +- [ ] Один лид → одно списание: уникальность по (lead_id) или advisory-lock в `LedgerService`. +- [ ] Ретрай `ImportLeadsJob`/`CsvReconcileJob` не создаёт дубль `lead_charges`. +- [ ] Транзакция + `lockForUpdate` на балансе при мутации (TOCTOU — см. Sprint 3 lockForUpdate). + +## I3. Корректность тарифной ступени + +- [ ] `PricingTierResolver` выбирает ступень по объёму `delivered_in_month` верно на границах. +- [ ] Границы ступеней непрерывны (нет дыр/перекрытий между 7 ступенями). +- [ ] Pest покрывает граничные значения (ступень N → N+1). + +## I4. Дрейф сверки CsvReconcile + +- [ ] Порог >5% — что сравнивается (поставка поставщика vs начислено) → `supplier_csv_reconcile_log`. +- [ ] Дрейф = рассинхрон поставки (норм) ИЛИ ошибка тарифа (баг) — различить по `charge_source`. + +## I5. Провенанс charge_source + +- [ ] Каждое `lead_charges.charge_source` заполнено и прослеживаемо. +- [ ] Авто/ручное/CSV-восстановленное (`recovered_from_csv_at`) различимы. + +## Reuse-инструменты + +Boost #10 (Eloquent-introspection), Pest #18 + pest-parallel-debugger (тесты + race), +Larastan #12 (статанализ bcmath), Sentry MCP #34 (runtime списаний), Redis MCP #35 (очередь сверки), context7 #60 (доки bcmath). diff --git a/cspell-words.txt b/cspell-words.txt index 475138c3..7e26a83e 100644 --- a/cspell-words.txt +++ b/cspell-words.txt @@ -1509,3 +1509,9 @@ GAAP ретрай субледжер хардкодит + +# finance-tooling C6+C7 — billing-audit skill (2026-05-20) +TOCTOU +bcadd +bcsub +bcmul