Дмитрий
65c5178c29
fix(billing): runwayDays clamps negative balance to 0 + type-filter test (Task 2 review)
...
Code-quality review fixups: runway_days клампится в 0 при отрицательном
балансе (overdrawn-тенант не должен показывать «−N дней»); (int)-каст в
wallet() для консистентности; усилены assertJsonPath на type-фильтре.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com >
2026-05-16 07:24:34 +03:00
Дмитрий
040d25423d
feat(billing): wallet/transactions/invoices read API (E3)
...
GET /api/billing/wallet (баланс + тариф + runway), /transactions
(пагинированный balance_transactions с фильтром type), /invoices
(saas_invoices, real-but-empty до Б-1). TariffPlan модель +
Tenant::tariff() relation + BalanceTransactionFactory.
Sprint 2 Plan C, audit E3 (backend).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com >
2026-05-16 07:08:09 +03:00
Дмитрий
7bee35768d
fix(billing): topup save() rationale comment + cross-tenant test (Task 1 review)
...
Code-quality review fixups: документирующий комментарий про безопасность
Eloquent save() для bcmath-строки (расхождение с LedgerService raw-update);
cross-tenant isolation тест на /api/billing/topup; balance_rub_after в
assertDatabaseHas.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com >
2026-05-16 07:00:19 +03:00
Дмитрий
44dc1025ec
feat(billing): topup ledger service + POST /api/billing/topup stub (E1)
...
BillingTopupService кредитует tenants.balance_rub (bcmath) и пишет
append-only строку balance_transactions(type='topup'). BillingController
+ route POST /api/billing/topup под [auth:sanctum, tenant]. MVP-stub:
без платёжного шлюза (ЮKassa — post-Б-1).
Sprint 2 Plan C, audit E1 (backend).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com >
2026-05-16 06:46:52 +03:00
Дмитрий
174dbae808
feat(billing): Plan 4 Task 11 — TenantChargesController + ChargesTab + CSV export
...
Backend TenantChargesController:
- GET /api/billing/charges — paginated list, filters period (current_month / last_month / 90d) + charge_source.
- POST /api/billing/charges/export — StreamedResponse CSV (BOM + UTF-8) с chunkById(500).
- auth:sanctum + tenant middleware — RLS изолирует tenant_id.
- 6 Pest integration tests (RLS isolation + filters + pagination + CSV export).
Frontend ChargesTab.vue:
- v-data-table-server с paginated load + period/charge_source filters.
- CSV-download через blob → createObjectURL.
- Forest-palette + JetBrains Mono tnum.
BillingView.vue — добавлен tab «Списания» с импортом ChargesTab.
ChargesTab.story.vue + 4 Vitest tests.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com >
2026-05-11 11:51:13 +03:00
Дмитрий
d2030f9121
feat(billing): Plan 4 Task 3 — LedgerService::chargeForDelivery (dual-balance + lead_charges/supplier_lead_costs INSERT)
2026-05-11 09:42:29 +03:00
Дмитрий
1e0c0ab90a
fix(billing): Plan 4 Task 2 code-review fixes (2 Important + 1 Minor)
...
- PricingTierResolver::resolveForCount — InvalidArgumentException на
$leadOrdinal < 1 (closes I-1: defensive contract validation).
- PricingTierRepository::activeAt — explicit @var Collection<int,
PricingTier> annotation для type narrowing (closes I-2; firstOrFail
отвергнут — Stan ругается на Eloquent\Model return-type).
- PricingTierResolverTest — +1 unit test (8/8 PASS): throws на 0/-1.
- PricingTierRepositoryTest — +1 integration test (5/5 PASS): excludes
inactive tiers (closes M-2 coverage gap).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com >
2026-05-11 09:17:13 +03:00
Дмитрий
e07d025efd
feat(billing): Plan 4 Task 2 — PricingTierResolver + Repository (pure resolver + DB-обёртка)
...
- PricingTierResolver: pure-function, ищет tier для N-го лида (1-based).
100→tier1, 101→tier2, 6000→tier6 (cumul.sum 1-6), 6001+→tier7 (NULL=unlimited).
RuntimeException на пустой коллекции.
- PricingTierRepository::activeAt(Carbon): DB-обёртка, MAX(effective_from) <= $at
per tier_no (учёт «новая сетка перекрывает старую»), is_active=true.
- 7 unit-тестов (in-memory, без БД) + 4 integration-теста (DatabaseTransactions
с baseline-cleanup для seed-7-tiers из PricingTierSeeder Task 1).
- phpstan-baseline.neon: +3 entry (Pest TestCall::\$resolver/\$tiers/\$repo) —
следуем project-convention (см. SupplierResolverTest идентичные baseline-entries).
Spec: docs/superpowers/specs/2026-05-11-plan4-billing-csv-admin-design.md §3.1
Plan: docs/superpowers/plans/2026-05-11-plan4-billing-csv-admin.md Task 2
Tests: 633 passed / 3 skipped / 0 failed (+11 new); pint clean; stan 0 errors above baseline.
2026-05-11 09:05:22 +03:00