Skip to content

Commit 2bfd184

Browse files
authored
Minor b2b improvements (#2825)
* Correctly handling currency codes in b2b happiness messages * Convinient refund() fixture to get rid of _set_current_user in tests * Ability to refund b2b shipped but not paid orders
1 parent 398ef39 commit 2bfd184

File tree

21 files changed

+150
-125
lines changed

21 files changed

+150
-125
lines changed

conftest.py

Lines changed: 11 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2,34 +2,33 @@
22
from django.core.cache import cache
33
from django.utils import translation
44

5-
# fmt: off
65
pytest_plugins = [
6+
"apps.b2b.factory",
7+
"apps.banking.factory",
8+
"apps.banking.fixtures",
9+
"apps.dashamail.fixtures",
710
"apps.diplomas.factory",
11+
"apps.homework.factory",
12+
"apps.lms.factory",
813
"apps.orders.factory",
9-
"apps.b2b.factory",
14+
"apps.orders.fixtures",
1015
"apps.products.factory",
11-
"apps.banking.factory",
12-
"core.factory",
13-
1416
"apps.products.fixtures",
1517
"apps.users.fixtures",
16-
"apps.lms.factory",
17-
"apps.homework.factory",
18-
"apps.dashamail.fixtures",
18+
"core.factory",
1919
"core.fixtures",
2020
]
21-
# fmt: on
2221

2322

2423
@pytest.fixture(autouse=True)
25-
def _cache() -> None: # type: ignore
24+
def _cache() -> None:
2625
"""Clear django cache after each test run."""
2726
yield
2827
cache.clear()
2928

3029

3130
@pytest.fixture
32-
def _en() -> None: # type: ignore
31+
def _en() -> None:
3332
"""Disable django translation"""
34-
with translation.override('en'):
33+
with translation.override("en"):
3534
yield

src/apps/amocrm/tests/services/order/conftest.py

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,3 @@ def paid_order_with_lead(user, course, factory, amocrm_lead):
2121
@pytest.fixture
2222
def paid_order_without_lead(user, course, factory):
2323
return factory.order(user=user, item=course, is_paid=True, author=user)
24-
25-
26-
@pytest.fixture(autouse=True)
27-
def set_current_user(_set_current_user):
28-
return _set_current_user

src/apps/amocrm/tests/services/order/tests_order_pusher.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,9 +48,9 @@ def not_paid_order_with_lead(factory, user, course, amocrm_lead):
4848

4949

5050
@pytest.fixture
51-
def returned_order_with_lead(factory, user, course, amocrm_lead):
51+
def returned_order_with_lead(factory, user, course, amocrm_lead, refund):
5252
order = factory.order(user=user, item=course, is_paid=True, author=user, amocrm_lead=amocrm_lead)
53-
order.refund()
53+
refund(order)
5454
return order
5555

5656

src/apps/b2b/models.py

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
1+
from decimal import Decimal
12
from typing import TYPE_CHECKING
23

34
from django.utils.translation import gettext_lazy as _
45
from django.utils.translation import pgettext_lazy
56

6-
from apps.banking.currency import CurrencyCodes
7+
from apps.banking import currency
78
from core.models import TimestampedModel, models
89

910
if TYPE_CHECKING:
@@ -27,7 +28,7 @@ class Deal(TimestampedModel):
2728
canceled = models.DateTimeField(_("Date when the deal got canceled"), null=True, blank=True)
2829
shipped_without_payment = models.DateTimeField(_("Date when the deal got shipped without payment"), null=True, blank=True)
2930
price = models.DecimalField(_("Price"), max_digits=9, decimal_places=2)
30-
currency = models.CharField(_("Currency"), choices=CurrencyCodes.choices, default="RUB")
31+
currency = models.CharField(_("Currency"), choices=currency.CurrencyCodes.choices(), default="RUB")
3132
currency_rate_on_creation = models.DecimalField(max_digits=9, decimal_places=2, null=True)
3233

3334
class Meta:
@@ -50,6 +51,12 @@ def get_status_representation(self) -> "StrPromise":
5051

5152
return pgettext_lazy("deals", "In progress")
5253

54+
def get_single_order_price(self) -> Decimal:
55+
try:
56+
return Decimal(self.price * currency.get_rate_or_default(self.currency) / self.students.count())
57+
except ArithmeticError:
58+
return Decimal(0)
59+
5360

5461
class Student(TimestampedModel):
5562
user = models.ForeignKey("users.User", on_delete=models.PROTECT)

src/apps/b2b/services/deal_completer.py

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
from dataclasses import dataclass
2-
from decimal import Decimal
32

43
from django.conf import settings
54
from django.contrib.admin.models import CHANGE
@@ -28,7 +27,7 @@ def act(self) -> None:
2827

2928
orders: list[Order] = []
3029

31-
orders += create_orders(deal=self.deal, single_order_price=self.get_single_order_price())
30+
orders += create_orders(deal=self.deal, single_order_price=self.deal.get_single_order_price())
3231
orders += assign_existing_orders(deal=self.deal)
3332

3433
if not self.ship_only:
@@ -41,12 +40,6 @@ def act(self) -> None:
4140
self.mark_deal_as_shipped_without_payment()
4241
self.write_auditlog()
4342

44-
def get_single_order_price(self) -> Decimal:
45-
try:
46-
return Decimal(self.deal.price * currency.get_rate_or_default(self.deal.currency) / self.deal.students.count())
47-
except ArithmeticError:
48-
return Decimal(0)
49-
5043
def mark_deal_as_complete(self) -> None:
5144
self.deal.completed = timezone.now()
5245
self.deal.save()
@@ -71,7 +64,7 @@ def send_happiness_message(self) -> None:
7164

7265
send_telegram_message.delay(
7366
chat_id=settings.HAPPINESS_MESSAGES_CHAT_ID,
74-
text=f"💰+{format_price(self.deal.price)} , {self.deal.customer}, {self.deal.course} продавец {self.deal.author}",
67+
text=f"💰+{format_price(self.deal.price)} {currency.get_symbol(self.deal.currency)}, {self.deal.customer}, {self.deal.course} продавец {self.deal.author}",
7568
)
7669

7770
def write_auditlog(self) -> None:

src/apps/b2b/tests/deal_completer/test_deal_completer_happiness_message.py

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,15 @@ def test_message_text(send_message, completer, deal):
4444
completer(deal=deal)()
4545

4646
assert str(deal.author) in send_message.call_args[1]["text"]
47-
assert "200\xa0500" in send_message.call_args[1]["text"]
47+
assert "200\xa0500" in send_message.call_args[1]["text"]
4848
assert "Росатом" in send_message.call_args[1]["text"]
4949
assert "безопасность" in send_message.call_args[1]["text"]
50+
51+
52+
@pytest.mark.usefixtures("usd")
53+
def test_currency(send_message, completer, deal):
54+
deal.update(price=Decimal(100), currency="usd")
55+
56+
completer(deal=deal)()
57+
58+
assert "100 $" in send_message.call_args[1]["text"]

src/apps/b2b/tests/deal_completer/test_deal_completer_new_order_creation.py

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,10 @@
44

55
from apps.orders.models import Order
66

7-
pytestmark = [pytest.mark.django_db]
8-
9-
10-
@pytest.fixture(autouse=True)
11-
def _usd_rate(factory):
12-
factory.currency(name="USD", rate=Decimal(100))
7+
pytestmark = [
8+
pytest.mark.django_db,
9+
pytest.mark.usefixtures("usd"),
10+
]
1311

1412

1513
def test_orders_are_created(completer, factory):
@@ -66,6 +64,7 @@ def test_price_calculation(completer, factory, student_count, single_order_price
6664
assert str(order.price) == single_order_price
6765

6866

67+
@pytest.mark.usefixtures("usd")
6968
@pytest.mark.parametrize(
7069
("currency", "single_order_price"),
7170
[
@@ -83,6 +82,6 @@ def test_currency_price_calculation(completer, factory, currency, single_order_p
8382
assert str(order.price) == single_order_price
8483

8584

86-
def test_zero_price(completer, factory):
85+
def test_zero_price(factory):
8786
deal = factory.deal(student_count=0, price=Decimal(0))
88-
assert completer(deal=deal).get_single_order_price() == 0
87+
assert deal.get_single_order_price() == 0

src/apps/banking/currency.py

Lines changed: 25 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,35 @@
11
import contextlib
22
from decimal import Decimal
3+
from enum import Enum
4+
from typing import TYPE_CHECKING
35

46
from django.apps import apps
5-
from django.db.models import TextChoices
67
from django.utils.translation import gettext_lazy as _
78

9+
if TYPE_CHECKING:
10+
from django.utils.functional import _StrPromise
811

9-
class CurrencyCodes(TextChoices):
10-
RUB = "RUB", _("RUB")
11-
USD = "USD", _("USD")
12-
KZT = "KZT", _("KZT")
13-
KIS = "KIS", _("KIS (for zero-price orders)")
12+
13+
class CurrencyCodes(Enum):
14+
RUB = "RUB", _("RUB"), "₽"
15+
USD = "USD", _("USD"), "$"
16+
KZT = "KZT", _("KZT"), "₸"
17+
KIS = "KIS", _("KIS (for zero-price orders)"), "💋"
18+
19+
@classmethod
20+
def choices(cls) -> tuple[tuple[str, "_StrPromise"], ...]:
21+
currencies = [(member.name, member.value[1]) for member in cls if member if member.name.isupper()]
22+
23+
return tuple(currencies)
24+
25+
26+
def get_symbol(code: str) -> str:
27+
symbol = getattr(CurrencyCodes, code.upper())
28+
29+
if symbol is None:
30+
raise ValueError("Non-existant currency symbol")
31+
32+
return symbol.value[2]
1433

1534

1635
def get_rate(name: str) -> Decimal | None:

src/apps/banking/fixtures.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
from decimal import Decimal
2+
3+
import pytest
4+
5+
6+
@pytest.fixture
7+
def usd(factory):
8+
return factory.currency(name="USD", rate=Decimal(100))

src/apps/banking/models.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55

66

77
class Currency(TimestampedModel):
8-
name = models.CharField(max_length=4, unique=True, choices=CurrencyCodes.choices, db_index=True)
8+
name = models.CharField(max_length=4, unique=True, choices=CurrencyCodes.choices(), db_index=True)
99
rate = models.DecimalField(max_digits=6, decimal_places=2, verbose_name=_("Rate"))
1010

1111
class Meta:

0 commit comments

Comments
 (0)