next(self::CIRCLE_STRIPE); } public function nextManualNumber(): string { return $this->next(self::CIRCLE_MANUAL); } public function next(string $circle): string { if (! in_array($circle, [self::CIRCLE_STRIPE, self::CIRCLE_MANUAL], true)) { throw new InvalidArgumentException("Unbekannter Rechnungskreis: {$circle}"); } $number = DB::transaction(function () use ($circle): int { $sequence = DB::table('invoice_number_sequences') ->where('circle', $circle) ->lockForUpdate() ->first(); if (! $sequence) { DB::table('invoice_number_sequences')->insert([ 'circle' => $circle, 'next_number' => 2, 'created_at' => now(), 'updated_at' => now(), ]); return 1; } DB::table('invoice_number_sequences') ->where('id', $sequence->id) ->update(['next_number' => $sequence->next_number + 1, 'updated_at' => now()]); return (int) $sequence->next_number; }); $padding = (int) config('billing.invoice_number_padding', 5); return sprintf('%s-%s', $circle, str_pad((string) $number, $padding, '0', STR_PAD_LEFT)); } }