Fix invoice number race condition: use DB lock for atomic increment
This commit is contained in:
@@ -5,6 +5,7 @@ namespace App\Listeners;
|
|||||||
use App\Events\Invoice\Creating;
|
use App\Events\Invoice\Creating;
|
||||||
use App\Models\Invoice;
|
use App\Models\Invoice;
|
||||||
use App\Models\Setting;
|
use App\Models\Setting;
|
||||||
|
use Illuminate\Support\Facades\DB;
|
||||||
|
|
||||||
class InvoiceCreatingListener
|
class InvoiceCreatingListener
|
||||||
{
|
{
|
||||||
@@ -13,15 +14,30 @@ class InvoiceCreatingListener
|
|||||||
*/
|
*/
|
||||||
public function handle(Creating $event): void
|
public function handle(Creating $event): void
|
||||||
{
|
{
|
||||||
|
// Use database lock to prevent duplicate invoice numbers across clustered nodes
|
||||||
|
$number = DB::transaction(function () {
|
||||||
|
$setting = Setting::where('key', 'invoice_number')
|
||||||
|
->whereNull('settingable_id')
|
||||||
|
->lockForUpdate()
|
||||||
|
->first();
|
||||||
|
|
||||||
|
$current = $setting ? (int) $setting->value : (int) config('settings.invoice_number', 0);
|
||||||
|
$next = $current + 1;
|
||||||
|
|
||||||
|
Setting::updateOrCreate([
|
||||||
|
'key' => 'invoice_number',
|
||||||
|
'settingable_id' => null,
|
||||||
|
'settingable_type' => null,
|
||||||
|
], [
|
||||||
|
'value' => $next,
|
||||||
|
]);
|
||||||
|
|
||||||
|
// Update in-memory config so subsequent calls in the same request are correct
|
||||||
|
config(['settings.invoice_number' => $next]);
|
||||||
|
|
||||||
|
return $next;
|
||||||
|
});
|
||||||
|
|
||||||
// Get the next invoice number
|
|
||||||
$number = config('settings.invoice_number', 1) + 1;
|
|
||||||
// Update setting
|
|
||||||
Setting::updateOrCreate([
|
|
||||||
'key' => 'invoice_number',
|
|
||||||
], [
|
|
||||||
'value' => $number,
|
|
||||||
]);
|
|
||||||
// Pad the invoice number with leading zeros
|
// Pad the invoice number with leading zeros
|
||||||
$paddedNumber = str_pad($number, config('settings.invoice_number_padding', 1), '0', STR_PAD_LEFT);
|
$paddedNumber = str_pad($number, config('settings.invoice_number_padding', 1), '0', STR_PAD_LEFT);
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user