Skip to content

Commit f96289b

Browse files
committed
fix: 상품 개수 제한에 따라 옵션 개수 제한이 영향받는 오류 수정
1 parent f0526a1 commit f96289b

2 files changed

Lines changed: 21 additions & 6 deletions

File tree

app/shop/product/serializers/dto.py

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,20 @@
77
from rest_framework import serializers
88
from shop.product.models import Option, OptionGroup, Product
99

10+
# product 인당 한도(product_max_quantity_per_user)는 "이 유저가 이 상품(티켓)을 몇 개 살 수 있나" 축이라,
11+
# 옵션/그룹 인스턴스 개수(max_quantity_per_product 축, 1 OPR 아래 다수 OPOR)를 제한하지 않는다.
12+
# breakdown 에는 참고용으로 남기되 leftover_stock_per_user(= binding min) 계산에서는 제외한다.
13+
_LEFTOVER_PER_USER_EXCLUDED_KEYS = frozenset({"product_max_quantity_per_user"})
14+
1015

1116
def _min_ignoring_none(values: Iterable[int | None]) -> int | None:
1217
return min(finite) if (finite := [v for v in values if v is not None]) else None
1318

1419

20+
def _leftover_stock_per_user(info: dict[str, int | None]) -> int | None:
21+
return _min_ignoring_none(v for k, v in info.items() if k not in _LEFTOVER_PER_USER_EXCLUDED_KEYS)
22+
23+
1524
@dataclass(frozen=True)
1625
class StockContext:
1726
"""Product 페이지에 등장하는 모든 product / option / group 의 점유 수량을 단일 stage 에서 prefetch 한 결과"""
@@ -115,7 +124,7 @@ def get_leftover_stock_info(self, option: Option) -> dict[str, int | None]:
115124
return self.context.get("stock_context", _EMPTY_STOCK_CONTEXT).option_leftover_info(option)
116125

117126
def get_leftover_stock_per_user(self, option: Option) -> int | None:
118-
return _min_ignoring_none(self.get_leftover_stock_info(option).values())
127+
return _leftover_stock_per_user(self.get_leftover_stock_info(option))
119128

120129

121130
class OptionGroupDto(serializers.ModelSerializer):
@@ -148,7 +157,7 @@ def get_leftover_stock_info(self, group: OptionGroup) -> dict[str, int | None]:
148157
return self.context.get("stock_context", _EMPTY_STOCK_CONTEXT).option_group_leftover_info(group)
149158

150159
def get_leftover_stock_per_user(self, group: OptionGroup) -> int | None:
151-
return _min_ignoring_none(self.get_leftover_stock_info(group).values())
160+
return _leftover_stock_per_user(self.get_leftover_stock_info(group))
152161

153162

154163
class ProductDto(serializers.ModelSerializer):

app/shop/product/test/leftover_stock_per_user_test.py

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -88,10 +88,11 @@ def test_breakdown_subtracts_cart_and_purchased_for_logged_in_user(
8888

8989

9090
@pytest.mark.django_db
91-
def test_product_level_limit_can_be_binding_constraint(
91+
def test_product_level_limit_does_not_cap_option_leftover(
9292
customer_client, customer_user, ticket_product, group_with_options
9393
):
94-
# Product.max_quantity_per_user 가 group / option 한도 보다 작아서 binding 이 되는 경우 — 명시적 케이스.
94+
# Product.max_quantity_per_user 는 "티켓 몇 장" 축이라 옵션/그룹 인스턴스 개수(max_quantity_per_product 축)를
95+
# 제한하지 않는다. breakdown 엔 잔여값을 남기되 leftover_stock_per_user 엔 반영되지 않아야 한다.
9596
ticket_product.max_quantity_per_user = 2
9697
ticket_product.save()
9798
group, a, _b = group_with_options
@@ -101,11 +102,16 @@ def test_product_level_limit_can_be_binding_constraint(
101102
a.save()
102103

103104
data = _serialize_via_api(customer_client, ticket_product)
104-
option_a = next(o for o in data["option_groups"][0]["options"] if o["id"] == str(a.id))
105+
grp = data["option_groups"][0]
106+
# 그룹 레벨: product 한도(2)가 breakdown 엔 남지만 leftover 는 group 한도(10)가 결정.
107+
assert grp["leftover_stock_info"]["product_max_quantity_per_user"] == 2
108+
assert grp["leftover_stock_per_user"] == 10
109+
110+
option_a = next(o for o in grp["options"] if o["id"] == str(a.id))
105111
assert option_a["leftover_stock_info"]["product_max_quantity_per_user"] == 2
106112
assert option_a["leftover_stock_info"]["option_group_max_quantity_per_user"] == 10
107113
assert option_a["leftover_stock_info"]["option_max_quantity_per_user"] == 10
108-
assert option_a["leftover_stock_per_user"] == 2 # ticket_product 한도가 binding
114+
assert option_a["leftover_stock_per_user"] == 10 # product 한도(2)는 binding 아님
109115

110116

111117
@pytest.mark.django_db

0 commit comments

Comments
 (0)