Skip to content

Commit 5c5cc4f

Browse files
committed
[#3793] Change payment reference number
The internal order ID isn't relevant anywore, instead we rely on the PK of the model. The race condition handling is also not relevant anymore as we rely on the PK which is already supposed to be unique.
1 parent 09ab59d commit 5c5cc4f

File tree

1 file changed

+27
-39
lines changed

1 file changed

+27
-39
lines changed

src/openforms/payments/models.py

+27-39
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
1+
from __future__ import annotations
2+
13
import uuid
24
from decimal import Decimal
35
from typing import TYPE_CHECKING
46

5-
from django.db import IntegrityError, models, transaction
6-
from django.db.models import Max, Sum
7+
from django.db import models, transaction
8+
from django.db.models import Sum
79
from django.utils.translation import gettext_lazy as _
810

911
from openforms.plugins.constants import UNIQUE_ID_MAX_LENGTH
@@ -14,18 +16,15 @@
1416
if TYPE_CHECKING:
1517
from openforms.submissions.models import Submission
1618

17-
ORDER_ID_START = 100
18-
ORDER_ID_PAD_LENGTH = 6
19-
2019

21-
class SubmissionPaymentManager(models.Manager):
20+
class SubmissionPaymentManager(models.Manager["SubmissionPayment"]):
2221
def create_for(
2322
self,
24-
submission: "Submission",
23+
submission: Submission,
2524
plugin_id: str,
2625
plugin_options: dict,
2726
amount: Decimal,
28-
):
27+
) -> SubmissionPayment:
2928
assert isinstance(amount, Decimal)
3029

3130
# first create without order_id
@@ -36,37 +35,27 @@ def create_for(
3635
amount=amount,
3736
)
3837
# then update with a unique order_id
39-
while True:
40-
try:
41-
with transaction.atomic():
42-
payment.order_id = self.get_next_order_id()
43-
payment.public_order_id = self.create_public_order_id_for(payment)
44-
payment.save(update_fields=("order_id", "public_order_id"))
45-
break
46-
except IntegrityError:
47-
# race condition on unique order_id
48-
continue
49-
return payment
5038

51-
def get_next_order_id(self) -> int:
52-
agg = self.aggregate(Max("order_id"))
53-
max_order_id = agg["order_id__max"] or ORDER_ID_START
54-
return max_order_id + 1
39+
with transaction.atomic():
40+
payment.public_order_id = self.create_public_order_id_for(payment)
41+
payment.save(update_fields=("public_order_id",))
42+
43+
return payment
5544

5645
@staticmethod
57-
def create_public_order_id_for(payment: "SubmissionPayment") -> str:
46+
def create_public_order_id_for(payment: SubmissionPayment) -> str:
5847
config = GlobalConfiguration.get_solo()
59-
prefix = config.payment_order_id_prefix
60-
prefix = prefix.replace("{year}", str(payment.created.year))
61-
order_id = str(payment.order_id).rjust(ORDER_ID_PAD_LENGTH, "0")
62-
return prefix + order_id
48+
prefix = config.payment_order_id_prefix.format(year=payment.created.year)
49+
return (
50+
f"{prefix}_{payment.submission.public_registration_reference}_{payment.pk}"
51+
)
6352

6453

65-
class SubmissionPaymentQuerySet(models.QuerySet):
54+
class SubmissionPaymentQuerySet(models.QuerySet["SubmissionPayment"]):
6655
def sum_amount(self) -> Decimal:
6756
return self.aggregate(sum_amount=Sum("amount"))["sum_amount"] or Decimal("0")
6857

69-
def get_completed_public_order_ids(self) -> list[int]:
58+
def get_completed_public_order_ids(self) -> list[str]:
7059
return list(
7160
self.filter(
7261
status__in=(PaymentStatus.registered, PaymentStatus.completed)
@@ -88,17 +77,16 @@ class SubmissionPayment(models.Model):
8877
null=True,
8978
help_text=_("Copy of payment options at time of initializing payment."),
9079
)
91-
order_id = models.BigIntegerField(
92-
_("Order ID (internal)"),
93-
unique=True,
94-
null=True,
95-
help_text=_("Unique tracking across backend"),
96-
)
80+
# TODO Django 5.2 Update to a `GeneratedField`
9781
public_order_id = models.CharField(
9882
_("Order ID"),
9983
max_length=32,
10084
blank=True,
101-
help_text=_("Order ID stored with payment provider."),
85+
help_text=_(
86+
"The order ID to be sent to the payment provider. This ID is built by "
87+
"concatenating an optional global prefix, the submission public reference "
88+
"and a unique incrementing ID."
89+
),
10290
)
10391
amount = models.DecimalField(
10492
_("payment amount"),
@@ -127,8 +115,8 @@ class Meta:
127115
)
128116
]
129117

130-
def __str__(self):
131-
return f"#{self.order_id} '{self.get_status_display()}' {self.amount}"
118+
def __str__(self) -> str:
119+
return f"#{self.public_order_id} '{self.get_status_display()}' {self.amount}"
132120

133121
@property
134122
def form(self):

0 commit comments

Comments
 (0)