fix: 수동 로트 배분 시 remaining_qty 처리 수정

문제:
- 수동 로트 배분 후 remaining_qty를 감소시키지 않아 재고 부족 오류 발생
- 재고 부족 체크를 수동 배분에서 제외했던 임시 처리

해결:
- 수동 로트 배분 시에도 remaining_qty 감소 처리 추가
- 재고 부족 체크를 수동/자동 모두에 적용하도록 복원
- 이제 수동 배분도 정확한 재고 검증 수행

검증 테스트 추가:
- 배분 합계 불일치 시 오류
- 로트 재고 부족 시 오류
- 존재하지 않는 로트 사용 시 오류

이제 수동 로트 배분도 자동 선택과 동일한 수준의 재고 검증을 수행합니다.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
시골약사 2026-02-17 02:18:34 +00:00
parent 0f40cdfba7
commit 7d2b458e31
2 changed files with 113 additions and 2 deletions

7
app.py
View File

@ -1366,6 +1366,9 @@ def create_compound():
VALUES ('CONSUME', ?, ?, ?, ?, 'compounds', ?)
""", (herb_item_id, lot_id, -requested_qty, unit_price, compound_id))
# remaining_qty 감소 (중요!)
remaining_qty -= requested_qty
# 자동 로트 선택 (기존 로직)
else:
# 원산지가 지정된 경우 해당 원산지만, 아니면 전체에서 FIFO
@ -1425,8 +1428,8 @@ def create_compound():
remaining_qty -= used
# 수동 배분이 아닌 경우에만 재고 부족 체크
if remaining_qty > 0 and not ('lot_assignments' in ingredient and ingredient['lot_assignments']):
# 재고 부족 체크 (수동/자동 모두 적용)
if remaining_qty > 0:
raise Exception(f"재고 부족: {ingredient.get('herb_name', herb_item_id)}")
# 총 원가 업데이트

108
test_lot_validation.py Normal file
View File

@ -0,0 +1,108 @@
#!/usr/bin/env python3
"""
로트 배분 검증 테스트 - 재고 부족 잘못된 배분 테스트
"""
import json
import requests
BASE_URL = "http://localhost:5001"
def test_insufficient_stock():
print("=== 로트 배분 검증 테스트 ===\n")
# 1. 배분 합계가 맞지 않는 경우
print("1. 배분 합계가 필요량과 맞지 않는 경우")
compound_data = {
"patient_id": 1,
"formula_id": None,
"je_count": 1,
"cheop_total": 1,
"pouch_total": 1,
"ingredients": [
{
"herb_item_id": 63,
"grams_per_cheop": 100.0,
"total_grams": 100.0,
"origin": "manual",
"lot_assignments": [
{"lot_id": 208, "quantity": 50.0}, # 50g
{"lot_id": 219, "quantity": 30.0} # 30g = 총 80g (100g 필요)
]
}
]
}
response = requests.post(f"{BASE_URL}/api/compounds", json=compound_data, headers={"Content-Type": "application/json"})
if response.status_code != 200:
result = response.json()
print(f" ✅ 예상된 오류 발생: {result.get('error')}")
else:
print(f" ❌ 오류가 발생해야 하는데 성공함")
# 2. 로트 재고가 부족한 경우
print("\n2. 로트 재고가 부족한 경우")
compound_data = {
"patient_id": 1,
"formula_id": None,
"je_count": 1,
"cheop_total": 1,
"pouch_total": 1,
"ingredients": [
{
"herb_item_id": 63,
"grams_per_cheop": 5000.0, # 5000g 요청
"total_grams": 5000.0,
"origin": "manual",
"lot_assignments": [
{"lot_id": 208, "quantity": 5000.0} # 로트 208에 5000g 요청 (실제로는 4784g만 있음)
]
}
]
}
response = requests.post(f"{BASE_URL}/api/compounds", json=compound_data, headers={"Content-Type": "application/json"})
if response.status_code != 200:
result = response.json()
print(f" ✅ 예상된 오류 발생: {result.get('error')}")
else:
print(f" ❌ 오류가 발생해야 하는데 성공함")
# 3. 존재하지 않는 로트
print("\n3. 존재하지 않는 로트 ID 사용")
compound_data = {
"patient_id": 1,
"formula_id": None,
"je_count": 1,
"cheop_total": 1,
"pouch_total": 1,
"ingredients": [
{
"herb_item_id": 63,
"grams_per_cheop": 10.0,
"total_grams": 10.0,
"origin": "manual",
"lot_assignments": [
{"lot_id": 99999, "quantity": 10.0} # 존재하지 않는 로트
]
}
]
}
response = requests.post(f"{BASE_URL}/api/compounds", json=compound_data, headers={"Content-Type": "application/json"})
if response.status_code != 200:
result = response.json()
print(f" ✅ 예상된 오류 발생: {result.get('error')}")
else:
print(f" ❌ 오류가 발생해야 하는데 성공함")
print("\n✅ 모든 검증 테스트 완료 - 잘못된 요청을 올바르게 거부함")
if __name__ == "__main__":
test_insufficient_stock()