Files

69 lines
2.1 KiB
PHP

<?php
namespace App\Support;
use App\Enums\Invoice\InvoiceStatus;
use App\Models\Invoice;
use App\Models\Setting;
use Illuminate\Support\Facades\DB;
class VatNumbering
{
public static function settle(Invoice $invoice): Invoice
{
VatCalculator::refresh($invoice);
return self::issueIfAuto($invoice);
}
public static function issueIfAuto(Invoice $invoice): Invoice
{
if (!$invoice->vat_number
&& (int) $invoice->status === InvoiceStatus::PAID->value
&& (int) $invoice->total > 0
&& VatSettings::numbering()['auto_issue']) {
return self::issue($invoice);
}
return $invoice;
}
public static function issue(Invoice $invoice): Invoice
{
return DB::transaction(function () use ($invoice) {
$locked = Invoice::query()->with('customer')->lockForUpdate()->findOrFail($invoice->id);
if ($locked->vat_number) {
return $locked;
}
Setting::query()->firstOrCreate(['key' => VatSettings::NUMBERING_KEY], ['value' => json_encode(VatSettings::numbering())]);
Setting::query()->where('key', VatSettings::NUMBERING_KEY)->lockForUpdate()->first();
$config = VatSettings::numbering();
VatCalculator::refresh($locked);
$next = $config['next_number'];
while (Invoice::query()->where('vat_serial', $config['serial'])->where('vat_number', self::format($next, $config['padding']))->exists()) {
$next++;
}
$locked->fill([
'vat_serial' => $config['serial'],
'vat_number' => self::format($next, $config['padding']),
'vat_issued_at' => now()->toDateString(),
'buyer_snapshot' => InvoiceBuyer::fromCustomer($locked->customer),
])->save();
VatSettings::saveNumbering([...$config, 'next_number' => $next + 1]);
return $locked;
});
}
private static function format(int $number, int $padding): string
{
return str_pad((string) $number, $padding, '0', STR_PAD_LEFT);
}
}