Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions src/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -396,6 +396,16 @@ def _bulk_approval_is_active() -> bool:
DEMAND_RISK_BUFFER_RATE = 0.20
SUPPLY_RISK_BUFFER_RATE = 0.20
MATERIAL_RISK_BUFFER_RATE = 0.10

# 발주 주기 — 보건기관은 재고 소진 시 수시 발주가 아니라 월 단위로 정기 발주한다.
# 확인: 2026-07-30 reo-23, 이슈 #54 (조달 실무 확인 결과)
# 그래서 재고 모형은 연속검토(ROP 도달 시 발주)가 아니라 정기검토를 쓴다.
# 보호기간 = 검토주기 + 리드타임 이며 src/modeling/inventory_policy.py 가 이를 따른다.
#
# ⚠️ backend 는 연속검토 식(SS = z·σ·√L, ROP = μ·L + SS)으로 운영 DB 를 채워 왔다.
# 두 모형이 공존하던 문제가 #54 이고, 실무 확인으로 정기검토가 정본임이 확정됐다.
# 운영 DB 재산정은 별도 PR 로 다룬다 — src/loading/apply_inventory_policy.py 는
# 그때까지 ss/rop/target/status/order_recommendation 을 쓰지 않는다.
DEFAULT_REVIEW_PERIOD_DAYS = 30
DEFAULT_LEAD_TIME_DAYS = 15

Expand Down
14 changes: 9 additions & 5 deletions src/loading/apply_inventory_policy.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,17 @@
"""Apply AI demand statistics to DB inventory without choosing an SS/ROP model.

Issue #54 records an unresolved policy conflict:
Issue #54 recorded a policy conflict between two models:

* backend: continuous review, ``SS=z*sigma*sqrt(L)``, ``ROP=mu*L+SS``
* AI: periodic review plus lead time, fixed/risk-adjusted target stock

Until the ordering cadence and one canonical policy are approved, this loader
updates only demand statistics and classification fields. It deliberately does
not write ``ss``, ``rop``, ``target``, ``status`` or ``order_recommendation``.
The cadence question is resolved (ai#54 request 1, 2026-07-30 confirmation from
reo-23): institutions place orders on a monthly cadence, so periodic review is
the canonical model. What is NOT yet done is applying that decision to the DB —
the SS/ROP recalculation itself is a separate, still-pending PR. Until that PR
lands, this loader updates only demand statistics and classification fields. It
deliberately does not write ``ss``, ``rop``, ``target``, ``status`` or
``order_recommendation``.

Run with ``DRY_RUN=1`` first. ``DRY_RUN=0`` commits the restricted update.
"""
Expand Down Expand Up @@ -121,7 +125,7 @@ def main() -> None:
)
print(
"보호 범위: SS/ROP/target/status/order_recommendation "
"(ai#54 결정 전 미변경)"
"(정기검토 모형 확정됨(ai#54 요청1) — DB 재계산은 별도 PR 대기, 이 로더는 미변경)"
)

with psycopg.connect(os.environ["DATABASE_URL"]) as conn:
Expand Down