Files
portal/app/app/Models/SaasInvoiceItem.php
T
Дмитрий a56dcb06b2 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>
2026-06-29 11:25:16 +03:00

43 lines
1.0 KiB
PHP

<?php
declare(strict_types=1);
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
/**
* Позиция счёта (schema.sql table saas_invoice_items). RLS косвенно через invoice_id.
*
* @property int $id
* @property int $invoice_id
* @property string $name
* @property string|null $okpd2
* @property string $quantity
* @property string $unit
* @property string $price
* @property string $amount_net
* @property string|null $vat_rate
* @property string|null $vat_amount
* @property string $amount_total
*/
class SaasInvoiceItem extends Model
{
public $timestamps = false;
protected $fillable = [
'invoice_id', 'name', 'okpd2', 'quantity', 'unit',
'price', 'amount_net', 'vat_rate', 'vat_amount', 'amount_total',
];
protected function casts(): array
{
return [
'quantity' => 'decimal:3',
'price' => 'decimal:2',
'amount_net' => 'decimal:2',
'amount_total' => 'decimal:2',
];
}
}