Create Custom Invoice Id

I’m afraid my solution will duplicate invoice _d if 2 users make invoices on stock at the same time
how can I prevent this I read about select_for_update but I don’t know what is the right place to implement it

class Order(models.Model):
    invoice_id = models.CharField(max_length=20)
    stock = models.ForeignKey(Stock, on_delete=models.CASCADE)
    
    class Meta:
        constraints = [
            models.UniqueConstraint(fields=['invoice_id', 'stock'],
                                    name='Order Id Already exists With a Stock'),
        ]

    def generate_invoice_id(self):
        # Get the count of existing invoices of stock and add 1
        sequential_number = self.stock.order_set.count() + 1
        return f'INV-{sequential_number}'  # format: INV-123

    def save(self, *args, **kwargs):
        if not self.invoice_id:
            self.invoice_id = self.generate_invoice_id()
            super().save(*args, **kwargs)

This is a bad idea for a number of reasons. And yes, the chance that this method generates a duplicate value will increase over time.

I’d first ask the question, “Is it really a requirement that invoice numbers be sequential and without gaps?” If that is the case, you have a much bigger issue that needs to be addressed. (Like answering the question, why is this so important.)

Otherwise, I’d suggest just using the id field on the model as your “sequential_number”. It doesn’t even need to be stored in the database as a separate entity - you can add a model method to generate it as needed when it’s needed to be displayed.

Beyond that, if there is a valid reason to segregate invoice numbers by Stock, you could add a field on Stock for current_invoice_number, with a model method that within a transaction does the select_for_update to get the Stock instance, increment the field, save it to the database, and return the value.

Is it really a requirement that invoice numbers be sequential and without gaps?

Ok I need to do this because every user can have multiple stocks when users create invoices from different stocks the invoice ID will be sequential which is not right user A has nothing to do with User B invoices.
User A creates an invoice with id 1 the user B invoice id will be 2

I want every stock ID order to be unique in its stock

the other reason if the order fails the invoice will reserve the ID if the order ID 1 fails the next order ID will be 2 which is not ideal transaction will not help in this case because I need to save the order first for some operations

If I understand your solution correctly it would be like this

class Stock(models.Model):
    current_invoice_number = models.IntegerField(default=0)
class Order(models.Model):
    def generate_invoice_id(self):
        with transaction.atomic():
            stock_instance = Stock.objects.select_for_update().get(id=self.stock.id)
            current_invoice_number = stock_instance.current_invoice_number
            new_invoice_number = current_invoice_number + 1
            stock_instance.current_invoice_number = new_invoice_number
            stock_instance.save()
            return f'INV-{new_invoice_number}'

    def save(self, *args, **kwargs):
        if not self.invoice_id:
            self.invoice_id = self.generate_invoice_id()
            super().save(*args, **kwargs)

Agreed. But who cares?

Making them strictly sequential adds a constraint that will need to prevent any two people from creating two invoices for a single stock at the same time. (If you allow for concurrent or near concurrent submission, then you need to single-thread this submission process to prevent a transaction failure from creating a gap in your numbers.)

Basically, yes. But this still doesn’t prevent gaps.

Agreed. But who cares?

it’s the client’s requirement

could you please provide me with how to do it?

Find out How to Customize Invoice in Odoo? Visit Ledger Labs

Would adding your invoices to a queue help where you have a background task that picks up the invoice one at a time and generates the invoice number? Means multiple users can raise invoices and you have a process to handle the load without risking duplicate invoice numbers?