Merge pull request #499 from ae-utbm/unify-account-creation

Unify account id creation
This commit is contained in:
thomas girod 2023-01-11 13:26:00 +01:00 committed by GitHub
commit d726f4b1e8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 81 additions and 28 deletions

View File

@ -0,0 +1,21 @@
# Generated by Django 3.2.16 on 2022-12-15 16:09
import accounting.models
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
("counter", "0019_billinginfo"),
]
operations = [
migrations.AlterField(
model_name="customer",
name="amount",
field=accounting.models.CurrencyField(
decimal_places=2, default=0, max_digits=12, verbose_name="amount"
),
),
]

View File

@ -21,6 +21,9 @@
# Place - Suite 330, Boston, MA 02111-1307, USA. # Place - Suite 330, Boston, MA 02111-1307, USA.
# #
# #
from __future__ import annotations
from typing import Tuple
from django.db import models from django.db import models
from django.db.models.functions import Length from django.db.models.functions import Length
@ -57,7 +60,7 @@ class Customer(models.Model):
user = models.OneToOneField(User, primary_key=True, on_delete=models.CASCADE) user = models.OneToOneField(User, primary_key=True, on_delete=models.CASCADE)
account_id = models.CharField(_("account id"), max_length=10, unique=True) account_id = models.CharField(_("account id"), max_length=10, unique=True)
amount = CurrencyField(_("amount")) amount = CurrencyField(_("amount"), default=0)
recorded_products = models.IntegerField(_("recorded product"), default=0) recorded_products = models.IntegerField(_("recorded product"), default=0)
class Meta: class Meta:
@ -94,12 +97,27 @@ class Customer(models.Model):
) < timedelta(days=90) ) < timedelta(days=90)
@classmethod @classmethod
def new_for_user(cls, user: User): def get_or_create(cls, user: User) -> Tuple[Customer, bool]:
""" """
Create a new Customer instance for the user given in parameter without saving it Work in pretty much the same way as the usual get_or_create method,
The account if is automatically generated and the amount set at 0 but with the default field replaced by some under the hood.
If the user has an account, return it as is.
Else create a new account with no money on it and a new unique account id
Example : ::
user = User.objects.get(pk=1)
account, created = Customer.get_or_create(user)
if created:
print(f"created a new account with id {account.id}")
else:
print(f"user has already an account, with {account.id} € on it"
""" """
# account_id are number with a letter appended if hasattr(user, "customer"):
return user.customer, False
# account_id are always a number with a letter appended
account_id = ( account_id = (
Customer.objects.order_by(Length("account_id"), "account_id") Customer.objects.order_by(Length("account_id"), "account_id")
.values("account_id") .values("account_id")
@ -107,14 +125,19 @@ class Customer(models.Model):
) )
if account_id is None: if account_id is None:
# legacy from the old site # legacy from the old site
return cls(user=user, account_id="1504a", amount=0) account = cls.objects.create(user=user, account_id="1504a")
account_id = account_id["account_id"] return account, True
num = int(account_id[:-1])
while Customer.objects.filter(account_id=account_id).exists():
num += 1
account_id = str(num) + random.choice(string.ascii_lowercase)
return cls(user=user, account_id=account_id, amount=0) account_id = account_id["account_id"]
account_num = int(account_id[:-1])
while Customer.objects.filter(account_id=account_id).exists():
# when entering the first iteration, we are using an already existing account id
# so the loop should always execute at least one time
account_num += 1
account_id = f"{account_num}{random.choice(string.ascii_lowercase)}"
account = cls.objects.create(user=user, account_id=account_id)
return account, True
def save(self, allow_negative=False, is_selling=False, *args, **kwargs): def save(self, allow_negative=False, is_selling=False, *args, **kwargs):
""" """

View File

@ -23,6 +23,7 @@
# #
import json import json
import re import re
import string
from django.test import TestCase from django.test import TestCase
from django.urls import reverse from django.urls import reverse
@ -754,18 +755,30 @@ class StudentCardTest(TestCase):
self.assertEqual(response.status_code, 403) self.assertEqual(response.status_code, 403)
class AccountIdTest(TestCase): class CustomerAccountIdTest(TestCase):
def setUp(self): def setUp(self):
user_a = User.objects.create(username="a", password="plop", email="a.a@a.fr") self.user_a = User.objects.create(
username="a", password="plop", email="a.a@a.fr"
)
user_b = User.objects.create(username="b", password="plop", email="b.b@b.fr") user_b = User.objects.create(username="b", password="plop", email="b.b@b.fr")
user_c = User.objects.create(username="c", password="plop", email="c.c@c.fr") user_c = User.objects.create(username="c", password="plop", email="c.c@c.fr")
Customer.objects.create(user=user_a, amount=0, account_id="1111a") Customer.objects.create(user=self.user_a, amount=10, account_id="1111a")
Customer.objects.create(user=user_b, amount=0, account_id="9999z") Customer.objects.create(user=user_b, amount=0, account_id="9999z")
Customer.objects.create(user=user_c, amount=0, account_id="12345f") Customer.objects.create(user=user_c, amount=0, account_id="12345f")
def test_create_customer(self): def test_create_customer(self):
user_d = User.objects.create(username="d", password="plop") user_d = User.objects.create(username="d", password="plop")
customer_d = Customer.new_for_user(user_d) customer, created = Customer.get_or_create(user_d)
customer_d.save() account_id = customer.account_id
number = customer_d.account_id[:-1] number = account_id[:-1]
self.assertTrue(created)
self.assertEqual(number, "12346") self.assertEqual(number, "12346")
self.assertEqual(6, len(account_id))
self.assertIn(account_id[-1], string.ascii_lowercase)
self.assertEqual(0, customer.amount)
def test_get_existing_account(self):
account, created = Customer.get_or_create(self.user_a)
self.assertFalse(created)
self.assertEqual(account.account_id, "1111a")
self.assertEqual(10, account.amount)

View File

@ -1792,11 +1792,7 @@ def create_billing_info(request, user_id):
if user.id != user_id and not user.has_perm("counter:add_billinginfo"): if user.id != user_id and not user.has_perm("counter:add_billinginfo"):
raise PermissionDenied() raise PermissionDenied()
user = get_object_or_404(User, pk=user_id) user = get_object_or_404(User, pk=user_id)
if not hasattr(user, "customer"): customer, _ = Customer.get_or_create(user)
customer = Customer.new_for_user(user)
customer.save()
else:
customer = get_object_or_404(Customer, user_id=user_id)
BillingInfo.objects.create(customer=customer) BillingInfo.objects.create(customer=customer)
return __manage_billing_info_req(request, user_id, True) return __manage_billing_info_req(request, user_id, True)

View File

@ -245,12 +245,13 @@ class Invoice(models.Model):
def validate(self): def validate(self):
if self.validated: if self.validated:
raise DataError(_("Invoice already validated")) raise DataError(_("Invoice already validated"))
customer, created = Customer.get_or_create(user=self.user)
eboutic = Counter.objects.filter(type="EBOUTIC").first() eboutic = Counter.objects.filter(type="EBOUTIC").first()
for i in self.items.all(): for i in self.items.all():
if i.type_id == settings.SITH_COUNTER_PRODUCTTYPE_REFILLING: if i.type_id == settings.SITH_COUNTER_PRODUCTTYPE_REFILLING:
new = Refilling( new = Refilling(
counter=eboutic, counter=eboutic,
customer=self.user.customer, customer=customer,
operator=self.user, operator=self.user,
amount=i.product_unit_price * i.quantity, amount=i.product_unit_price * i.quantity,
payment_method="CARD", payment_method="CARD",
@ -266,7 +267,7 @@ class Invoice(models.Model):
club=product.club, club=product.club,
product=product, product=product,
seller=self.user, seller=self.user,
customer=self.user.customer, customer=customer,
unit_price=i.product_unit_price, unit_price=i.product_unit_price,
quantity=i.quantity, quantity=i.quantity,
payment_method="CARD", payment_method="CARD",

View File

@ -24,7 +24,6 @@
from datetime import date, timedelta from datetime import date, timedelta
from django.db import models from django.db import models
from django.utils import timezone
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
from django.conf import settings from django.conf import settings
from django.core.exceptions import ValidationError from django.core.exceptions import ValidationError
@ -101,8 +100,8 @@ class Subscription(models.Model):
super(Subscription, self).save() super(Subscription, self).save()
from counter.models import Customer from counter.models import Customer
if not Customer.objects.filter(user=self.member).exists(): _, created = Customer.get_or_create(self.member)
Customer.new_for_user(self.member).save() if created:
form = PasswordResetForm({"email": self.member.email}) form = PasswordResetForm({"email": self.member.email})
if form.is_valid(): if form.is_valid():
form.save( form.save(