feat(751-3): M01 · M02 셈 끝수 = 품셈 1-2-2 버림 — 줄 0.1 원 · 비목 소계 1 원 · 계 = 셋 합 · B09 와 같은 값

- 끝수 한 곳 `master_cut.py`(B09 `rounddown` 재사용) — 엔진 답에 입힘: 줄 비목마다 0.1 원 버림 · 하위 로직 줄 = 하위 끝수 계 × 수량 · 덧줄 = 버린 줄 합으로 다시 셈 · 비목 합 1 원 버림 · 중간 칸도 버린 값
- 쓰는 곳: M01 시험 계산(`check_master.calc`) · 읽는 식(`master_text.lines`) · 옛 조합 미리보기(`master_combo`) · M02 `run_line`(단가산출 · 일위대가 · 펼침) · 별도계상 뺀 기준 · 켜쌓기율 곱 뒤 · 일위대가 반영 · 반영합 · 비율 줄 · 사전조합 안 줄
- 일위대가 반영 TS 짝(`Labor_Calc.ts`) 같은 끝수 · 거울 헬퍼가 상대 경로 .ts 가져옴
- B09 = `run_line(raw=True)` 엔진 날 값(제 단가로 다시 버림 · % 덧줄 율 비 그대로) — 검증_sub3 B09 247 칸 전후 같음
- `master_text` 꼴 셋 = `common_util_money` 로 갈음(줄 금액 `fmt_line_money` · 식 안 금액 = 버린 0.1 원)
- 화면: 상세 줄 금액 · 시험 계산 줄 · 중간 돈 = `formatLineMoney` · 일위대가 반영 칸 0.1 원 · 상세 바닥 · 박스 · 인력재료경비 비목 소계 = 줄 합 1 원 버림
- 테스트1 네 보기 · 서고 목록 = 124,419(옛 124,420 / 124,419.85) · 재료 줄 11,694.6 + 23,425.6 + 1,404.8 = 소계 36,525
- 새 지킴 시험 test_751_3_money_cut
- 옛 날 금액(끝수 없음)을 대조하던 시험 24 파일 고침 — 까닭 = 금액 규칙이 줄 0.1 원 · 소계 1 원 버림으로 바뀜(고정 값 → 새 값 · 줄 합 = 계 → 줄 합 아래 비목마다 1 원 안 · 곱 비 = 끝수 폭 안) · 실무 xlsx 영월 엑셀 같음 22 → 23 · 이어 붙이는 TS 시험(704_3 · 717_2b)에 `rounddown` 같은 식

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-11-03 06:16:01 +09:00
co-authored by Claude Opus 5.5
parent d8af573f1c
commit f4be6782a5
40 changed files with 484 additions and 162 deletions
@@ -4,6 +4,7 @@
* 거울 시험 `resources/tester/test_m02_labor_calc_mirror.py`.
* 기준(결과단위 그대로)은 서버 calc 길이 셈 · 여기는 받은 기준에
* 수량(손이 칸보다 앞섬 · 칸 = 알약 키 값) · 반영 = 기준 × 수량 ÷ 배 · 자재 = 재료비만.
* 끝수(751-3 · 품셈 1-2-2) = B09 `rounddown` — 반영 · 자재 금액 비목마다 0.1 원 · 반영합 비목마다 1 원 버림.
* 별도계상 자재(222-1 · 빈 칸 = 기본 별도계상) = 반영은 보이되 합에 안 더함(짝 `apart`).
* 참조 줄(단가승격 · 사전조합 · 50-3)은 `lineId` = 「참조|층|주인|키」(짝 `M02_Labor_Ref.ref_id`) ·
* 고정 줄(705-1)은 「고정|이름|규격|단위」(짝 `M02_Labor_Fixed.fixed_id`) · 기준 = 서버가 낸 줄 단가.
@@ -11,8 +12,10 @@
import type { CalcLine as LogicLine } from "../M01_MasterData/M01_MasterData_UI_Logic_Api";
import type { LogicMat } from "./M02_Labor_LogicMats_Model";
import { rounddown } from "../B09_Estimation/B09_Estimation_Calc_Base";
export const MONEY_KEYS = ["노무비", "재료비", "경비", "계"] as const;
const COSTS = ["노무비", "재료비", "경비"] as const;
export type Money = Record<(typeof MONEY_KEYS)[number], number>;
export type QtySource = "손" | "칸" | "없음";
/** 문서의 수량 칸 — null = 면적산출 알약 · {손} = 손 입력 */
@@ -175,14 +178,22 @@ function pick(
return [null, "없음"];
}
/** 반영 = 기준 × 수량 ÷ 배 — 수량이 없으면 null */
/** 반영 = 기준 × 수량 ÷ 배 — 비목마다 0.1 원 버림 · 계 = 셋 합 · 수량이 없으면 null */
export function reflect(base: Money, qty: number | null, times: number): Money | null {
if (qty === null) return null;
const out = zero();
for (const k of MONEY_KEYS) out[k] = (base[k] * qty) / times;
for (const k of COSTS) out[k] = rounddown((base[k] * qty) / times, 1);
out.계 = out.노무비 + out.재료비 + out.경비;
return out;
}
/** 반영합 끝수 — 비목마다 1 원 버림 · 계 = 셋 합(짝 `master_cut.sums`) */
function cutSums(sum: Money): Money {
for (const k of COSTS) sum[k] = rounddown(sum[k], 0);
sum.계 = sum.노무비 + sum.재료비 + sum.경비;
return sum;
}
/** 기준이 선 계산 답 + 문서 손 값 + 알약 값(`{키: 값}`) → 수량 · 반영 · 합을 새로 채운 답 ·
* `col` = 규격 열(없으면 기본 · 704-4) */
export function settle(
@@ -218,12 +229,13 @@ export function settle(
const 자재 = result.자재.map((one) => {
const [qty, source] = pick(mhands.get(one.코드), one.알약, have);
const price = num(one.기준단가);
const cost = price !== null && qty !== null ? price * qty : null;
const cost = price !== null && qty !== null ? rounddown(price * qty, 1) : null;
if (price !== null && !aside.has(one.코드)) add(base, { ...zero(), 재료비: price, 계: price });
if (cost !== null && !aside.has(one.코드)) add(done, { ...zero(), 재료비: cost, 계: cost });
return { ...one, 수량: qty, 수량출처: source, 반영: cost };
});
return { 줄: ratioLines(줄, done, base), 자재, 기준합: base, 반영합: done };
const lines = ratioLines(줄, done, base);
return { 줄: lines, 자재, 기준합: base, 반영합: cutSums(done) };
}
/** 비율 고정 줄(705-3 · 짝 `M02_Labor_Fixed.ratios`) — 밑 = 비율 줄 뺀 반영합 · 기준[비목] = 밑[기준] ·