こんにちは、こんです🦊
100日チャレンジ Day 45 の記録です!
🧪 今日の挑戦テーマ
今日は「商品ごとに異なる条件で特典を自動配布する処理」を、
どんなパターンにも対応できるよう汎用化することに取り組みました。
たとえば:
地域A向けの商品には特典Aを
地域B向けの商品には特典Bを
一人の顧客が両方を注文したら、それぞれ別ルールで処理する
そんな業務に対応できるような処理構造を目指しました。
🤔 背景と課題感
業務でよくある「特典配布」や「在庫に基づくおまけ付け」のロジックは、意外と厄介です。
以下のような複雑さが絡みます:
商品の種類によって、渡すべき特典が異なる
特典ごとに在庫数も異なる
配布は「3個で1セット」+「余りはバラで配布」などの細かいルールがある
顧客が複数種類の商品を一括で注文するケースがある
こうしたケースでは、単純な一括処理では破綻してしまいます。
🔧 解決アプローチと工夫
✅ 特典の条件分離(地域やシリーズなど)
商品名に含まれるキーワードをもとに、
「この商品にはこの特典を配布すべき」といったルールを個別に分けて管理しました。
たとえば:
GIFT_RULES = {
"RegionA": ["GiftA1", "GiftA2", "GiftA3"],
"RegionB": ["GiftB1", "GiftB2", "GiftB3"],
# ...
}
「商品名にA地域を含む」→ 特典A群を対象
「商品名にB地域を含む」→ 特典B群を対象
といったように、商品ごとのルールを分離して処理。
✅ 在庫と整合性のとれた配布ロジック
セット配布:1セット=特典全種を1個ずつ配布
バラ配布:残りの余りは在庫のある順で1個ずつラウンドロビン
在庫が足りない場合は配布せず、ログに記録(在庫マイナス防止)
配布ロジックはすべて関数化して、任意の特典数にも対応できるように汎用設計にしました。
def calculate_distribution(total, gift_names):
sets = total // len(gift_names)
remainder = total % len(gift_names)
dist = {gift: sets for gift in gift_names}
for i in range(remainder):
dist[gift_names[i % len(gift_names)]] += 1
return dist
✅ 顧客が複数条件の商品を同時に注文した場合への対応
これまでは「顧客単位でまとめて1回処理」していたため、
カテゴリをまたいだ商品に対して特典が誤って配布されるリスクがありました。
そこで今回の改善では、顧客 × 条件ごとに個別に処理を実行し、
在庫も別々に管理することで、処理の混同を完全に防止しました。
for condition in GIFT_RULES:
filtered = df[df["商品名"].str.contains(condition)]
for customer in filtered["顧客ID"].unique():
# 購入数の集計・配布数の計算・在庫の割当を実施
📈 効果と成果
在庫がマイナスになるような配布ミスがゼロに!
特典の種類や配布ルールが増えても、ロジックは変更なしで対応可能
処理が明快になったことで、配布構成の可視化やCSV出力の信頼性も向上
🤯 詰まったポイントと学び
複数条件の商品を同時に処理すると、ラウンドロビンが崩れてバランスが偏る
「まとめて処理する」ではなく、「分けて考える・処理する」という設計が大切
汎用性を高めようとするあまり、かえってバグを生むリスクがあるので、シンプルなルール単位で切るのがコツ
📝 まとめと今後の展望
業務の自動化において、ロジックを分ける「粒度の正しさ」は本当に重要だと実感しました。
今回は、
条件別ルールの切り分け
セット+バラの安全な割当処理
配布結果の見える化とログ出力
を実装したことで、かなり再利用性の高い仕組みに進化できたと感じています。
次は、
昨日トライしてうまくいかなかった列名の動的指定
UIから特典ルールを定義できる仕組み
他業務との連携(ピッキング表・納品書出力など)
に挑戦していきたいと思います!
#業務改善 #Python #Streamlit #自動化 #在庫管理
#特典配布ロジック #条件分岐 #DX推進 #100日チャレンジ
#業務設計 #汎用化テンプレート #失敗から学ぶ