69 lines
2.1 KiB
PHP
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);
|
|
}
|
|
}
|