Skip to main content

Black Friday 2025! Only until December 1st: coupon FRIDAY25 for 40% off Yearly/Lifetime membership!

Read more here
Premium Members Only
Join to unlock this tutorial and all of our courses.
Tutorial Premium Tutorial

Laravel Invoices: Auto-Generate Serial Numbers - 4 Different Ways

September 25, 2023
10 min read

Tutorial last revisioned on March 17, 2024 with Laravel 11

When working with invoices, you need to deal with serial numbers that look like ABC-000001. Do you know how to auto-generate them in Laravel? This tutorial will cover a few ways to do this.


DB Structure

For our example, we will use a simple invoice DB table with the following columns:

  • id
  • user_id
  • due_date
  • amount
  • serial - Full serial number like ABC-1
  • serial_number - Serial number like 1
  • serial_series - Serial series like ABC

Here's how that looks in our migration:

Migration

Schema::create('invoices', function (Blueprint $table) {
$table->id();
$table->foreignIdFor(User::class)->constrained();
$table->date('due_date');
$table->integer('amount');
$table->string('serial')->nullable();
$table->string('serial_series');
$table->integer('serial_number')->nullable();
$table->timestamps();
});

This makes our Model look like this:

app/Models/Invoice.php

use Illuminate\Database\Eloquent\Casts\Attribute;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
 
class Invoice extends Model
{
use HasFactory;
 
protected $fillable = [
'user_id',
'due_date',
'amount',
'serial',
'serial_number',
'serial_series',
];
 
protected function amount(): Attribute
{
return Attribute::make(
get: fn($value) => $value / 100,
set: fn($value) => $value * 100,
);
}
 
public function user(): BelongsTo
{
return $this->belongsTo(User::class);
}
}

Create Invoice: Form

Let's create the Controller methods to save a new invoice and auto-generate the serial number.

This is the form, with series options coming from the config.

app/Http/Controllers/InvoiceController.php

use App\Http\Requests\StoreInvoiceRequest;
use App\Models\Invoice;
use App\Models\User;
 
class InvoiceController extends Controller
{
public function create()
{
$users = User::pluck('name', 'id');
$invoiceSeries = config('invoiceSettings.availableInvoiceSeries');
 
return view('invoices.create', [
'users' => $users,
'invoiceSeries' => $invoiceSeries,
]);
}
}

These are the config values:

config/invoiceSettings.php

return [
'availableInvoiceSeries' => [
'ABC',
'DAF',
'GHI',
'UKS'
],
];

And here's the form in...

Premium Members Only

This advanced tutorial is available exclusively to Laravel Daily Premium members.

Premium membership includes:

Access to all premium tutorials
Video and Text Courses
Private Discord Channel

Comments & Discussion

YD
Yimy David ✓ Link copied!

Is there a way to make it restart every year? 0001-2022 0002-2022 .... 0160-2022 0001-2023

M
Modestas ✓ Link copied!

yes!

Set your serial series to the year and that's it!

For example in config file set it to now("Y")

YD
Yimy David ✓ Link copied!

like this?

return [ 'availableInvoiceSeries' => [ now("Y"); ], ];

M
Modestas ✓ Link copied!

Yes, just without typos:

return [
    'availableInvoiceSeries' => [
				now('Y'),
    ],
];

Of course, this will be the only selection in the dropdown, but you can make it a hidden field instead of select (if you don't need to select the series)

YD
Yimy David ✓ Link copied!

thank you very much for your help

F
Fonog ✓ Link copied!

Hello

  1. Git repositories mentioned does not exists , 404 Not found
  2. as these applications are used by Team(multi user), when two users generating Invoice at same time there can be duplication of generated invoice number.
M
Modestas ✓ Link copied!

Hi,

  1. We might have missed that in an update! Sorry about this.
  2. Sorry, where did we mention multi-tenancy here? I don't think that this is designed with multi-tenancy in mind. To do so, you do have to have a different workflow from ours :)
ED
Enrique De Jesus Robledo Camacho ✓ Link copied!

Verry usefull, i found this for primary, would this avoid using the job? I mean if some of the columns used is autoincrement. https://laravel.com/api/10.x/Illuminate/Database/Schema/Blueprint.html#method_primary

M
Modestas ✓ Link copied!

The main purpose of the job is not only to avoid the issue with repeating numbers, but to also solve the problem of re-trying the job.

Imagine, you have an index that's unique (mentioned in 3rd option) and when trying to create the invoice - it fails. Then you have to manually re-try the saving with a new number. Job does by itself.

Also, auto-increment is okay, but it would not be PER each of the series, so that causes other issues where gaps will appear. And gaps are pretty bad!

IM
Ismail Mahmoud ✓ Link copied!

What about soft deleted records? how to deal with them? here:

$this->invoice->serial_number = (Invoice::where('serial_series', $this->invoice->serial_series)->max('serial_number') ?? 0) + 1;

M
Modestas ✓ Link copied!

You can add withTrashed() and that will cover it

We'd Love Your Feedback

Tell us what you like or what we can improve

Feel free to share anything you like or dislike about this page or the platform in general.