Skip to content

Commit

Permalink
💩(backend) reassign organization for pending orders
Browse files Browse the repository at this point in the history
On submit request, if the related order is in pending state, we want to
reassign a new organization. This is a dirty workaround to prevent to create
a migration for this fix. A better solution will be implemented in the
development branch.
  • Loading branch information
jbpenrath committed Sep 19, 2024
1 parent f55d69a commit 35893fc
Show file tree
Hide file tree
Showing 3 changed files with 76 additions and 28 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@ and this project adheres to

## [Unreleased]

### Changed

- Reassign organization for pending orders

### Fixed

- Improve signature backend `handle_notification` error catching
Expand Down
12 changes: 10 additions & 2 deletions src/backend/joanie/core/api/client/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -332,7 +332,12 @@ def _get_organization_with_least_active_orders(
"order",
filter=clause
& Q(order__product=product)
& ~Q(order__state=enums.ORDER_STATE_CANCELED),
& ~Q(
order__state__in=[
enums.ORDER_STATE_CANCELED,
enums.ORDER_STATE_PENDING,
]
),
)

try:
Expand Down Expand Up @@ -425,7 +430,10 @@ def submit(self, request, pk=None): # pylint: disable=no-self-use, invalid-name
credit_card_id = request.data.get("credit_card_id")
order = self.get_object()

if order.organization is None:
# If the order is in pending state, we want to reaffect an organization
# when the order is resubmit. This is a temporary fix to prevent to
# create a migration on the main branch.
if order.organization is None or order.state == enums.ORDER_STATE_PENDING:
order.organization = self._get_organization_with_least_active_orders(
order.product, order.course, order.enrollment
)
Expand Down
88 changes: 62 additions & 26 deletions src/backend/joanie/tests/core/api/order/test_submit.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
"""Tests for the Order submit API."""

import random
from http import HTTPStatus
from unittest import mock

from django.core.cache import cache
from django.db.models import Count, Q

from joanie.core import enums, factories
from joanie.core.api.client import OrderViewSet
Expand Down Expand Up @@ -218,6 +216,30 @@ def test_api_order_submit_should_auto_assign_organization_if_needed(

mocked_round_robin.assert_not_called()

@mock.patch.object(
OrderViewSet, "_get_organization_with_least_active_orders", return_value=None
)
def test_api_order_submit_should_auto_assign_organization_if_pending(
self, mocked_round_robin
):
"""
Order should have organization auto assigned on submit if its state is
pending
"""
user = factories.UserFactory()
token = self.generate_token_from_user(user)

# Auto assignment should have been triggered if order has no organization linked
order = factories.OrderFactory(owner=user, state=enums.ORDER_STATE_PENDING)
self.client.patch(
f"/api/v1.0/orders/{order.id}/submit/",
content_type="application/json",
data={"billing_address": BillingAddressDictFactory()},
HTTP_AUTHORIZATION=f"Bearer {token}",
)

mocked_round_robin.assert_called_once()

def test_api_order_submit_auto_assign_organization_with_least_orders(self):
"""
Order auto-assignment logic should always return the organization with the least
Expand All @@ -226,32 +248,46 @@ def test_api_order_submit_auto_assign_organization_with_least_orders(self):
user = factories.UserFactory()
token = self.generate_token_from_user(user)

organizations = factories.OrganizationFactory.create_batch(2)
organization, expected_organization = (
factories.OrganizationFactory.create_batch(2)
)

relation = factories.CourseProductRelationFactory(organizations=organizations)
relation = factories.CourseProductRelationFactory(
organizations=[organization, expected_organization]
)

# Create randomly several orders linked to one of both organization
for _ in range(5):
factories.OrderFactory(
organization=random.choice(organizations),
product=relation.product,
course=relation.course,
state=random.choice(
[enums.ORDER_STATE_DRAFT, enums.ORDER_STATE_CANCELED]
),
)
# Create 3 orders for the first organization (1 draft, 1 pending, 1 canceled)
factories.OrderFactory(
organization=organization,
product=relation.product,
course=relation.course,
state=enums.ORDER_STATE_DRAFT,
)
factories.OrderFactory(
organization=organization,
product=relation.product,
course=relation.course,
state=enums.ORDER_STATE_PENDING,
)
factories.OrderFactory(
organization=organization,
product=relation.product,
course=relation.course,
state=enums.ORDER_STATE_CANCELED,
)

organization_with_least_active_orders = (
relation.organizations.annotate(
order_count=Count(
"order",
filter=Q(order__course=relation.course)
& Q(order__product=relation.product)
& ~Q(order__state=enums.ORDER_STATE_CANCELED),
)
)
.order_by("order_count")
.first()
# 2 ignored orders for the second organization (1 pending, 1 canceled)
factories.OrderFactory(
organization=expected_organization,
product=relation.product,
course=relation.course,
state=enums.ORDER_STATE_PENDING,
)
factories.OrderFactory(
organization=expected_organization,
product=relation.product,
course=relation.course,
state=enums.ORDER_STATE_CANCELED,
)

# Then create an order without organization
Expand All @@ -271,4 +307,4 @@ def test_api_order_submit_auto_assign_organization_with_least_orders(self):
)

order.refresh_from_db()
self.assertEqual(order.organization, organization_with_least_active_orders)
self.assertEqual(order.organization, expected_organization)

0 comments on commit 35893fc

Please sign in to comment.