feat(B09): 유가 지역 단가를 물림 — 시도를 고르면 그 값으로 기계 연료비가 다시 섬

품셈 8-1-7 5호 「유류가격은 해당지역의 가격으로 한다」. 칸은 있었으나 시도별 값이
없어 잠겨 있었음. 오피넷 avgSidoPrice.do 스냅샷을 받아 데이터셋으로 세우고 물림.

- 안 고르면 전국평균 그대로 — 현장 소재지를 임의로 찍지 않음.
- 판에 없는 지역은 조용히 전국평균으로 눕지 않고 사유를 남김.
- 「지역 공시가」를 고를 수 있는지는 코드가 아니라 판이 정함.
- ⚠ 원천의 시도 가름이 행정구역과 다름(20=전남광주 한 줄, 07·16 없음) — 원문 그대로 둠.
- 수집 스크립트에 시도별 호출을 더함(전국평균과 두 벌로 보존).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-09-09 21:22:15 +09:00
co-authored by Claude Opus 5
parent 92ba0780cd
commit f39c195b52
12 changed files with 786 additions and 46 deletions
+31 -2
View File
@@ -248,7 +248,13 @@ async def _build_for(project_id: UUID):
sorted((str(k), str(v)) for k, v in (settings.get("machine_choices") or {}).items())
)
# 공구손료·잡재료 — **비어 있는 것이 기본**이라 안 넣으면 줄이 안 선다(확정 5차 작은 것 1).
return cached_build(ranges, machines, str(settings.get("misc_material_percent") or ""))
# 유가 지역 — 안 고르면 전국평균(품셈 8-1-7 5호 「해당지역의 가격」).
return cached_build(
ranges,
machines,
str(settings.get("misc_material_percent") or ""),
str(settings.get("fuel_region") or ""),
)
@router.get("/{project_id}/estimation/base-data")
@@ -284,13 +290,19 @@ async def get_price_sources(project_id: UUID) -> JSONResponse:
material_price_comparison,
)
from common_util.common_util_project_settings import estimation_settings
try:
build = await _build_for(project_id)
root = await _project_root_of(project_id)
settings = estimation_settings(root) if root else {}
return JSONResponse(
content={
"status": "success",
"material_comparison": material_price_comparison(build),
"base_reference": base_reference_data(build),
"base_reference": base_reference_data(
build, str(settings.get("fuel_region") or "") or None
),
}
)
except Exception:
@@ -432,6 +444,8 @@ class FactorChoiceBody(BaseModel):
machine_choices: dict[str, str] | None = None
#: 공구손료·잡재료 비율 — **빈 문자열이면 안 붙는다**(칸을 도로 비우는 길).
misc_material_percent: str | None = None
#: 유가 지역(시도코드) — **빈 문자열이면 전국평균**으로 돌아간다.
fuel_region: str | None = None
@router.put("/{project_id}/estimation/factors")
@@ -470,6 +484,21 @@ async def put_factor_choices(project_id: UUID, body: FactorChoiceBody) -> JSONRe
# 금액이 어긋난 채로 선다.
return JSONResponse(status_code=400, content={"status": "error", "message": str(exc)})
values["misc_material_percent"] = "" if percent is None else str(percent)
if body.fuel_region is not None:
from B09_Estimation.B09_Estimation_MachineOperating import load_regional_fuel_table
region = str(body.fuel_region).strip()
table, _ = load_regional_fuel_table()
if region and region not in table:
# ⚠ 판에 없는 지역을 받아 두면 조용히 전국평균으로 서고 사용자는 지역값인 줄 안다.
return JSONResponse(
status_code=400,
content={
"status": "error",
"message": f"유가 판에 없는 지역입니다: {region}",
},
)
values["fuel_region"] = region
try:
save_section(root, "estimation", values, replace_keys=tuple(values))
return JSONResponse(content={"status": "success", **values})