cdfae077a3
Клиент сам выставляет 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>
34 lines
1.4 KiB
PHP
34 lines
1.4 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
use App\Models\LegalEntity;
|
|
use App\Models\SaasInvoice;
|
|
use App\Models\Tenant;
|
|
use Illuminate\Foundation\Testing\DatabaseTransactions;
|
|
|
|
uses(DatabaseTransactions::class);
|
|
|
|
function expSeed(int $tenantId, int $leId, string $status, string $number, $expiresAt): SaasInvoice
|
|
{
|
|
return SaasInvoice::create([
|
|
'tenant_id' => $tenantId, 'legal_entity_id' => $leId, 'invoice_number' => $number,
|
|
'payer_type' => 'legal', 'amount_net' => '100.00', 'amount_total' => '100.00',
|
|
'status' => $status, 'issued_at' => now()->subDays(10), 'expires_at' => $expiresAt,
|
|
]);
|
|
}
|
|
|
|
it('помечает overdue только просроченные неоплаченные счета', function () {
|
|
$t = Tenant::factory()->create();
|
|
$le = LegalEntity::create(['code' => 'exp_'.uniqid(), 'name' => 'ИП', 'legal_form' => 'IP', 'inn' => '770000000020']);
|
|
$stale = expSeed($t->id, $le->id, 'issued', 'СЧ-2026-02001', now()->subDay());
|
|
$fresh = expSeed($t->id, $le->id, 'issued', 'СЧ-2026-02002', now()->addDay());
|
|
$paid = expSeed($t->id, $le->id, 'paid', 'СЧ-2026-02003', now()->subDay());
|
|
|
|
$this->artisan('invoices:expire')->assertExitCode(0);
|
|
|
|
expect(SaasInvoice::find($stale->id)->status)->toBe('overdue')
|
|
->and(SaasInvoice::find($fresh->id)->status)->toBe('issued')
|
|
->and(SaasInvoice::find($paid->id)->status)->toBe('paid');
|
|
});
|