feat(billing): renewals, coupons, VAT numbering, billing cycles and invoice payment tracking
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 5.5
parent
ea9a53f742
commit
25f23d541e
@@ -0,0 +1,68 @@
|
||||
<?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);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user