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:
@@ -576,3 +576,39 @@ export async function executePdErasure(id: number, adminUserId?: number): Promis
|
||||
const { data } = await apiClient.post<EraseSubjectResult>(`/api/admin/pd-subject-requests/${id}/erase`, payload);
|
||||
return data;
|
||||
}
|
||||
|
||||
// --- Оплата по счёту (Этап 1): список счетов + ручная отметка оплаты ---
|
||||
|
||||
export interface AdminInvoiceRow {
|
||||
id: number;
|
||||
invoice_number: string;
|
||||
amount_total: string;
|
||||
status: string;
|
||||
issued_at: string;
|
||||
expires_at: string | null;
|
||||
tenant_id: number;
|
||||
tenant_name: string | null;
|
||||
payer_name: string | null;
|
||||
}
|
||||
|
||||
export interface ListAdminInvoicesParams {
|
||||
status?: string;
|
||||
search?: string;
|
||||
page?: number;
|
||||
per_page?: number;
|
||||
}
|
||||
|
||||
export interface ListAdminInvoicesResponse {
|
||||
data: AdminInvoiceRow[];
|
||||
meta: { current_page: number; last_page: number; total: number; per_page: number };
|
||||
}
|
||||
|
||||
export async function listAdminInvoices(params: ListAdminInvoicesParams = {}): Promise<ListAdminInvoicesResponse> {
|
||||
const { data } = await apiClient.get<ListAdminInvoicesResponse>('/api/admin/invoices', { params });
|
||||
return data;
|
||||
}
|
||||
|
||||
export async function markInvoicePaid(id: number): Promise<void> {
|
||||
await ensureCsrfCookie();
|
||||
await apiClient.post(`/api/admin/invoices/${id}/mark-paid`);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user