Merge remote-tracking branch 'origin/sub_desktop_1' into main_laptop_1
This commit is contained in:
@@ -66,9 +66,10 @@ FIELD_HINTS: dict[str, str] = {
|
||||
),
|
||||
"overhead_class": "임도가 (주)공사·전문공사 어느 쪽인지 정한 규정 없음 — 기본 (주)공사",
|
||||
"cut_basis": (
|
||||
"기본 「안 함」 — 이윤을 자동으로 깎지 않는다는 합의(2026-09-08) · 참고: 실무 임도 3건"
|
||||
"(봉화·영월·거창) 모두 「총공사비 1,000원 미만 + 이윤 자동보정」을 씀 — 고르면 이윤 줄에"
|
||||
" 「절사 자동보정」으로 적힘"
|
||||
"기본 「총공사비 1,000원 미만 버림」 — 근거: 산림청고시 제2025-82호 「금액의 단위표준」"
|
||||
"(설계서의 총액 · 원 · 1,000 · 미만버림) · 실무 6건이 여섯 다 000 으로 끝남 ·"
|
||||
" 깎은 몫은 이윤 줄에 「절사 자동보정(산림청고시 2025-82호)」으로 적혀 조용히 안 깎임 ·"
|
||||
" 발주처가 달리 정하면 「절사 안 함」으로 끄고 이윤보정액을 직접 넣을 것"
|
||||
),
|
||||
"vat_mode": "실무 임도는 (0) 공급가액의 10% · 산림조합 형식은 계류보전(영덕) 1건",
|
||||
"private_material_position": (
|
||||
@@ -118,7 +119,12 @@ VAT_MODE_LABELS = {
|
||||
|
||||
|
||||
def choice_options() -> dict[str, list[dict[str, str]]]:
|
||||
"""10·11·12 선택지. 절사 「안 함」이 첫째 = 기본(이윤을 자동으로 안 깎음)."""
|
||||
"""10·11·12 선택지.
|
||||
|
||||
⚠ 절사 「안 함」 값은 **`none`** 임 — 빈 값이면 저장되지 않아(`clean_settings`)
|
||||
기본으로 되돌아가 **끌 수가 없다**.
|
||||
기본은 「총공사비에서 조정 · 1,000원 미만」(산림청고시 2025-82호).
|
||||
"""
|
||||
return {
|
||||
"employment_insurance_grade": _GRADE_OPTIONS,
|
||||
"retirement_mutual_aid_mode": [
|
||||
@@ -154,8 +160,15 @@ def choice_options() -> dict[str, list[dict[str, str]]]:
|
||||
],
|
||||
"overhead_class": [{"value": k, "label": v} for k, v in OVERHEAD_CLASSES.items()],
|
||||
"cut_basis": [
|
||||
{"value": "", "label": "절사 안 함(이윤보정액 입력)"},
|
||||
*({"value": k, "label": f"{v}에서 조정"} for k, v in CUT_BASES.items()),
|
||||
*(
|
||||
{
|
||||
"value": k,
|
||||
"label": f"{v}에서 조정"
|
||||
+ (" (기본 · 고시 2025-82호)" if k == "grand_total" else ""),
|
||||
}
|
||||
for k, v in CUT_BASES.items()
|
||||
),
|
||||
{"value": "none", "label": "절사 안 함(이윤보정액 직접 입력)"},
|
||||
],
|
||||
"cut_unit_krw": [{"value": str(u), "label": f"{u:,}원 미만"} for u in CUT_UNITS_KRW],
|
||||
"vat_mode": [{"value": k, "label": VAT_MODE_LABELS[k]} for k in VAT_MODES],
|
||||
|
||||
@@ -123,10 +123,15 @@ class CostInput:
|
||||
#: 12. 부가세 방식(`VAT_MODES`) · 산림조합-면세품이면 면세품 금액.
|
||||
vat_mode: str = "supply"
|
||||
tax_exempt_material_krw: Decimal = _ZERO
|
||||
#: 11. 절사 — `grand_total`(총공사비에서 조정) · `supply`(공급가액) · 빈 값은 안 자름.
|
||||
#: ⚠ 고른 때만 이윤을 자동보정한다 — 기본은 수동 이윤보정만(2026-09-08 합의).
|
||||
cut_basis: str = ""
|
||||
cut_unit_krw: int = 0
|
||||
#: 11. 절사 — `grand_total`(총공사비에서 조정) · `supply`(공급가액) · `none`·빈 값은 안 자름.
|
||||
#: ⭐ 2026-09-14 **기본이 켜짐** — 근거 산림청고시 제2025-82호 「금액의 단위표준」
|
||||
#: 「설계서의 총액 · 원 · 1,000 · 미만버림」.
|
||||
#: 실무 6건이 여섯 다 `000` 으로 끝나는 것이 증거.
|
||||
#: ⚠ 2026-09-08 「조용히 깎지 않음」 합의의 정신은 살린다 — 깎은 몫과
|
||||
#: **까닭(고시 번호)** 을 이윤 조정액 줄과 결과 메모에 적고,
|
||||
#: 설계자가 `none` 으로 **끌 수 있게** 둔다.
|
||||
cut_basis: str = "grand_total"
|
||||
cut_unit_krw: int = 1000
|
||||
#: 4. 고용보험 등급 — `auto`(추정금액 구간) · `none`(없음) · `1`~`7`(등급 직접).
|
||||
employment_insurance_grade: str = "auto"
|
||||
#: 5. 퇴직공제부금비 — `auto`(추정금액 1억 이상) · `apply`(적용) · `none`(미적용).
|
||||
@@ -360,8 +365,11 @@ def calculate_cost(data: CostInput) -> CostResult:
|
||||
)
|
||||
|
||||
|
||||
#: 절사 뒤 잔차 맞춤 반복 상한 — 실무 수식도 잔차 칸 한 번뿐(표본 넷 모두 0).
|
||||
#: 절사 뒤 잔차 맞춤 반복 상한. 실무 6건은 **두 걸음 안에** 앉음
|
||||
#: (다섯 건 한 걸음 · 울진 신설 두 걸음).
|
||||
_CUT_MAX_PASSES = 3
|
||||
#: 11. 절사 끔 — 고르개가 보내는 값. 빈 값도 같이 받는다(저장 안 된 옛 프로젝트).
|
||||
CUT_OFF = ("", "none")
|
||||
|
||||
|
||||
def _calculate_with_scale(
|
||||
@@ -370,32 +378,47 @@ def _calculate_with_scale(
|
||||
scale: Decimal,
|
||||
notes: list[str],
|
||||
) -> CostResult:
|
||||
"""규모 기준액을 못 박고 계산 — 11. 절사를 **고른 때만** 이윤을 보정해 다시 셈."""
|
||||
"""규모 기준액을 못 박고 계산 — 11. 절사가 켜져 있으면 이윤을 보정해 다시 셈.
|
||||
|
||||
⚠ **걸음마다 ÷1.1 을 해야 한다**(2026-09-14 고침). 이윤을 1원 깎으면 부가세가 따라 줄어
|
||||
총공사비는 **1.1원** 줄므로, 잔차를 그대로 빼면 경계를 **지나쳐** 진동한다 —
|
||||
울진 신설 실측 `654 → 999 → 900 → 910 → 909 …` 로 안 앉았다. 늘 ÷1.1 하면 `654 → 999 → 0`.
|
||||
"""
|
||||
result = _calculate_once(data, dataset, scale, notes)
|
||||
if not data.cut_basis or data.cut_unit_krw <= 0:
|
||||
if data.cut_basis in CUT_OFF or data.cut_unit_krw <= 0:
|
||||
return result
|
||||
key = "grand_total" if data.cut_basis == "grand_total" else "total_cost"
|
||||
automatic = _ZERO
|
||||
gap = _ZERO
|
||||
for _ in range(_CUT_MAX_PASSES):
|
||||
gap = cut_gap(result.totals[key], data.cut_unit_krw)
|
||||
if gap == 0:
|
||||
break
|
||||
# 첫 차례는 실무 식(총공사비·공급가액 비례 부가세면 ÷1.1), 그 뒤는 잔차 그대로.
|
||||
automatic += profit_cut(gap, data.cut_basis, data.vat_mode) if automatic == 0 else gap
|
||||
# 실무 식 — 총공사비 절사 + 부가세가 공급가액 비례면 ÷1.1(잔차 걸음도 같음).
|
||||
automatic += profit_cut(gap, data.cut_basis, data.vat_mode)
|
||||
adjusted = replace(
|
||||
data,
|
||||
profit_adjustment_krw=data.profit_adjustment_krw + automatic,
|
||||
cut_basis="",
|
||||
cut_basis="none",
|
||||
profit_adjustment_label=(
|
||||
f"{CUT_BASES.get(data.cut_basis, data.cut_basis)} {data.cut_unit_krw:,}원 미만"
|
||||
" 절사 자동보정" + (" + 설계자 입력" if data.profit_adjustment_krw else "")
|
||||
" 절사 자동보정(산림청고시 2025-82호)"
|
||||
+ (" + 설계자 입력" if data.profit_adjustment_krw else "")
|
||||
),
|
||||
)
|
||||
result = _calculate_once(adjusted, dataset, scale, notes)
|
||||
result.notes.append(
|
||||
f"{CUT_BASES.get(data.cut_basis, data.cut_basis)} {data.cut_unit_krw:,}원 미만 절사 —"
|
||||
f" 이윤에서 {automatic:,}원 자동보정(설계자가 고른 절사 기준)"
|
||||
f" 이윤에서 {automatic:,}원 자동보정."
|
||||
" 근거: 산림청고시 제2025-82호 「금액의 단위표준」(설계서의 총액 1,000원 미만버림) ·"
|
||||
" 기준 입력판 11 에서 끌 수 있음"
|
||||
)
|
||||
if gap:
|
||||
# 조용히 남기지 않는다 — 안 앉았으면 끝자리가 남았다는 것을 화면에 보인다.
|
||||
result.notes.append(
|
||||
f"⚠ 절사가 {_CUT_MAX_PASSES}걸음 안에 안 앉음 —"
|
||||
f" 끝자리 {gap:,}원이 남음(설계자 확인 필요)"
|
||||
)
|
||||
return result
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user