feat(биллинг): оплата по счёту (Этап 1) — счёт, акт, отметка оплаты
Клиент сам выставляет PDF-счёт (TopupDialog вкладка «По счёту»), счета и акты — в отдельной вкладке «Счета». Админ (/admin/invoices) отмечает оплату одной кнопкой → атомарно зачисляет баланс (BillingTopupService), формирует Акт (без НДС, saas_upd_documents ДОП) и шлёт клиенту письмо «Счёт оплачен» с вложением PDF-акта. PDF открываются inline в браузере (ASCII-имя). - Сервисы InvoiceNumberGenerator/InvoiceService/ActService/InvoicePaymentService/PdfRenderer - Контроллеры InvoiceController (клиент) + AdminInvoiceController (список+mark-paid) - Модели SaasInvoice/SaasInvoiceItem/SaasUpdDocument; шаблоны pdf/invoice|act - Нумерация СЧ-ГГГГ-NNNNN (advisory-lock); просрочка invoices:expire (cron) - Наименование услуги: «Оплата генерации рекламных лидов» - Зависимость barryvdh/laravel-dompdf (default_font dejavu sans); схема БД не менялась - Этап 2 (автомат через ВТБ API) — отдельно, спека/план в docs/superpowers Тесты: счета 13, Billing 138, фронт зелёные; larastan baseline +6 (Pest false-pos). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -139,6 +139,11 @@ Route::middleware(['saas-admin', 'admin-db'])->group(function () {
|
||||
// SaaS-admin → Биллинг: aggregates пополнений/списаний за текущий месяц.
|
||||
Route::get('/api/admin/billing', 'App\Http\Controllers\Api\AdminBillingController@index');
|
||||
|
||||
// SaaS-admin → Счета: список выставленных счетов + ручная отметка оплаты (Этап 1).
|
||||
Route::get('/api/admin/invoices', 'App\Http\Controllers\Api\AdminInvoiceController@index');
|
||||
Route::post('/api/admin/invoices/{id}/mark-paid', 'App\Http\Controllers\Api\AdminInvoiceController@markPaid')
|
||||
->whereNumber('id');
|
||||
|
||||
// Sprint 3D (G4): SaaS-admin billing row-actions — приостановка/возврат/смена тарифа.
|
||||
Route::get('/api/admin/billing/tariff-plans', 'App\Http\Controllers\Api\AdminBillingController@tariffPlans');
|
||||
Route::patch('/api/admin/billing/tenants/{id}/status', 'App\Http\Controllers\Api\AdminBillingController@updateStatus')
|
||||
@@ -238,6 +243,9 @@ Route::middleware(['auth:sanctum', 'tenant'])->prefix('/api/billing')->group(fun
|
||||
Route::get('/balance-status', 'App\Http\Controllers\Api\BillingController@balanceStatus');
|
||||
Route::get('/transactions', 'App\Http\Controllers\Api\BillingController@transactions');
|
||||
Route::get('/invoices', 'App\Http\Controllers\Api\BillingController@invoices');
|
||||
Route::post('/invoices', 'App\Http\Controllers\Api\InvoiceController@store');
|
||||
Route::get('/invoices/{id}/pdf', 'App\Http\Controllers\Api\InvoiceController@pdf')->whereNumber('id');
|
||||
Route::get('/invoices/{id}/act', 'App\Http\Controllers\Api\InvoiceController@act')->whereNumber('id');
|
||||
});
|
||||
|
||||
// API-ключи тенанта (audit D2/D3/J5). RLS на api_keys требует tenant middleware.
|
||||
@@ -381,6 +389,7 @@ Route::view('/import', 'welcome'); // Sprint 4 — CSV-импорт истори
|
||||
Route::view('/admin', 'welcome');
|
||||
Route::view('/admin/tenants', 'welcome');
|
||||
Route::view('/admin/billing', 'welcome');
|
||||
Route::view('/admin/invoices', 'welcome');
|
||||
Route::view('/admin/incidents', 'welcome');
|
||||
Route::view('/admin/system', 'welcome');
|
||||
Route::view('/admin/pricing-tiers', 'welcome');
|
||||
|
||||
Reference in New Issue
Block a user