Skip to content

Commit bee47b7

Browse files
Enhance BMS functionality and add new images
- Added support for development requirements in `uv.lock`. - Introduced new images: `example_square.jpg` and `example_wide.jpg`. - Refactored BMS models to use `list` instead of `List` for type hints. - Improved validation logic in `BmsCommerce` for discount handling. - Updated tests to ensure proper assertions and added image checks for BMS wide item list.
1 parent 726e163 commit bee47b7

File tree

7 files changed

+118
-138
lines changed

7 files changed

+118
-138
lines changed

examples/images/example_square.jpg

17.6 KB
Loading

examples/images/example_wide.jpg

25.4 KB
Loading

solapi/model/kakao/bms/bms_carousel.py

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,12 @@
1-
from typing import List, Optional, Union
1+
from typing import Optional
22

33
from pydantic import BaseModel, ConfigDict, Field
44
from pydantic.alias_generators import to_camel
55

6-
from solapi.model.kakao.bms.bms_button import BmsAppButton, BmsWebButton
6+
from solapi.model.kakao.bms.bms_button import BmsLinkButton
77
from solapi.model.kakao.bms.bms_commerce import BmsCommerce
88
from solapi.model.kakao.bms.bms_coupon import BmsCoupon
99

10-
BmsLinkButton = Union[BmsWebButton, BmsAppButton]
11-
1210

1311
class BmsCarouselHead(BaseModel):
1412
header: Optional[str] = None
@@ -36,14 +34,14 @@ class BmsCarouselFeedItem(BaseModel):
3634
content: Optional[str] = None
3735
image_id: Optional[str] = None
3836
image_link: Optional[str] = None
39-
buttons: Optional[List[BmsLinkButton]] = None
37+
buttons: Optional[list[BmsLinkButton]] = None
4038
coupon: Optional[BmsCoupon] = None
4139

4240
model_config = ConfigDict(alias_generator=to_camel, populate_by_name=True)
4341

4442

4543
class BmsCarouselFeedSchema(BaseModel):
46-
items: Optional[List[BmsCarouselFeedItem]] = Field(default=None, alias="list")
44+
items: Optional[list[BmsCarouselFeedItem]] = Field(default=None, alias="list")
4745
tail: Optional[BmsCarouselTail] = None
4846

4947
model_config = ConfigDict(populate_by_name=True)
@@ -53,7 +51,7 @@ class BmsCarouselCommerceItem(BaseModel):
5351
commerce: Optional[BmsCommerce] = None
5452
image_id: Optional[str] = None
5553
image_link: Optional[str] = None
56-
buttons: Optional[List[BmsLinkButton]] = None
54+
buttons: Optional[list[BmsLinkButton]] = None
5755
additional_content: Optional[str] = None
5856
coupon: Optional[BmsCoupon] = None
5957

@@ -62,7 +60,7 @@ class BmsCarouselCommerceItem(BaseModel):
6260

6361
class BmsCarouselCommerceSchema(BaseModel):
6462
head: Optional[BmsCarouselHead] = None
65-
items: Optional[List[BmsCarouselCommerceItem]] = Field(default=None, alias="list")
63+
items: Optional[list[BmsCarouselCommerceItem]] = Field(default=None, alias="list")
6664
tail: Optional[BmsCarouselTail] = None
6765

6866
model_config = ConfigDict(populate_by_name=True)

solapi/model/kakao/bms/bms_commerce.py

Lines changed: 16 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -40,34 +40,28 @@ def validate_price_combination(self) -> "BmsCommerce":
4040
has_discount_rate = self.discount_rate is not None
4141
has_discount_fixed = self.discount_fixed is not None
4242

43-
if not has_discount_price and not has_discount_rate and not has_discount_fixed:
44-
return self
45-
46-
if has_discount_price and has_discount_rate and not has_discount_fixed:
47-
return self
48-
49-
if has_discount_price and has_discount_fixed and not has_discount_rate:
43+
# 할인 정보 없음 = 유효
44+
if not any([has_discount_price, has_discount_rate, has_discount_fixed]):
5045
return self
5146

47+
# discountRate와 discountFixed는 상호 배타적
5248
if has_discount_rate and has_discount_fixed:
5349
raise ValueError(
5450
"discountRate와 discountFixed는 동시에 사용할 수 없습니다. "
5551
"할인율(discountRate) 또는 정액할인(discountFixed) 중 하나만 선택하세요."
5652
)
5753

58-
if not has_discount_price and (has_discount_rate or has_discount_fixed):
59-
raise ValueError(
60-
"discountRate 또는 discountFixed를 사용하려면 "
61-
"discountPrice(할인가)도 함께 지정해야 합니다."
62-
)
63-
64-
if has_discount_price and not has_discount_rate and not has_discount_fixed:
65-
raise ValueError(
66-
"discountPrice를 사용하려면 discountRate(할인율) 또는 "
67-
"discountFixed(정액할인) 중 하나를 함께 지정해야 합니다."
68-
)
54+
# 할인 정보는 완전한 세트여야 함 (discountPrice + discountRate/discountFixed)
55+
if has_discount_price != (has_discount_rate or has_discount_fixed):
56+
if has_discount_price:
57+
raise ValueError(
58+
"discountPrice를 사용하려면 discountRate(할인율) 또는 "
59+
"discountFixed(정액할인) 중 하나를 함께 지정해야 합니다."
60+
)
61+
else:
62+
raise ValueError(
63+
"discountRate 또는 discountFixed를 사용하려면 "
64+
"discountPrice(할인가)도 함께 지정해야 합니다."
65+
)
6966

70-
raise ValueError(
71-
"알 수 없는 가격 조합입니다. regularPrice만 사용하거나, "
72-
"regularPrice + discountPrice + discountRate/discountFixed 조합을 사용하세요."
73-
)
67+
return self

solapi/model/request/kakao/bms.py

Lines changed: 6 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -10,38 +10,15 @@
1010
)
1111
from solapi.model.kakao.bms.bms_commerce import BmsCommerce
1212
from solapi.model.kakao.bms.bms_coupon import BmsCoupon
13+
from solapi.model.kakao.bms.bms_option import (
14+
BMS_REQUIRED_FIELDS,
15+
WIDE_ITEM_LIST_MIN_SUB_ITEMS,
16+
BmsChatBubbleType,
17+
_to_camel,
18+
)
1319
from solapi.model.kakao.bms.bms_video import BmsVideo
1420
from solapi.model.kakao.bms.bms_wide_item import BmsMainWideItem, BmsSubWideItem
1521

16-
BmsChatBubbleType = Literal[
17-
"TEXT",
18-
"IMAGE",
19-
"WIDE",
20-
"WIDE_ITEM_LIST",
21-
"COMMERCE",
22-
"CAROUSEL_FEED",
23-
"CAROUSEL_COMMERCE",
24-
"PREMIUM_VIDEO",
25-
]
26-
27-
BMS_REQUIRED_FIELDS: dict[BmsChatBubbleType, list[str]] = {
28-
"TEXT": [],
29-
"IMAGE": ["image_id"],
30-
"WIDE": ["image_id"],
31-
"WIDE_ITEM_LIST": ["header", "main_wide_item", "sub_wide_item_list"],
32-
"COMMERCE": ["image_id", "commerce", "buttons"],
33-
"CAROUSEL_FEED": ["carousel"],
34-
"CAROUSEL_COMMERCE": ["carousel"],
35-
"PREMIUM_VIDEO": ["video"],
36-
}
37-
38-
WIDE_ITEM_LIST_MIN_SUB_ITEMS = 3
39-
40-
41-
def _to_camel(s: str) -> str:
42-
components = s.split("_")
43-
return components[0] + "".join(x.title() for x in components[1:])
44-
4522

4623
class Bms(BaseModel):
4724
targeting: Optional[Literal["I", "M", "N"]] = None

0 commit comments

Comments
 (0)