Skip to content

Commit

Permalink
fixup! ✨(backend) add start and end date on order groups model
Browse files Browse the repository at this point in the history
  • Loading branch information
jonathanreveille committed Feb 7, 2025
1 parent 514d25b commit 371e71a
Show file tree
Hide file tree
Showing 10 changed files with 55 additions and 75 deletions.
6 changes: 1 addition & 5 deletions src/backend/joanie/core/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -230,11 +230,7 @@ def get_readonly_fields(self, request, obj=None):

def nb_available_seats(self, obj): # pylint: disable=no-self-use
"""Return the number of available seats for this order group."""
return (
obj.nb_seats - obj.get_nb_binding_orders()
if obj.nb_seats is not None
else None
)
return obj.available_seats


class CourseProductRelationInline(admin.StackedInline):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
class Migration(migrations.Migration):

dependencies = [
('core', '0053_alter_certificate_options_and_more'),
('core', '0054_discount_discount_discount_rate_or_amount_required_and_more'),
]

operations = [
Expand All @@ -24,14 +24,10 @@ class Migration(migrations.Migration):
migrations.AlterField(
model_name='ordergroup',
name='nb_seats',
field=models.IntegerField(blank=True, default=None, help_text='The maximum number of orders that can be validated for a given order group', null=True, validators=[django.core.validators.MinValueValidator(0)], verbose_name='Number of seats'),
field=models.PositiveSmallIntegerField(blank=True, default=None, help_text='The maximum number of orders that can be validated for a given order group', null=True, verbose_name='Number of seats'),
),
migrations.AddConstraint(
model_name='ordergroup',
constraint=models.CheckConstraint(check=models.Q(('start__lte', models.F('end'))), name='check_start_before_end', violation_error_message='Start date cannot be greater than end date'),
),
migrations.AddConstraint(
model_name='ordergroup',
constraint=models.CheckConstraint(check=models.Q(('nb_seats__gte', 0), ('nb_seats__isnull', True), _connector='OR'), name='check_nb_seats_non_negative_or_null'),
),
]
62 changes: 33 additions & 29 deletions src/backend/joanie/core/models/products.py
Original file line number Diff line number Diff line change
Expand Up @@ -365,15 +365,14 @@ def delete(self, using=None, keep_parents=False):
class OrderGroup(BaseModel):
"""Order group to enforce a maximum number of seats for a product."""

nb_seats = models.IntegerField(
nb_seats = models.PositiveSmallIntegerField(
default=None,
verbose_name=_("Number of seats"),
help_text=_(
"The maximum number of orders that can be validated for a given order group"
),
null=True,
blank=True,
validators=[MinValueValidator(0)],
)
course_product_relation = models.ForeignKey(
to=CourseProductRelation,
Expand Down Expand Up @@ -403,10 +402,6 @@ class Meta:
name="check_start_before_end",
violation_error_message=_("Start date cannot be greater than end date"),
),
models.CheckConstraint(
check=models.Q(nb_seats__gte=0) | models.Q(nb_seats__isnull=True),
name="check_nb_seats_non_negative_or_null",
),
]

def get_nb_binding_orders(self):
Expand All @@ -426,6 +421,15 @@ def can_edit(self):
"""Return True if the order group can be edited."""
return not self.orders.exists()

@property
def available_seats(self) -> int | None:
"""Return the number of available seats on the order group."""
if self.nb_seats is None:
return None
return self.nb_seats - self.get_nb_binding_orders()

# ruff: noqa: PLR0911
# pylint: disable=too-many-return-statements
@property
def is_enabled(self):
"""
Expand All @@ -438,36 +442,36 @@ def is_enabled(self):
"""
if not self.is_active:
return False

# Unlimited spots
if self.is_active and self.nb_seats is None:
if self.nb_seats is None:
return True

available_seats = self.nb_seats - self.get_nb_binding_orders()
if not available_seats:
if self.nb_seats - self.get_nb_binding_orders() == 0:
return False
if not self.start and not self.end:
return True

now = timezone.now()
if self.start and self.end:
return self.start <= now <= self.end

if self.course_product_relation.product.type == enums.PRODUCT_TYPE_CERTIFICATE: # pylint: disable=no-member
instance = self.course_product_relation.course
course_run_dates = (
self.course_product_relation.course.get_equivalent_course_run_dates( # pylint: disable=no-member
ignore_archived=True
)
)
else:
instance = self.course_product_relation.product
course_run_dates = instance.get_equivalent_course_run_dates( # pylint: disable=no-member
ignore_archived=True
)

is_active = False
now = timezone.now()
course_run_dates = (
self.course_product_relation.product.get_equivalent_course_run_dates( # pylint: disable=no-member
ignore_archived=True
)
)
if self.end: # Early birds
return course_run_dates["enrollment_start"] <= now <= self.end
if self.start: # Last minutes
return self.start <= now <= course_run_dates["enrollment_end"]

if not self.start and not self.end:
is_active = self.is_active
elif self.start and self.end:
is_active = self.start <= now <= self.end
elif self.end: # Early birds
is_active = self.end >= now >= course_run_dates["enrollment_start"]
elif self.start: # Last minutes
is_active = self.start <= now <= course_run_dates["enrollment_end"]

return is_active
return False


class OrderManager(models.Manager):
Expand Down
6 changes: 1 addition & 5 deletions src/backend/joanie/core/serializers/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -475,11 +475,7 @@ class Meta:

def get_nb_available_seats(self, order_group) -> int | None:
"""Return the number of available seats for this order group."""
return (
order_group.nb_seats - order_group.get_nb_binding_orders()
if order_group.nb_seats is not None
else None
)
return order_group.available_seats


@extend_schema_serializer(exclude_fields=("course_product_relation",))
Expand Down
6 changes: 1 addition & 5 deletions src/backend/joanie/core/serializers/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -776,11 +776,7 @@ class Meta:

def get_nb_available_seats(self, order_group) -> int | None:
"""Return the number of available seats for this order group."""
return (
order_group.nb_seats - order_group.get_nb_binding_orders()
if order_group.nb_seats is not None
else None
)
return order_group.available_seats


class DefinitionResourcesProductSerializer(serializers.ModelSerializer):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ def test_api_admin_orders_course_retrieve(self):
total=D("1.00"),
)

with self.assertNumQueries(42):
with self.assertNumQueries(40):
response = self.client.get(f"/api/v1.0/admin/orders/{order.id}/")

self.assertEqual(response.status_code, HTTPStatus.OK)
Expand Down
10 changes: 5 additions & 5 deletions src/backend/joanie/tests/core/test_api_admin_order_group.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ def test_admin_api_order_group_list_authenticated(self):
)
factories.OrderGroupFactory.create_batch(5)

with self.assertNumQueries(19):
with self.assertNumQueries(13):
response = self.client.get(f"{self.base_url}/{relation.id}/order-groups/")
self.assertEqual(response.status_code, HTTPStatus.OK)
content = response.json()
Expand Down Expand Up @@ -116,7 +116,7 @@ def test_admin_api_order_group_retrieve_authenticated(self):
relation = factories.CourseProductRelationFactory()
order_group = factories.OrderGroupFactory(course_product_relation=relation)

with self.assertNumQueries(8):
with self.assertNumQueries(6):
response = self.client.get(
f"{self.base_url}/{relation.id}/order-groups/{order_group.id}/"
)
Expand Down Expand Up @@ -195,7 +195,7 @@ def test_admin_api_order_group_create_authenticated(self):
"nb_seats": 5,
"is_active": True,
}
with self.assertNumQueries(9):
with self.assertNumQueries(7):
response = self.client.post(
f"{self.base_url}/{relation.id}/order-groups/",
content_type="application/json",
Expand Down Expand Up @@ -239,7 +239,7 @@ def test_admin_api_order_group_update_authenticated(self):
"nb_seats": 505,
"is_active": True,
}
with self.assertNumQueries(9):
with self.assertNumQueries(7):
response = self.client.put(
f"{self.base_url}/{relation.id}/order-groups/{str(order_group.id)}/",
content_type="application/json",
Expand Down Expand Up @@ -283,7 +283,7 @@ def test_admin_api_order_group_patch_authenticated(self):
data = {
"is_active": True,
}
with self.assertNumQueries(9):
with self.assertNumQueries(7):
response = self.client.patch(
f"{self.base_url}/{relation.id}/order-groups/{str(order_group.id)}/",
content_type="application/json",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -777,7 +777,7 @@ def test_api_course_product_relation_read_detail_with_order_groups(self):
course=course, product=product, order_group=order_group1, state=state
)

with self.assertNumQueries(57):
with self.assertNumQueries(55):
self.client.get(
f"/api/v1.0/course-product-relations/{relation.id}/",
HTTP_AUTHORIZATION=f"Bearer {token}",
Expand Down
18 changes: 5 additions & 13 deletions src/backend/joanie/tests/core/test_models_order_group.py
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ def test_models_order_group_is_not_active_is_not_enabled(self):

self.assertFalse(order_group.is_enabled)

def test_models_order_group_is_active_and_nb_seats(self):
def test_models_order_group_is_enabled_is_active_and_nb_seats(self):
"""
When the order group is active and the nb_seats is set to None, the property
`is_enabled` should return True. Otherwise, it should return False.
Expand All @@ -91,7 +91,7 @@ def test_models_order_group_is_active_and_nb_seats(self):

self.assertFalse(order_group.is_enabled)

def test_models_order_group_is_active_is_enabled_when_no_dates(
def test_models_order_group_is_enabled_is_active_when_no_dates(
self,
):
"""
Expand All @@ -102,7 +102,7 @@ def test_models_order_group_is_active_is_enabled_when_no_dates(

self.assertTrue(order_group.is_enabled)

def test_models_order_group_is_active_is_enabled_and_now_between_start_and_end_dates(
def test_models_order_group_is_enabled_is_active_and_now_between_start_and_end_dates(
self,
):
"""
Expand Down Expand Up @@ -211,7 +211,7 @@ def test_models_order_group_is_enabled_credential_product_last_minute(
order_group = factories.OrderGroupFactory(
course_product_relation=relation,
is_active=False,
start=timezone.now(),
start=timezone.now() - timedelta(seconds=30),
end=None,
)

Expand Down Expand Up @@ -296,15 +296,7 @@ def test_models_order_group_is_enabled_certificate_product_last_minute(

self.assertTrue(order_group.is_enabled)

def test_models_order_group_nb_of_seats_negative_number(self):
"""
It should not be possible to insert a negative value for the number of seats
on the order group.
"""
with self.assertRaises(IntegrityError):
factories.OrderGroupFactory(nb_seats=-1)

def test_models_order_group_nb_seats_is_null_and_is_not_active(self):
def test_models_order_group_is_enabled_nb_seats_is_null_and_is_not_active(self):
"""
When the order group is active and nb seats is null, orders can be created
on that group. As soon as the order group is not active, it should not let
Expand Down
10 changes: 5 additions & 5 deletions src/backend/joanie/tests/swagger/admin-swagger.json
Original file line number Diff line number Diff line change
Expand Up @@ -6391,7 +6391,7 @@
},
"nb_seats": {
"type": "integer",
"maximum": 2147483647,
"maximum": 32767,
"minimum": 0,
"nullable": true,
"title": "Number of seats",
Expand Down Expand Up @@ -6456,7 +6456,7 @@
},
"nb_seats": {
"type": "integer",
"maximum": 2147483647,
"maximum": 32767,
"minimum": 0,
"nullable": true,
"title": "Number of seats",
Expand Down Expand Up @@ -6515,7 +6515,7 @@
"properties": {
"nb_seats": {
"type": "integer",
"maximum": 2147483647,
"maximum": 32767,
"minimum": 0,
"nullable": true,
"title": "Number of seats",
Expand Down Expand Up @@ -6547,7 +6547,7 @@
"properties": {
"nb_seats": {
"type": "integer",
"maximum": 2147483647,
"maximum": 32767,
"minimum": 0,
"nullable": true,
"title": "Number of seats",
Expand Down Expand Up @@ -8603,7 +8603,7 @@
"properties": {
"nb_seats": {
"type": "integer",
"maximum": 2147483647,
"maximum": 32767,
"minimum": 0,
"nullable": true,
"title": "Number of seats",
Expand Down

0 comments on commit 371e71a

Please sign in to comment.