django-getpaid/django-plans

Duplicated invoice numbers

PetrDlouhy opened this issue · 2 comments

I have just bumped to this issue, and I would like to be recorded here as soon as possible, because I think it can cause serious accounting problems. I will post more details, as I will investigate this more deeply.

I have got duplicated invoice numbers on server, where I was testing recurring payments. I noticed, that the duplicated numbers are on invoices from orders with (almost) same creation times (which could is common for invoices for automatically renewed plans).

When I tried to send two payments at exactly the same time, I also got duplicated invoices. So I think, that the problem is present in all versions for some time now. Only it doesn't occur so often in non-recurring payments, because it is quite uncommon to have two purchases at the same time.

A I investigated further, I hope, this is concurrency issue - in my tests the save() on both invoices occurs much later, than the clean() method finishes. So the invoice numbers are set bad at that time.

Some concurrency issues are described here: https://kevinmahoney.co.uk/articles/django-orm-mistakes/

Now I have investigated this quite deeply, I tried different approaches like using subquery

last_number = Coalesce(Subquery(older_invoices.order_by("-number").values('number')[:1]), 0)

to generate the invoice number in the SQL INSERT query, or having helper unique hidden model fields for all three sequence types.

After consulting many internet sources and StackOverflow answers, I came to that the best way to have the sequence truly continuous (without duplicities and gaps) would be to use django-sequences for that purpose, which adds new dependency and need to add their application to INSTALLED_APPS. But I think it is worth that, because it can prevent serious problems with accounting.