feat(B09): 기계 수송비를 세움 — 기계경비의 셋째 몫이 통째로 빠져 있었음
기계경비 = 기계손료 + 운전경비 + 수송비인데 수송비가 없었음. 수송비를 내는 공종 FP-10-04 중기운반은 표가 미판정이라 단가가 아예 안 섰음. - 산림품셈 10-4 사이클 그대로 — 트레일러(20TON) t1=20·t3=20·t4=0.42 / 트럭(10.5TON) t1=10·t3=10·t4=5 · ㎝=t1+t2+t3+t4 · N=60×0.9/㎝, 단위는 회당. - 거리·도로 구분은 설계 입력 — 비면 줄이 안 서고 사유만 남음(임의 거리 금지). - 원문이 「-」로 둔 칸(트레일러·고속4차선)은 지어내지 않고 사유로 냄. - ⚠ 우리가 정한 둘을 화면 근거에 적음 — 속도를 8-1-6의 2 나 이동속도표에서 가져온 것, 운반시간을 왕복으로 본 것. - 곁다리: 운전경비 표가 페이지에서 잘려 앞자리를 못 이어받아 39 기종이 통째로 버려지고 있었음(그 안에 수송 차량 2702 가 있었음). 이어받되 카탈로그에 있는 코드일 때만 받게 함 — 운전경비 줄 92 → 158. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -249,11 +249,14 @@ async def _build_for(project_id: UUID):
|
||||
)
|
||||
# 공구손료·잡재료 — **비어 있는 것이 기본**이라 안 넣으면 줄이 안 선다(확정 5차 작은 것 1).
|
||||
# 유가 지역 — 안 고르면 전국평균(품셈 8-1-7 5호 「해당지역의 가격」).
|
||||
# 기계 수송비 — 거리·도로 구분이 있어야 선다(산림품셈 10-4 [주]).
|
||||
return cached_build(
|
||||
ranges,
|
||||
machines,
|
||||
str(settings.get("misc_material_percent") or ""),
|
||||
str(settings.get("fuel_region") or ""),
|
||||
str(settings.get("transport_distance_km") or ""),
|
||||
str(settings.get("transport_road") or ""),
|
||||
)
|
||||
|
||||
|
||||
@@ -389,9 +392,22 @@ async def get_factor_choices(project_id: UUID) -> JSONResponse:
|
||||
MISC_MATERIAL_MIN_PERCENT,
|
||||
)
|
||||
|
||||
from B09_Estimation.B09_Estimation_Transport import ASSUMPTION_TEXT as TRANSPORT_ASSUMPTION
|
||||
from B09_Estimation.B09_Estimation_Transport import BASIS_TEXT as TRANSPORT_BASIS
|
||||
from B09_Estimation.B09_Estimation_Transport import ROAD_CLASSES as TRANSPORT_ROADS
|
||||
from B09_Estimation.B09_Estimation_Transport import TRANSPORT_VARIANTS
|
||||
from B09_Estimation.B09_Estimation_Transport import WORK_ITEM_CODE as TRANSPORT_CODE
|
||||
|
||||
# 「넣을 데가 있는가」 — 주재료비가 선 일위대가가 몇인지 세어 그대로 알린다.
|
||||
# 지금은 사급 자재 단가가 미결(확정 5차 큰 것 8)이라 0 이 정상이다.
|
||||
book = (await _build_for(project_id)).book
|
||||
prices = await _build_for(project_id)
|
||||
book = prices.book
|
||||
transport_notes = list(prices.transport_notes)
|
||||
transport_prices = {}
|
||||
for variant in TRANSPORT_VARIANTS:
|
||||
code = f"B-{TRANSPORT_CODE}#{variant['key']}"
|
||||
if code in book.titles:
|
||||
transport_prices[variant["key"]] = f"{book.resolve(code).total:,.0f}"
|
||||
with_material = sum(
|
||||
1
|
||||
for unit_code, unit_title in book.titles.items()
|
||||
@@ -423,6 +439,23 @@ async def get_factor_choices(project_id: UUID) -> JSONResponse:
|
||||
" 없으므로, 사급 자재 단가가 서는 날 이 칸이 함께 살아납니다."
|
||||
),
|
||||
},
|
||||
"transport": {
|
||||
"distance_km": str(settings.get("transport_distance_km") or ""),
|
||||
"road": str(settings.get("transport_road") or ""),
|
||||
"roads": [
|
||||
{"key": row["key"], "label": row["label"]} for row in TRANSPORT_ROADS
|
||||
],
|
||||
"variants": [
|
||||
{
|
||||
"key": variant["key"],
|
||||
"label": variant["label"],
|
||||
"unit_price_krw": transport_prices.get(variant["key"], ""),
|
||||
}
|
||||
for variant in TRANSPORT_VARIANTS
|
||||
],
|
||||
"basis": [TRANSPORT_BASIS, TRANSPORT_ASSUMPTION],
|
||||
"notes": transport_notes,
|
||||
},
|
||||
"notes": [
|
||||
"고를 수 있는 것은 원문에 적힌 값뿐입니다 — 그 밖의 수는 만들지 않습니다.",
|
||||
"바꾸면 그 공종 단가가 바로 달라집니다. [저장]한 값은 이 프로젝트에만 걸립니다.",
|
||||
@@ -446,6 +479,9 @@ class FactorChoiceBody(BaseModel):
|
||||
misc_material_percent: str | None = None
|
||||
#: 유가 지역(시도코드) — **빈 문자열이면 전국평균**으로 돌아간다.
|
||||
fuel_region: str | None = None
|
||||
#: 기계 수송 거리(㎞)·도로 구분 — **비면 수송비 줄이 안 선다.**
|
||||
transport_distance_km: str | None = None
|
||||
transport_road: str | None = None
|
||||
|
||||
|
||||
@router.put("/{project_id}/estimation/factors")
|
||||
@@ -499,6 +535,25 @@ async def put_factor_choices(project_id: UUID, body: FactorChoiceBody) -> JSONRe
|
||||
},
|
||||
)
|
||||
values["fuel_region"] = region
|
||||
if body.transport_distance_km is not None:
|
||||
from B09_Estimation.B09_Estimation_Transport import parse_distance_km
|
||||
|
||||
try:
|
||||
distance = parse_distance_km(body.transport_distance_km)
|
||||
except ValueError as exc:
|
||||
return JSONResponse(status_code=400, content={"status": "error", "message": str(exc)})
|
||||
values["transport_distance_km"] = "" if distance is None else str(distance)
|
||||
if body.transport_road is not None:
|
||||
from B09_Estimation.B09_Estimation_Transport import road_class
|
||||
|
||||
road = str(body.transport_road).strip()
|
||||
if road and road_class(road) is None:
|
||||
# ⚠ 원문 표에 없는 도로 구분을 받아 두면 속도를 못 골라 조용히 안 선다.
|
||||
return JSONResponse(
|
||||
status_code=400,
|
||||
content={"status": "error", "message": f"원문에 없는 도로 구분입니다: {road}"},
|
||||
)
|
||||
values["transport_road"] = road
|
||||
try:
|
||||
save_section(root, "estimation", values, replace_keys=tuple(values))
|
||||
return JSONResponse(content={"status": "success", **values})
|
||||
|
||||
Reference in New Issue
Block a user