From 3caa3e219333d50605c2b271ac40ac5e376bb5b6 Mon Sep 17 00:00:00 2001 From: umsangdon Date: Sun, 15 Nov 2026 16:10:24 +0900 Subject: [PATCH 1/3] =?UTF-8?q?fix(840-5):=20=EC=A1=B0=EC=A2=85=EC=9B=90?= =?UTF-8?q?=20=EB=85=B8=EC=9E=84=20=EC=8B=9C=EA=B0=84=20=ED=99=98=EC=82=B0?= =?UTF-8?q?=20=3D=201/8=20=C3=97=2016/12=20=C3=97=2025/20(=EC=9A=94?= =?UTF-8?q?=EA=B5=AC=20864=20=C2=B7=20=EA=B2=BD=EC=9F=81=EC=82=AC=EC=99=80?= =?UTF-8?q?=20=EA=B0=99=EA=B2=8C)=20=E2=80=94=20=EA=B8=B0=EA=B3=84=20?= =?UTF-8?q?=EC=8B=9D=20=EC=97=B4=20=EA=B0=9C=EA=B0=80=20=EA=B3=84=EC=88=98?= =?UTF-8?q?=ED=91=9C=20CC000336=20=EC=8B=9C=EA=B0=84=ED=99=98=EC=82=B0=20?= =?UTF-8?q?=C2=B7=20=EC=83=81=EC=97=AC=20=C2=B7=20=ED=9C=B4=EC=A7=80=20?= =?UTF-8?q?=EC=84=B8=20=EC=A4=84=EC=9D=84=20=EC=9D=BD=EC=9D=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 정본 로직(08장 아홉 · 산림 10장 4륜 덤프) 시간환산 중간 식 = 세 줄 분자 ÷ 분모 곱(713-4 전 꼴) · 수식 문서 = 빌더 변환으로 다시 지음(변환 대조 1,520 같음 · 채택 1 원 셋 = 같은 까닭 714-3) - 체인톱 GF000337 은 그대로(시간환산 = 하루 연료 · 손료 → 시간 · 조종원 없음) - 짝: 기계 양식 시간환산(M01 lists = 세 줄 곱) · master_machine_coef ↔ common_util_machine_coef 기본값 5/24 · 기계 덮음 새 조종원 줄 꼴(master_doc_cover ↔ common_util_formula_doc_cover) · 기호 이름 상여분자 · 상여분모 · 휴지분자 · 휴지분모 - 전후: 조종원 기계 295 대 노무비만 ×5/3 · 조종원 없는 기계 불변 · 로직 1,520 중 기계 부르는 431 만 오름 - B09 는 안 고침(계수표 세 줄을 스스로 곱함 · 두 번 곱 0) 낡은 시험 고침: 713-4 지킴을 세 계수 꼴로 · masterdata_check · 796-1 · 821-2 · 821-4 · 822-2 · 836-1 · 839-1 기대값 = 조종원 5/24 · 802-2 채택 차이 셋 · 821-3 줄 쌓기 내 기계 = 836-2 뒤 개인 층 자동 따라옴(옛 단가새값 · 단가갱신 기대 걷음) Co-Authored-By: Claude Opus 5.5 --- M01_MasterData/M01_MasterData_Machine.py | 13 ++-- common_util/common_util_formula_doc_cover.ts | 23 +++++- common_util/common_util_machine_coef.ts | 4 +- .../common_util_machine_symbol_name.ts | 4 ++ .../scripts/build_수식문서_802_2.py | 2 +- .../master_data/scripts/master_doc_cover.py | 13 +++- .../scripts/master_machine_coef.py | 5 +- .../로직_건설품셈_08장_건설기계.json | 68 +++++++++++++----- .../로직_산림품셈_10장_자재․장비_운반.json | 8 ++- .../수식문서/수식_건설품셈_08장_건설기계.json | 72 ++++++++++++++----- .../수식_산림품셈_10장_자재․장비_운반.json | 8 ++- resources/tester/test_713_4_source_match.py | 6 +- resources/tester/test_796_1_formula_model.py | 2 +- resources/tester/test_802_2_convert.py | 10 ++- resources/tester/test_821_2_machine_save.py | 4 +- .../tester/test_821_3_my_machine_screen.py | 12 ++-- .../tester/test_821_4_import_my_machine.py | 5 +- .../tester/test_822_2_machine_hourly_all.py | 11 +-- resources/tester/test_836_1_machine_cover.py | 4 +- .../tester/test_839_1_machine_symbol_name.py | 4 +- resources/tester/test_masterdata_check.py | 12 ++-- 21 files changed, 210 insertions(+), 80 deletions(-) diff --git a/M01_MasterData/M01_MasterData_Machine.py b/M01_MasterData/M01_MasterData_Machine.py index 4016d920b..e04fdd85b 100644 --- a/M01_MasterData/M01_MasterData_Machine.py +++ b/M01_MasterData/M01_MasterData_Machine.py @@ -23,6 +23,7 @@ from M01_MasterData import M01_MasterData_Store as store FILE = "기계.json" OWN_SOURCE = "자체" +HOUR_PARTS = ("시간환산", "상여계수", "휴지계수") # 조종원 노임 1/8 × 16/12 × 25/20(CC000336) _EQ = re.compile(r"EQ\d{6}") _cache: dict[str, Any] = {"ver": None, "hourly": {}, "uses": None} @@ -71,12 +72,14 @@ def lists() -> dict: if isinstance(r.get("값"), (int, float, Decimal)) ] head = store.read(FILE)[0] - hour = Decimal(1) / Decimal(8) + hour = Decimal(5) / Decimal(24) # 1/8 × 16/12 × 25/20(계수표 없을 때) for t in files.get("계수_건설품셈_08장_건설기계.json", {}).get("표") or []: - if t.get("키") == "CC000336": - got = next((x for x in t.get("줄") or [] if x.get("구분") == "시간환산"), None) - if got: - hour = Decimal(got["분자"]) / Decimal(got["분모"]) + if t.get("키") == "CC000336": # 기계 식 Q_시간환산 과 같은 세 줄(840-5) + got = {x.get("구분"): x for x in t.get("줄") or []} + if all(p in got for p in HOUR_PARTS): + hour = Decimal(1) + for p in HOUR_PARTS: + hour *= Decimal(got[p]["분자"]) / Decimal(got[p]["분모"]) return { "fuels": fuels, "jobs": jobs, diff --git a/common_util/common_util_formula_doc_cover.ts b/common_util/common_util_formula_doc_cover.ts index 6b598089d..12d0f28ea 100644 --- a/common_util/common_util_formula_doc_cover.ts +++ b/common_util/common_util_formula_doc_cover.ts @@ -24,7 +24,7 @@ export const COVER = "기계덮음"; /** 값 칸에서 빼는 기계 칸 — 줄(조종원 · 잡품)로 다룸 */ export const NOT_VALUE = ["조종원", "조종원직종", "잡재료율"]; const HOUR = "Q_시간환산"; -/** 조종원 줄을 새로 넣을 때(조종원 없는 식) 시간환산 — GC000267 과 같은 꼴(건설품셈 공통 1-1-3 2) */ +/** 조종원 줄을 새로 넣을 때(조종원 없는 식) 시간환산 — GC000267 과 같은 꼴(1/8 × 16/12 × 25/20 · 건협기술-2230) */ const HOUR_VARS: DocVar[] = [ { 열쇠: "Q_분자^표", 기호: "Q", 아래: "분자", 위: "표", 이름: "건설기계 조종원 노임 시간 환산 계수.분자", 단위: "", @@ -34,9 +34,28 @@ const HOUR_VARS: DocVar[] = [ 열쇠: "Q_분모^표", 기호: "Q", 아래: "분모", 위: "표", 이름: "건설기계 조종원 노임 시간 환산 계수.분모", 단위: "", 연결: { 갈래: "표", 표: "CC000336", 조건: { 구분: "'시간환산'" }, 칸: "분모" }, }, + { + 열쇠: "Q_분자2^표", 기호: "Q", 아래: "분자2", 위: "표", 이름: "건설기계 조종원 노임 시간 환산 계수.분자", 단위: "", + 연결: { 갈래: "표", 표: "CC000336", 조건: { 구분: "'상여계수'" }, 칸: "분자" }, + }, + { + 열쇠: "Q_분모2^표", 기호: "Q", 아래: "분모2", 위: "표", 이름: "건설기계 조종원 노임 시간 환산 계수.분모", 단위: "", + 연결: { 갈래: "표", 표: "CC000336", 조건: { 구분: "'상여계수'" }, 칸: "분모" }, + }, + { + 열쇠: "Q_분자3^표", 기호: "Q", 아래: "분자3", 위: "표", 이름: "건설기계 조종원 노임 시간 환산 계수.분자", 단위: "", + 연결: { 갈래: "표", 표: "CC000336", 조건: { 구분: "'휴지계수'" }, 칸: "분자" }, + }, + { + 열쇠: "Q_분모3^표", 기호: "Q", 아래: "분모3", 위: "표", 이름: "건설기계 조종원 노임 시간 환산 계수.분모", 단위: "", + 연결: { 갈래: "표", 표: "CC000336", 조건: { 구분: "'휴지계수'" }, 칸: "분모" }, + }, { 열쇠: "시간환산", 기호: "Q", 아래: "시간환산", 위: "", 이름: "시간환산", 단위: "", 연결: { 갈래: "식" } }, ]; // prettier-ignore -const HOUR_LINE = { 기호: HOUR, 식: "Q_분자^표 ÷ Q_분모^표" }; +const HOUR_LINE = { + 기호: HOUR, + 식: "Q_분자^표 ÷ Q_분모^표 × Q_분자2^표 ÷ Q_분모2^표 × Q_분자3^표 ÷ Q_분모3^표", +}; export interface CrewRow { 키: string; diff --git a/common_util/common_util_machine_coef.ts b/common_util/common_util_machine_coef.ts index 83316e0ee..4bbba8228 100644 --- a/common_util/common_util_machine_coef.ts +++ b/common_util/common_util_machine_coef.ts @@ -20,6 +20,8 @@ export const MACHINE_COEFS = ["상각비계수", "정비비계수", "관리비 export type MachineCoefs = Record<(typeof MACHINE_COEFS)[number], number>; const E = 1e-6; // 끊는 경계 부동소수 흔들림 막음 +/** 조종원 하루 → 시간(CC000336 시간환산 · 상여 · 휴지 = 1/8 × 16/12 × 25/20 · 840-5) */ +export const MACHINE_HOUR = ((((1 / 8) * 16) / 12) * 25) / 20; // 파이썬 짝과 같은 차례 const num = (v: unknown): number => (typeof v === "number" && Number.isFinite(v) ? v : 0); const halfUp = (x: number): number => Math.floor(x + 0.5 + E); @@ -69,7 +71,7 @@ export function machineHourly( fuel: number, wage: number, power = 0, - hour = 1 / 8, + hour = MACHINE_HOUR, ): MachineHourly { const loss = Math.floor((num(values["가격"]) * 1000 * num(values["손료계수"])) / 1e7 + E); const oil = tenth(num(values["주연료"]) * num(fuel)); diff --git a/common_util/common_util_machine_symbol_name.ts b/common_util/common_util_machine_symbol_name.ts index 84f99d476..53830025b 100644 --- a/common_util/common_util_machine_symbol_name.ts +++ b/common_util/common_util_machine_symbol_name.ts @@ -34,6 +34,10 @@ const NAME: Record = { Q_시간환산: "환산", "Q_분자^표": "환산분자", "Q_분모^표": "환산분모", + "Q_분자2^표": "상여분자", + "Q_분모2^표": "상여분모", + "Q_분자3^표": "휴지분자", + "Q_분모3^표": "휴지분모", "Q_주연료^표": "휘발유량", "Q_주연료2^표": "오일량", "Q_잡^표": "잡품", diff --git a/resources/master_data/scripts/build_수식문서_802_2.py b/resources/master_data/scripts/build_수식문서_802_2.py index 5d32c7cf5..41fbdeb63 100644 --- a/resources/master_data/scripts/build_수식문서_802_2.py +++ b/resources/master_data/scripts/build_수식문서_802_2.py @@ -47,7 +47,7 @@ RAW_SUB = ( "(옛 엔진 = 끝수 전 날 값 · `master_formula.run` 결과를 식에 씀)" ) # 채택된 차이 — (로직, 옵션) · 요구 714-3 -ADOPTED = {("GC000100", "지역=경북")} +ADOPTED = {("GC000091", "지역=울산"), ("GC000093", "지역=경북"), ("GC000100", "지역=전북")} def rows_of(files: dict) -> list[tuple[str, dict]]: diff --git a/resources/master_data/scripts/master_doc_cover.py b/resources/master_data/scripts/master_doc_cover.py index 7258111f6..86701b53b 100644 --- a/resources/master_data/scripts/master_doc_cover.py +++ b/resources/master_data/scripts/master_doc_cover.py @@ -30,9 +30,20 @@ _HOUR_VARS = [ "단위": "", "연결": {"갈래": "표", "표": "CC000336", "조건": {"구분": "'시간환산'"}, "칸": "분자"}}, {"열쇠": "Q_분모^표", "기호": "Q", "아래": "분모", "위": "표", "이름": "건설기계 조종원 노임 시간 환산 계수.분모", "단위": "", "연결": {"갈래": "표", "표": "CC000336", "조건": {"구분": "'시간환산'"}, "칸": "분모"}}, + {"열쇠": "Q_분자2^표", "기호": "Q", "아래": "분자2", "위": "표", "이름": "건설기계 조종원 노임 시간 환산 계수.분자", + "단위": "", "연결": {"갈래": "표", "표": "CC000336", "조건": {"구분": "'상여계수'"}, "칸": "분자"}}, + {"열쇠": "Q_분모2^표", "기호": "Q", "아래": "분모2", "위": "표", "이름": "건설기계 조종원 노임 시간 환산 계수.분모", + "단위": "", "연결": {"갈래": "표", "표": "CC000336", "조건": {"구분": "'상여계수'"}, "칸": "분모"}}, + {"열쇠": "Q_분자3^표", "기호": "Q", "아래": "분자3", "위": "표", "이름": "건설기계 조종원 노임 시간 환산 계수.분자", + "단위": "", "연결": {"갈래": "표", "표": "CC000336", "조건": {"구분": "'휴지계수'"}, "칸": "분자"}}, + {"열쇠": "Q_분모3^표", "기호": "Q", "아래": "분모3", "위": "표", "이름": "건설기계 조종원 노임 시간 환산 계수.분모", + "단위": "", "연결": {"갈래": "표", "표": "CC000336", "조건": {"구분": "'휴지계수'"}, "칸": "분모"}}, {"열쇠": "시간환산", "기호": "Q", "아래": "시간환산", "위": "", "이름": "시간환산", "단위": "", "연결": {"갈래": "식"}}, ] # fmt: skip -_HOUR_LINE = {"기호": HOUR, "식": "Q_분자^표 ÷ Q_분모^표"} +_HOUR_LINE = { + "기호": HOUR, + "식": "Q_분자^표 ÷ Q_분모^표 × Q_분자2^표 ÷ Q_분모2^표 × Q_분자3^표 ÷ Q_분모3^표", +} MAX_ROWS = 30 diff --git a/resources/master_data/scripts/master_machine_coef.py b/resources/master_data/scripts/master_machine_coef.py index e9ae5f8c6..bf4085fef 100644 --- a/resources/master_data/scripts/master_machine_coef.py +++ b/resources/master_data/scripts/master_machine_coef.py @@ -22,6 +22,7 @@ import math INPUTS = ("내용시간", "연간표준가동시간", "상각비율", "정비비율", "연간관리비율") COEFS = ("상각비계수", "정비비계수", "관리비계수", "손료계수") _E = 1e-6 # 끊는 경계 부동소수 흔들림 막음 +HOUR = 1 / 8 * 16 / 12 * 25 / 20 # 조종원 하루 → 시간(CC000336 시간환산 · 상여 · 휴지 · 840-5) def _num(v) -> float: @@ -56,9 +57,7 @@ def coef_of(values: dict) -> dict | None: return coef(*(values[k] for k in INPUTS)) -def preview( - values: dict, fuel: float, wage: float, power: float = 0.0, hour: float = 1 / 8 -) -> dict: +def preview(values: dict, fuel: float, wage: float, power: float = 0.0, hour: float = HOUR) -> dict: """값 묶음 + 유가(원/ℓ) · 노임(원/일) · 전력(원/kWh) · 시간환산 → 시간당 줄 · 비목 · 계.""" loss = math.floor(_num(values.get("가격")) * 1000 * _num(values.get("손료계수")) / 1e7 + _E) oil = _tenth(_num(values.get("주연료")) * _num(fuel)) diff --git a/resources/master_data/로직_건설품셈_08장_건설기계.json b/resources/master_data/로직_건설품셈_08장_건설기계.json index a699bb932..016258fab 100644 --- a/resources/master_data/로직_건설품셈_08장_건설기계.json +++ b/resources/master_data/로직_건설품셈_08장_건설기계.json @@ -33,7 +33,11 @@ }, { "이름": "연료", "식": "EQ:{기계}.주연료종류", "출처": "건설품셈 공통 8-4 주①㉰" }, { "이름": "직종", "식": "EQ:{기계}.조종원직종", "출처": "건설품셈 공통 8-1-3 5" }, - { "이름": "시간환산", "식": "찾기(CC000336, 구분='시간환산').분자 / 찾기(CC000336, 구분='시간환산').분모", "출처": "건설품셈 공통 1-1-3 2(일일 작업시간 8시간)" } + { + "이름": "시간환산", + "식": "찾기(CC000336, 구분='시간환산').분자 / 찾기(CC000336, 구분='시간환산').분모 * 찾기(CC000336, 구분='상여계수').분자 / 찾기(CC000336, 구분='상여계수').분모 * 찾기(CC000336, 구분='휴지계수').분자 / 찾기(CC000336, 구분='휴지계수').분모", + "출처": "질의회신 건협기술-2230(1일 8시간 · 상여 · 휴지)" + } ], "호표": [ { "종류": "재료", "요소": "MO:{연료}:{지역}", "이름": "주연료", "단위": "ℓ", "수량": "EQ:{기계}.주연료", "비목": "재료비", "출처": "건설품셈 공통 8-4-1" }, @@ -45,7 +49,7 @@ "수량": "EQ:{기계}.조종원 * 시간환산", "비목": "노무비", "출처": "건설품셈 공통 8-1-3 6", - "비고": "하루 노임 × 1/8(1일 8시간 · 건설품셈 공통 1-1-3 2) — 질의회신 건협기술-2230 의 상여 16/12 · 휴지 25/20 뺌(내역 15 · 품셈 본문대로)" + "비고": "하루 노임 × 1/8 × 16/12 × 25/20(1일 8시간 · 상여 300 % + 퇴직 100 % · 월 25 일 중 20 일 가동 — 질의회신 건협기술-2230 · 실무 · 경쟁사와 같음)" } ], "덧줄": [{ "이름": "잡재료비", "식": "재료비 * EQ:{기계}.잡재료율 / 100", "비목": "재료비", "출처": "건설품셈 공통 8-4 주②" }, { "이름": "기계손료", "식": "손료", "비목": "경비", "출처": "건설품셈 공통 8-1-6 2" }], @@ -303,7 +307,11 @@ { "이름": "손료", "식": "버림(EQ:{기계}.가격 * 1000 * ((EQ:{기계}.상각비계수 + EQ:{기계}.정비비계수) + EQ:{기계}.관리비계수) / 10000000, 0)", "출처": "건설품셈 공통 8-1-5 12 · 8-1-6 4" }, { "이름": "연료", "식": "EQ:{기계}.주연료종류", "출처": "건설품셈 공통 8-4 주①㉰" }, { "이름": "직종", "식": "EQ:{기계}.조종원직종", "출처": "건설품셈 공통 8-1-3 5" }, - { "이름": "시간환산", "식": "찾기(CC000336, 구분='시간환산').분자 / 찾기(CC000336, 구분='시간환산').분모", "출처": "건설품셈 공통 1-1-3 2(일일 작업시간 8시간)" } + { + "이름": "시간환산", + "식": "찾기(CC000336, 구분='시간환산').분자 / 찾기(CC000336, 구분='시간환산').분모 * 찾기(CC000336, 구분='상여계수').분자 / 찾기(CC000336, 구분='상여계수').분모 * 찾기(CC000336, 구분='휴지계수').분자 / 찾기(CC000336, 구분='휴지계수').분모", + "출처": "질의회신 건협기술-2230(1일 8시간 · 상여 · 휴지)" + } ], "호표": [ { "종류": "재료", "요소": "MO:{연료}:{지역}", "이름": "주연료", "단위": "ℓ", "수량": "EQ:{기계}.주연료", "비목": "재료비", "출처": "건설품셈 공통 8-4-1" }, @@ -315,7 +323,7 @@ "수량": "EQ:{기계}.조종원 * 시간환산", "비목": "노무비", "출처": "건설품셈 공통 8-1-3 6", - "비고": "하루 노임 × 1/8(1일 8시간 · 건설품셈 공통 1-1-3 2) — 질의회신 건협기술-2230 의 상여 16/12 · 휴지 25/20 뺌(내역 15 · 품셈 본문대로)" + "비고": "하루 노임 × 1/8 × 16/12 × 25/20(1일 8시간 · 상여 300 % + 퇴직 100 % · 월 25 일 중 20 일 가동 — 질의회신 건협기술-2230 · 실무 · 경쟁사와 같음)" } ], "덧줄": [{ "이름": "잡재료비", "식": "재료비 * EQ:{기계}.잡재료율 / 100", "비목": "재료비", "출처": "건설품셈 공통 8-4 주②" }, { "이름": "기계손료", "식": "손료", "비목": "경비", "출처": "건설품셈 공통 8-1-6 2" }], @@ -496,7 +504,11 @@ }, { "이름": "연료", "식": "EQ:{기계}.주연료종류", "출처": "건설품셈 공통 8-4 주①㉰" }, { "이름": "직종", "식": "EQ:{기계}.조종원직종", "출처": "건설품셈 공통 8-1-3 5" }, - { "이름": "시간환산", "식": "찾기(CC000336, 구분='시간환산').분자 / 찾기(CC000336, 구분='시간환산').분모", "출처": "건설품셈 공통 1-1-3 2(일일 작업시간 8시간)" } + { + "이름": "시간환산", + "식": "찾기(CC000336, 구분='시간환산').분자 / 찾기(CC000336, 구분='시간환산').분모 * 찾기(CC000336, 구분='상여계수').분자 / 찾기(CC000336, 구분='상여계수').분모 * 찾기(CC000336, 구분='휴지계수').분자 / 찾기(CC000336, 구분='휴지계수').분모", + "출처": "질의회신 건협기술-2230(1일 8시간 · 상여 · 휴지)" + } ], "호표": [ { "종류": "재료", "요소": "MO:{연료}:{지역}", "이름": "주연료", "단위": "ℓ", "수량": "EQ:{기계}.주연료", "비목": "재료비", "출처": "건설품셈 공통 8-4-1" }, @@ -508,7 +520,7 @@ "수량": "EQ:{기계}.조종원 * 시간환산", "비목": "노무비", "출처": "건설품셈 공통 8-1-3 6", - "비고": "하루 노임 × 1/8(1일 8시간 · 건설품셈 공통 1-1-3 2) — 질의회신 건협기술-2230 의 상여 16/12 · 휴지 25/20 뺌(내역 15 · 품셈 본문대로)" + "비고": "하루 노임 × 1/8 × 16/12 × 25/20(1일 8시간 · 상여 300 % + 퇴직 100 % · 월 25 일 중 20 일 가동 — 질의회신 건협기술-2230 · 실무 · 경쟁사와 같음)" } ], "덧줄": [{ "이름": "잡재료비", "식": "재료비 * EQ:{기계}.잡재료율 / 100", "비목": "재료비", "출처": "건설품셈 공통 8-4 주②" }, { "이름": "기계손료", "식": "손료", "비목": "경비", "출처": "건설품셈 공통 8-1-6 2" }], @@ -557,7 +569,11 @@ { "이름": "조합잡재료율", "식": "찾기(CC000074, 구분='불도저 및 굴착기 잡재료 비율').적용", "출처": "건설품셈 공통 8-4-8 주⑤" }, { "이름": "연료", "식": "EQ:{기계}.주연료종류", "출처": "건설품셈 공통 8-4 주①㉰" }, { "이름": "직종", "식": "EQ:{기계}.조종원직종", "출처": "건설품셈 공통 8-1-3 5" }, - { "이름": "시간환산", "식": "찾기(CC000336, 구분='시간환산').분자 / 찾기(CC000336, 구분='시간환산').분모", "출처": "건설품셈 공통 1-1-3 2(일일 작업시간 8시간)" } + { + "이름": "시간환산", + "식": "찾기(CC000336, 구분='시간환산').분자 / 찾기(CC000336, 구분='시간환산').분모 * 찾기(CC000336, 구분='상여계수').분자 / 찾기(CC000336, 구분='상여계수').분모 * 찾기(CC000336, 구분='휴지계수').분자 / 찾기(CC000336, 구분='휴지계수').분모", + "출처": "질의회신 건협기술-2230(1일 8시간 · 상여 · 휴지)" + } ], "호표": [ { "종류": "재료", "요소": "MO:{연료}:{지역}", "이름": "주연료", "단위": "ℓ", "수량": "EQ:{기계}.주연료", "비목": "재료비", "출처": "건설품셈 공통 8-4-1" }, @@ -569,7 +585,7 @@ "수량": "EQ:{기계}.조종원 * 시간환산", "비목": "노무비", "출처": "건설품셈 공통 8-1-3 6", - "비고": "하루 노임 × 1/8(1일 8시간 · 건설품셈 공통 1-1-3 2) — 질의회신 건협기술-2230 의 상여 16/12 · 휴지 25/20 뺌(내역 15 · 품셈 본문대로)" + "비고": "하루 노임 × 1/8 × 16/12 × 25/20(1일 8시간 · 상여 300 % + 퇴직 100 % · 월 25 일 중 20 일 가동 — 질의회신 건협기술-2230 · 실무 · 경쟁사와 같음)" } ], "덧줄": [ @@ -628,7 +644,11 @@ { "이름": "조합잡재료율", "식": "찾기(CC000074, 구분='불도저 및 굴착기 잡재료 비율').적용", "출처": "건설품셈 공통 8-4-8 주⑤" }, { "이름": "연료", "식": "EQ:{기계}.주연료종류", "출처": "건설품셈 공통 8-4 주①㉰" }, { "이름": "직종", "식": "EQ:{기계}.조종원직종", "출처": "건설품셈 공통 8-1-3 5" }, - { "이름": "시간환산", "식": "찾기(CC000336, 구분='시간환산').분자 / 찾기(CC000336, 구분='시간환산').분모", "출처": "건설품셈 공통 1-1-3 2(일일 작업시간 8시간)" } + { + "이름": "시간환산", + "식": "찾기(CC000336, 구분='시간환산').분자 / 찾기(CC000336, 구분='시간환산').분모 * 찾기(CC000336, 구분='상여계수').분자 / 찾기(CC000336, 구분='상여계수').분모 * 찾기(CC000336, 구분='휴지계수').분자 / 찾기(CC000336, 구분='휴지계수').분모", + "출처": "질의회신 건협기술-2230(1일 8시간 · 상여 · 휴지)" + } ], "호표": [ { "종류": "재료", "요소": "MO:{연료}:{지역}", "이름": "주연료", "단위": "ℓ", "수량": "EQ:{기계}.주연료", "비목": "재료비", "출처": "건설품셈 공통 8-4-1" }, @@ -640,7 +660,7 @@ "수량": "EQ:{기계}.조종원 * 시간환산", "비목": "노무비", "출처": "건설품셈 공통 8-1-3 6", - "비고": "하루 노임 × 1/8(1일 8시간 · 건설품셈 공통 1-1-3 2) — 질의회신 건협기술-2230 의 상여 16/12 · 휴지 25/20 뺌(내역 15 · 품셈 본문대로)" + "비고": "하루 노임 × 1/8 × 16/12 × 25/20(1일 8시간 · 상여 300 % + 퇴직 100 % · 월 25 일 중 20 일 가동 — 질의회신 건협기술-2230 · 실무 · 경쟁사와 같음)" } ], "덧줄": [ @@ -1318,7 +1338,11 @@ "중간": [ { "이름": "손료", "식": "버림(EQ:{기계}.가격 * 1000 * ((EQ:{기계}.상각비계수 + EQ:{기계}.정비비계수) + EQ:{기계}.관리비계수) / 10000000, 0)", "출처": "건설품셈 공통 8-1-5 12 · 8-1-6 4" }, { "이름": "연료", "식": "EQ:{기계}.주연료종류", "출처": "건설품셈 공통 8-4 주①㉰" }, - { "이름": "시간환산", "식": "찾기(CC000336, 구분='시간환산').분자 / 찾기(CC000336, 구분='시간환산').분모", "출처": "건설품셈 공통 1-1-3 2(일일 작업시간 8시간)" } + { + "이름": "시간환산", + "식": "찾기(CC000336, 구분='시간환산').분자 / 찾기(CC000336, 구분='시간환산').분모 * 찾기(CC000336, 구분='상여계수').분자 / 찾기(CC000336, 구분='상여계수').분모 * 찾기(CC000336, 구분='휴지계수').분자 / 찾기(CC000336, 구분='휴지계수').분모", + "출처": "질의회신 건협기술-2230(1일 8시간 · 상여 · 휴지)" + } ], "호표": [ { "종류": "재료", "요소": "MO:{연료}:{지역}", "이름": "주연료", "단위": "ℓ", "수량": "EQ:{기계}.주연료", "비목": "재료비", "출처": "건설품셈 공통 8-4-1" }, @@ -1330,7 +1354,7 @@ "수량": "EQ:{기계}.조종원 * 시간환산", "비목": "노무비", "출처": "건설품셈 공통 8-1-3 6", - "비고": "하루 노임 × 1/8(1일 8시간 · 건설품셈 공통 1-1-3 2) — 질의회신 건협기술-2230 의 상여 16/12 · 휴지 25/20 뺌(내역 15 · 품셈 본문대로)" + "비고": "하루 노임 × 1/8 × 16/12 × 25/20(1일 8시간 · 상여 300 % + 퇴직 100 % · 월 25 일 중 20 일 가동 — 질의회신 건협기술-2230 · 실무 · 경쟁사와 같음)" } ], "덧줄": [{ "이름": "잡재료비", "식": "재료비 * EQ:{기계}.잡재료율 / 100", "비목": "재료비", "출처": "건설품셈 공통 8-4 주②" }, { "이름": "기계손료", "식": "손료", "비목": "경비", "출처": "건설품셈 공통 8-1-6 2" }], @@ -1389,7 +1413,11 @@ "중간": [ { "이름": "손료", "식": "버림(EQ:{기계}.가격 * 1000 * ((EQ:{기계}.상각비계수 + EQ:{기계}.정비비계수) + EQ:{기계}.관리비계수) / 10000000, 0)", "출처": "건설품셈 공통 8-1-5 12 · 8-1-6 4" }, { "이름": "직종", "식": "EQ:{기계}.조종원직종", "출처": "건설품셈 공통 8-1-3 5" }, - { "이름": "시간환산", "식": "찾기(CC000336, 구분='시간환산').분자 / 찾기(CC000336, 구분='시간환산').분모", "출처": "건설품셈 공통 1-1-3 2(일일 작업시간 8시간)" } + { + "이름": "시간환산", + "식": "찾기(CC000336, 구분='시간환산').분자 / 찾기(CC000336, 구분='시간환산').분모 * 찾기(CC000336, 구분='상여계수').분자 / 찾기(CC000336, 구분='상여계수').분모 * 찾기(CC000336, 구분='휴지계수').분자 / 찾기(CC000336, 구분='휴지계수').분모", + "출처": "질의회신 건협기술-2230(1일 8시간 · 상여 · 휴지)" + } ], "호표": [ { @@ -1400,7 +1428,7 @@ "수량": "EQ:{기계}.조종원 * 시간환산", "비목": "노무비", "출처": "건설품셈 공통 8-1-3 6", - "비고": "하루 노임 × 1/8(1일 8시간 · 건설품셈 공통 1-1-3 2) — 질의회신 건협기술-2230 의 상여 16/12 · 휴지 25/20 뺌(내역 15 · 품셈 본문대로)" + "비고": "하루 노임 × 1/8 × 16/12 × 25/20(1일 8시간 · 상여 300 % + 퇴직 100 % · 월 25 일 중 20 일 가동 — 질의회신 건협기술-2230 · 실무 · 경쟁사와 같음)" } ], "덧줄": [{ "이름": "기계손료", "식": "손료", "비목": "경비", "출처": "건설품셈 공통 8-1-6 2" }], @@ -3516,7 +3544,11 @@ "중간": [ { "이름": "손료", "식": "버림(EQ:{기계}.가격 * 1000 * EQ:{기계}.손료계수 / 10000000, 0)", "출처": "건설품셈 공통 8-1-5 12 · 8-1-6 4" }, { "이름": "직종", "식": "EQ:{기계}.조종원직종", "출처": "건설품셈 공통 8-1-3 5" }, - { "이름": "시간환산", "식": "찾기(CC000336, 구분='시간환산').분자 / 찾기(CC000336, 구분='시간환산').분모", "출처": "건설품셈 공통 1-1-3 2(일일 작업시간 8시간)" } + { + "이름": "시간환산", + "식": "찾기(CC000336, 구분='시간환산').분자 / 찾기(CC000336, 구분='시간환산').분모 * 찾기(CC000336, 구분='상여계수').분자 / 찾기(CC000336, 구분='상여계수').분모 * 찾기(CC000336, 구분='휴지계수').분자 / 찾기(CC000336, 구분='휴지계수').분모", + "출처": "질의회신 건협기술-2230(1일 8시간 · 상여 · 휴지)" + } ], "호표": [ { @@ -3671,7 +3703,11 @@ { "이름": "손료", "식": "버림(EQ:{기계}.가격 * 1000 * EQ:{기계}.손료계수 / 10000000, 0)", "출처": "건설품셈 공통 8-1-5 12 · 8-1-6 4" }, { "이름": "연료", "식": "EQ:{기계}.주연료종류", "출처": "건설품셈 공통 8-4 주①㉰" }, { "이름": "직종", "식": "EQ:{기계}.조종원직종", "출처": "건설품셈 공통 8-1-3 5" }, - { "이름": "시간환산", "식": "찾기(CC000336, 구분='시간환산').분자 / 찾기(CC000336, 구분='시간환산').분모", "출처": "건설품셈 공통 1-1-3 2(일일 작업시간 8시간)" } + { + "이름": "시간환산", + "식": "찾기(CC000336, 구분='시간환산').분자 / 찾기(CC000336, 구분='시간환산').분모 * 찾기(CC000336, 구분='상여계수').분자 / 찾기(CC000336, 구분='상여계수').분모 * 찾기(CC000336, 구분='휴지계수').분자 / 찾기(CC000336, 구분='휴지계수').분모", + "출처": "질의회신 건협기술-2230(1일 8시간 · 상여 · 휴지)" + } ], "호표": [ { "종류": "재료", "요소": "MO:{연료}:{지역}", "이름": "주연료", "단위": "ℓ", "수량": "EQ:{기계}.주연료", "비목": "재료비", "출처": "건설품셈 공통 8-4-8" }, diff --git a/resources/master_data/로직_산림품셈_10장_자재․장비_운반.json b/resources/master_data/로직_산림품셈_10장_자재․장비_운반.json index cb693f8e3..62ecf86f5 100644 --- a/resources/master_data/로직_산림품셈_10장_자재․장비_운반.json +++ b/resources/master_data/로직_산림품셈_10장_자재․장비_운반.json @@ -534,7 +534,11 @@ { "이름": "손료", "식": "버림(EQ000655.가격 * 1000 * (EQ000069.상각비계수 + EQ000069.정비비계수 + EQ000069.관리비계수) / 10000000, 0)", "출처": "산림품셈 10-9 · 건설품셈 공통 8-1-5 12" }, { "이름": "비율", "식": "찾기(QF000330, 구분='2.5톤 덤프 주 연료 대비').비율", "출처": "산림품셈 10-9" }, { "이름": "직종", "식": "EQ000069.조종원직종" }, - { "이름": "시간환산", "식": "찾기(CC000336, 구분='시간환산').분자 / 찾기(CC000336, 구분='시간환산').분모", "출처": "건설품셈 공통 1-1-3 2(일일 작업시간 8시간)" } + { + "이름": "시간환산", + "식": "찾기(CC000336, 구분='시간환산').분자 / 찾기(CC000336, 구분='시간환산').분모 * 찾기(CC000336, 구분='상여계수').분자 / 찾기(CC000336, 구분='상여계수').분모 * 찾기(CC000336, 구분='휴지계수').분자 / 찾기(CC000336, 구분='휴지계수').분모", + "출처": "질의회신 건협기술-2230(1일 8시간 · 상여 · 휴지)" + } ], "호표": [ { "종류": "재료", "요소": "MO:경유:{지역}", "이름": "주연료", "단위": "ℓ", "수량": "EQ000069.주연료 * 비율 / 100", "비목": "재료비", "출처": "산림품셈 10-9" }, @@ -546,7 +550,7 @@ "수량": "EQ000069.조종원 * 시간환산", "비목": "노무비", "출처": "건설품셈 공통 8-1-3 6", - "비고": "하루 노임 × 1/8(1일 8시간 · 건설품셈 공통 1-1-3 2) — 질의회신 건협기술-2230 의 상여 16/12 · 휴지 25/20 뺌(내역 15 · 품셈 본문대로)" + "비고": "하루 노임 × 1/8 × 16/12 × 25/20(1일 8시간 · 상여 300 % + 퇴직 100 % · 월 25 일 중 20 일 가동 — 질의회신 건협기술-2230 · 실무 · 경쟁사와 같음)" } ], "덧줄": [ diff --git a/resources/master_data/수식문서/수식_건설품셈_08장_건설기계.json b/resources/master_data/수식문서/수식_건설품셈_08장_건설기계.json index 57886345c..c1d1d7792 100644 --- a/resources/master_data/수식문서/수식_건설품셈_08장_건설기계.json +++ b/resources/master_data/수식문서/수식_건설품셈_08장_건설기계.json @@ -20,7 +20,7 @@ {"열쇠": "손료", "기호": "C", "아래": "손료", "위": "", "이름": "손료", "단위": "", "출처": "건설품셈 공통 8-1-5 12 · 8-1-6 4 · 8-1-7 1", "연결": {"갈래": "식"}}, {"열쇠": "연료", "기호": "S", "아래": "연료", "위": "", "이름": "연료", "단위": "", "출처": "건설품셈 공통 8-4 주①㉰", "연결": {"갈래": "요소", "키": "EQ:{S_기계}", "칸": "주연료종류"}}, {"열쇠": "직종", "기호": "S", "아래": "직종", "위": "", "이름": "직종", "단위": "", "출처": "건설품셈 공통 8-1-3 5", "연결": {"갈래": "요소", "키": "EQ:{S_기계}", "칸": "조종원직종"}}, - {"열쇠": "시간환산", "기호": "Q", "아래": "시간환산", "위": "", "이름": "시간환산", "단위": "", "출처": "건설품셈 공통 1-1-3 2(일일 작업시간 8시간)", "연결": {"갈래": "식"}}, + {"열쇠": "시간환산", "기호": "Q", "아래": "시간환산", "위": "", "이름": "시간환산", "단위": "", "출처": "질의회신 건협기술-2230(1일 8시간 · 상여 · 휴지)", "연결": {"갈래": "식"}}, {"열쇠": "r_가산^표", "기호": "r", "아래": "가산", "위": "표", "이름": "기계손료의 보정.가산비율", "단위": "%", "연결": {"갈래": "표", "표": "CC000034", "조건": {"기종": "'굴착기(무한궤도) 및 로더(무한궤도)'", "작업": "S_보정작업"}, "칸": "가산비율"}}, {"열쇠": "P_기계", "기호": "P", "아래": "기계", "위": "", "이름": "EQ:{기계}.가격", "단위": "", "연결": {"갈래": "요소", "키": "EQ:{S_기계}", "칸": "가격"}}, {"열쇠": "k_기계상각비", "기호": "k", "아래": "기계상각비", "위": "", "이름": "EQ:{기계}.상각비계수", "단위": "", "연결": {"갈래": "요소", "키": "EQ:{S_기계}", "칸": "상각비계수"}}, @@ -28,6 +28,10 @@ {"열쇠": "k_기계관리비", "기호": "k", "아래": "기계관리비", "위": "", "이름": "EQ:{기계}.관리비계수", "단위": "", "연결": {"갈래": "요소", "키": "EQ:{S_기계}", "칸": "관리비계수"}}, {"열쇠": "Q_분자^표", "기호": "Q", "아래": "분자", "위": "표", "이름": "건설기계 조종원 노임 시간 환산 계수.분자", "단위": "", "연결": {"갈래": "표", "표": "CC000336", "조건": {"구분": "'시간환산'"}, "칸": "분자"}}, {"열쇠": "Q_분모^표", "기호": "Q", "아래": "분모", "위": "표", "이름": "건설기계 조종원 노임 시간 환산 계수.분모", "단위": "", "연결": {"갈래": "표", "표": "CC000336", "조건": {"구분": "'시간환산'"}, "칸": "분모"}}, + {"열쇠": "Q_분자2^표", "기호": "Q", "아래": "분자2", "위": "표", "이름": "건설기계 조종원 노임 시간 환산 계수.분자", "단위": "", "연결": {"갈래": "표", "표": "CC000336", "조건": {"구분": "'상여계수'"}, "칸": "분자"}}, + {"열쇠": "Q_분모2^표", "기호": "Q", "아래": "분모2", "위": "표", "이름": "건설기계 조종원 노임 시간 환산 계수.분모", "단위": "", "연결": {"갈래": "표", "표": "CC000336", "조건": {"구분": "'상여계수'"}, "칸": "분모"}}, + {"열쇠": "Q_분자3^표", "기호": "Q", "아래": "분자3", "위": "표", "이름": "건설기계 조종원 노임 시간 환산 계수.분자", "단위": "", "연결": {"갈래": "표", "표": "CC000336", "조건": {"구분": "'휴지계수'"}, "칸": "분자"}}, + {"열쇠": "Q_분모3^표", "기호": "Q", "아래": "분모3", "위": "표", "이름": "건설기계 조종원 노임 시간 환산 계수.분모", "단위": "", "연결": {"갈래": "표", "표": "CC000336", "조건": {"구분": "'휴지계수'"}, "칸": "분모"}}, {"열쇠": "P_기계주연료", "기호": "P", "아래": "기계주연료", "위": "", "이름": "EQ:{기계}.주연료", "단위": "", "연결": {"갈래": "요소", "키": "EQ:{S_기계}", "칸": "주연료"}}, {"열쇠": "P_자동차용경유", "기호": "P", "아래": "자동차용경유", "위": "", "이름": "자동차용경유단가", "단위": "", "연결": {"갈래": "요소", "키": "MO:{S_연료}:{S_지역}"}}, {"열쇠": "Q_기계조종원", "기호": "Q", "아래": "기계조종원", "위": "", "이름": "EQ:{기계}.조종원", "단위": "", "연결": {"갈래": "요소", "키": "EQ:{S_기계}", "칸": "조종원"}}, @@ -37,7 +41,7 @@ "식": [ {"기호": "r_가산", "식": "만약(S_보정작업 == '해당 없음', 0, r_가산^표)"}, {"기호": "C_손료", "식": "버림(P_기계 × 1000 × ((k_기계상각비 + k_기계정비비) × (1 + r_가산 ÷ 100) + k_기계관리비) ÷ 10000000, 0)"}, - {"기호": "Q_시간환산", "식": "Q_분자^표 ÷ Q_분모^표"} + {"기호": "Q_시간환산", "식": "Q_분자^표 ÷ Q_분모^표 × Q_분자2^표 ÷ Q_분모2^표 × Q_분자3^표 ÷ Q_분모3^표"} ], "금액": [ {"이름": "주연료", "단위": "ℓ", "비목": "재료비", "수량": "P_기계주연료", "단가": "P_자동차용경유", "출처": "건설품셈 공통 8-4-1"}, @@ -67,13 +71,17 @@ {"열쇠": "손료", "기호": "C", "아래": "손료", "위": "", "이름": "손료", "단위": "", "출처": "건설품셈 공통 8-1-5 12 · 8-1-6 4", "연결": {"갈래": "식"}}, {"열쇠": "연료", "기호": "S", "아래": "연료", "위": "", "이름": "연료", "단위": "", "출처": "건설품셈 공통 8-4 주①㉰", "연결": {"갈래": "요소", "키": "EQ:{S_기계}", "칸": "주연료종류"}}, {"열쇠": "직종", "기호": "S", "아래": "직종", "위": "", "이름": "직종", "단위": "", "출처": "건설품셈 공통 8-1-3 5", "연결": {"갈래": "요소", "키": "EQ:{S_기계}", "칸": "조종원직종"}}, - {"열쇠": "시간환산", "기호": "Q", "아래": "시간환산", "위": "", "이름": "시간환산", "단위": "", "출처": "건설품셈 공통 1-1-3 2(일일 작업시간 8시간)", "연결": {"갈래": "식"}}, + {"열쇠": "시간환산", "기호": "Q", "아래": "시간환산", "위": "", "이름": "시간환산", "단위": "", "출처": "질의회신 건협기술-2230(1일 8시간 · 상여 · 휴지)", "연결": {"갈래": "식"}}, {"열쇠": "P_기계", "기호": "P", "아래": "기계", "위": "", "이름": "EQ:{기계}.가격", "단위": "", "연결": {"갈래": "요소", "키": "EQ:{S_기계}", "칸": "가격"}}, {"열쇠": "k_기계상각비", "기호": "k", "아래": "기계상각비", "위": "", "이름": "EQ:{기계}.상각비계수", "단위": "", "연결": {"갈래": "요소", "키": "EQ:{S_기계}", "칸": "상각비계수"}}, {"열쇠": "k_기계정비비", "기호": "k", "아래": "기계정비비", "위": "", "이름": "EQ:{기계}.정비비계수", "단위": "", "연결": {"갈래": "요소", "키": "EQ:{S_기계}", "칸": "정비비계수"}}, {"열쇠": "k_기계관리비", "기호": "k", "아래": "기계관리비", "위": "", "이름": "EQ:{기계}.관리비계수", "단위": "", "연결": {"갈래": "요소", "키": "EQ:{S_기계}", "칸": "관리비계수"}}, {"열쇠": "Q_분자^표", "기호": "Q", "아래": "분자", "위": "표", "이름": "건설기계 조종원 노임 시간 환산 계수.분자", "단위": "", "연결": {"갈래": "표", "표": "CC000336", "조건": {"구분": "'시간환산'"}, "칸": "분자"}}, {"열쇠": "Q_분모^표", "기호": "Q", "아래": "분모", "위": "표", "이름": "건설기계 조종원 노임 시간 환산 계수.분모", "단위": "", "연결": {"갈래": "표", "표": "CC000336", "조건": {"구분": "'시간환산'"}, "칸": "분모"}}, + {"열쇠": "Q_분자2^표", "기호": "Q", "아래": "분자2", "위": "표", "이름": "건설기계 조종원 노임 시간 환산 계수.분자", "단위": "", "연결": {"갈래": "표", "표": "CC000336", "조건": {"구분": "'상여계수'"}, "칸": "분자"}}, + {"열쇠": "Q_분모2^표", "기호": "Q", "아래": "분모2", "위": "표", "이름": "건설기계 조종원 노임 시간 환산 계수.분모", "단위": "", "연결": {"갈래": "표", "표": "CC000336", "조건": {"구분": "'상여계수'"}, "칸": "분모"}}, + {"열쇠": "Q_분자3^표", "기호": "Q", "아래": "분자3", "위": "표", "이름": "건설기계 조종원 노임 시간 환산 계수.분자", "단위": "", "연결": {"갈래": "표", "표": "CC000336", "조건": {"구분": "'휴지계수'"}, "칸": "분자"}}, + {"열쇠": "Q_분모3^표", "기호": "Q", "아래": "분모3", "위": "표", "이름": "건설기계 조종원 노임 시간 환산 계수.분모", "단위": "", "연결": {"갈래": "표", "표": "CC000336", "조건": {"구분": "'휴지계수'"}, "칸": "분모"}}, {"열쇠": "P_기계주연료", "기호": "P", "아래": "기계주연료", "위": "", "이름": "EQ:{기계}.주연료", "단위": "", "연결": {"갈래": "요소", "키": "EQ:{S_기계}", "칸": "주연료"}}, {"열쇠": "P_휘발유", "기호": "P", "아래": "휘발유", "위": "", "이름": "휘발유단가", "단위": "", "연결": {"갈래": "요소", "키": "MO:{S_연료}:{S_지역}"}}, {"열쇠": "Q_기계조종원", "기호": "Q", "아래": "기계조종원", "위": "", "이름": "EQ:{기계}.조종원", "단위": "", "연결": {"갈래": "요소", "키": "EQ:{S_기계}", "칸": "조종원"}}, @@ -82,7 +90,7 @@ ], "식": [ {"기호": "C_손료", "식": "버림(P_기계 × 1000 × (k_기계상각비 + k_기계정비비 + k_기계관리비) ÷ 10000000, 0)"}, - {"기호": "Q_시간환산", "식": "Q_분자^표 ÷ Q_분모^표"} + {"기호": "Q_시간환산", "식": "Q_분자^표 ÷ Q_분모^표 × Q_분자2^표 ÷ Q_분모2^표 × Q_분자3^표 ÷ Q_분모3^표"} ], "금액": [ {"이름": "주연료", "단위": "ℓ", "비목": "재료비", "수량": "P_기계주연료", "단가": "P_휘발유", "출처": "건설품셈 공통 8-4-1"}, @@ -115,7 +123,7 @@ {"열쇠": "손료", "기호": "C", "아래": "손료", "위": "", "이름": "손료", "단위": "", "출처": "건설품셈 공통 8-1-5 12 · 8-1-6 4 · 8-1-7 1", "연결": {"갈래": "식"}}, {"열쇠": "연료", "기호": "S", "아래": "연료", "위": "", "이름": "연료", "단위": "", "출처": "건설품셈 공통 8-4 주①㉰", "연결": {"갈래": "요소", "키": "EQ:{S_기계}", "칸": "주연료종류"}}, {"열쇠": "직종", "기호": "S", "아래": "직종", "위": "", "이름": "직종", "단위": "", "출처": "건설품셈 공통 8-1-3 5", "연결": {"갈래": "요소", "키": "EQ:{S_기계}", "칸": "조종원직종"}}, - {"열쇠": "시간환산", "기호": "Q", "아래": "시간환산", "위": "", "이름": "시간환산", "단위": "", "출처": "건설품셈 공통 1-1-3 2(일일 작업시간 8시간)", "연결": {"갈래": "식"}}, + {"열쇠": "시간환산", "기호": "Q", "아래": "시간환산", "위": "", "이름": "시간환산", "단위": "", "출처": "질의회신 건협기술-2230(1일 8시간 · 상여 · 휴지)", "연결": {"갈래": "식"}}, {"열쇠": "r_가산^표", "기호": "r", "아래": "가산", "위": "표", "이름": "기계손료의 보정.가산비율", "단위": "%", "연결": {"갈래": "표", "표": "CC000034", "조건": {"기종": "S_보정기종", "작업": "S_보정작업"}, "칸": "가산비율"}}, {"열쇠": "P_기계", "기호": "P", "아래": "기계", "위": "", "이름": "EQ:{기계}.가격", "단위": "", "연결": {"갈래": "요소", "키": "EQ:{S_기계}", "칸": "가격"}}, {"열쇠": "k_기계상각비", "기호": "k", "아래": "기계상각비", "위": "", "이름": "EQ:{기계}.상각비계수", "단위": "", "연결": {"갈래": "요소", "키": "EQ:{S_기계}", "칸": "상각비계수"}}, @@ -123,6 +131,10 @@ {"열쇠": "k_기계관리비", "기호": "k", "아래": "기계관리비", "위": "", "이름": "EQ:{기계}.관리비계수", "단위": "", "연결": {"갈래": "요소", "키": "EQ:{S_기계}", "칸": "관리비계수"}}, {"열쇠": "Q_분자^표", "기호": "Q", "아래": "분자", "위": "표", "이름": "건설기계 조종원 노임 시간 환산 계수.분자", "단위": "", "연결": {"갈래": "표", "표": "CC000336", "조건": {"구분": "'시간환산'"}, "칸": "분자"}}, {"열쇠": "Q_분모^표", "기호": "Q", "아래": "분모", "위": "표", "이름": "건설기계 조종원 노임 시간 환산 계수.분모", "단위": "", "연결": {"갈래": "표", "표": "CC000336", "조건": {"구분": "'시간환산'"}, "칸": "분모"}}, + {"열쇠": "Q_분자2^표", "기호": "Q", "아래": "분자2", "위": "표", "이름": "건설기계 조종원 노임 시간 환산 계수.분자", "단위": "", "연결": {"갈래": "표", "표": "CC000336", "조건": {"구분": "'상여계수'"}, "칸": "분자"}}, + {"열쇠": "Q_분모2^표", "기호": "Q", "아래": "분모2", "위": "표", "이름": "건설기계 조종원 노임 시간 환산 계수.분모", "단위": "", "연결": {"갈래": "표", "표": "CC000336", "조건": {"구분": "'상여계수'"}, "칸": "분모"}}, + {"열쇠": "Q_분자3^표", "기호": "Q", "아래": "분자3", "위": "표", "이름": "건설기계 조종원 노임 시간 환산 계수.분자", "단위": "", "연결": {"갈래": "표", "표": "CC000336", "조건": {"구분": "'휴지계수'"}, "칸": "분자"}}, + {"열쇠": "Q_분모3^표", "기호": "Q", "아래": "분모3", "위": "표", "이름": "건설기계 조종원 노임 시간 환산 계수.분모", "단위": "", "연결": {"갈래": "표", "표": "CC000336", "조건": {"구분": "'휴지계수'"}, "칸": "분모"}}, {"열쇠": "P_기계주연료", "기호": "P", "아래": "기계주연료", "위": "", "이름": "EQ:{기계}.주연료", "단위": "", "연결": {"갈래": "요소", "키": "EQ:{S_기계}", "칸": "주연료"}}, {"열쇠": "P_휘발유", "기호": "P", "아래": "휘발유", "위": "", "이름": "휘발유단가", "단위": "", "연결": {"갈래": "요소", "키": "MO:{S_연료}:{S_지역}"}}, {"열쇠": "Q_기계조종원", "기호": "Q", "아래": "기계조종원", "위": "", "이름": "EQ:{기계}.조종원", "단위": "", "연결": {"갈래": "요소", "키": "EQ:{S_기계}", "칸": "조종원"}}, @@ -133,7 +145,7 @@ {"기호": "S_보정기종", "식": "만약(S_기계 == '0101-0007' 또는 S_기계 == '0101-0010' 또는 S_기계 == '0101-0012' 또는 S_기계 == '0102-0015' 또는 S_기계 == '0102-0028' 또는 S_기계 == '0102-0033' 또는 S_기계 == '0121-0004' 또는 S_기계 == '0121-0013', '불도저(19톤이상 제외)', 만약(S_기계 == '0602-0025' 또는 S_기계 == '0602-0045' 또는 S_기계 == '0602-0060' 또는 S_기계 == '0602-0080' 또는 S_기계 == '0602-0105' 또는 S_기계 == '0602-0150', '덤프트럭', 만약(S_기계 == '0301-0057' 또는 S_기계 == '0301-0076' 또는 S_기계 == '0301-0095' 또는 S_기계 == '0301-0115' 또는 S_기계 == '0301-0134' 또는 S_기계 == '0301-0153' 또는 S_기계 == '0301-0172' 또는 S_기계 == '0301-0287', '굴착기(무한궤도) 및 로더(무한궤도)', '없음')))"}, {"기호": "r_가산", "식": "만약(S_보정작업 == '해당 없음' 또는 S_보정기종 == '없음', 0, r_가산^표)"}, {"기호": "C_손료", "식": "버림(P_기계 × 1000 × ((k_기계상각비 + k_기계정비비) × (1 + r_가산 ÷ 100) + k_기계관리비) ÷ 10000000, 0)"}, - {"기호": "Q_시간환산", "식": "Q_분자^표 ÷ Q_분모^표"} + {"기호": "Q_시간환산", "식": "Q_분자^표 ÷ Q_분모^표 × Q_분자2^표 ÷ Q_분모2^표 × Q_분자3^표 ÷ Q_분모3^표"} ], "금액": [ {"이름": "주연료", "단위": "ℓ", "비목": "재료비", "수량": "P_기계주연료", "단가": "P_휘발유", "출처": "건설품셈 공통 8-4-1"}, @@ -168,7 +180,7 @@ {"열쇠": "조합잡재료율", "기호": "r", "아래": "조합잡재료", "위": "", "이름": "조합잡재료율", "단위": "", "출처": "건설품셈 공통 8-4-8 주⑤", "연결": {"갈래": "표", "표": "CC000074", "조건": {"구분": "'불도저 및 굴착기 잡재료 비율'"}, "칸": "적용"}}, {"열쇠": "연료", "기호": "S", "아래": "연료", "위": "", "이름": "연료", "단위": "", "출처": "건설품셈 공통 8-4 주①㉰", "연결": {"갈래": "요소", "키": "EQ:{S_기계}", "칸": "주연료종류"}}, {"열쇠": "직종", "기호": "S", "아래": "직종", "위": "", "이름": "직종", "단위": "", "출처": "건설품셈 공통 8-1-3 5", "연결": {"갈래": "요소", "키": "EQ:{S_기계}", "칸": "조종원직종"}}, - {"열쇠": "시간환산", "기호": "Q", "아래": "시간환산", "위": "", "이름": "시간환산", "단위": "", "출처": "건설품셈 공통 1-1-3 2(일일 작업시간 8시간)", "연결": {"갈래": "식"}}, + {"열쇠": "시간환산", "기호": "Q", "아래": "시간환산", "위": "", "이름": "시간환산", "단위": "", "출처": "질의회신 건협기술-2230(1일 8시간 · 상여 · 휴지)", "연결": {"갈래": "식"}}, {"열쇠": "r_가산^표", "기호": "r", "아래": "가산", "위": "표", "이름": "기계손료의 보정.가산비율", "단위": "%", "연결": {"갈래": "표", "표": "CC000034", "조건": {"기종": "'굴착기(무한궤도) 및 로더(무한궤도)'", "작업": "S_보정작업"}, "칸": "가산비율"}}, {"열쇠": "P_기계", "기호": "P", "아래": "기계", "위": "", "이름": "EQ:{기계}.가격", "단위": "", "연결": {"갈래": "요소", "키": "EQ:{S_기계}", "칸": "가격"}}, {"열쇠": "k_기계상각비", "기호": "k", "아래": "기계상각비", "위": "", "이름": "EQ:{기계}.상각비계수", "단위": "", "연결": {"갈래": "요소", "키": "EQ:{S_기계}", "칸": "상각비계수"}}, @@ -180,6 +192,10 @@ {"열쇠": "k_집게관리비", "기호": "k", "아래": "집게관리비", "위": "", "이름": "EQ:{집게}.관리비계수", "단위": "", "연결": {"갈래": "요소", "키": "EQ:{S_집게}", "칸": "관리비계수"}}, {"열쇠": "Q_분자^표", "기호": "Q", "아래": "분자", "위": "표", "이름": "건설기계 조종원 노임 시간 환산 계수.분자", "단위": "", "연결": {"갈래": "표", "표": "CC000336", "조건": {"구분": "'시간환산'"}, "칸": "분자"}}, {"열쇠": "Q_분모^표", "기호": "Q", "아래": "분모", "위": "표", "이름": "건설기계 조종원 노임 시간 환산 계수.분모", "단위": "", "연결": {"갈래": "표", "표": "CC000336", "조건": {"구분": "'시간환산'"}, "칸": "분모"}}, + {"열쇠": "Q_분자2^표", "기호": "Q", "아래": "분자2", "위": "표", "이름": "건설기계 조종원 노임 시간 환산 계수.분자", "단위": "", "연결": {"갈래": "표", "표": "CC000336", "조건": {"구분": "'상여계수'"}, "칸": "분자"}}, + {"열쇠": "Q_분모2^표", "기호": "Q", "아래": "분모2", "위": "표", "이름": "건설기계 조종원 노임 시간 환산 계수.분모", "단위": "", "연결": {"갈래": "표", "표": "CC000336", "조건": {"구분": "'상여계수'"}, "칸": "분모"}}, + {"열쇠": "Q_분자3^표", "기호": "Q", "아래": "분자3", "위": "표", "이름": "건설기계 조종원 노임 시간 환산 계수.분자", "단위": "", "연결": {"갈래": "표", "표": "CC000336", "조건": {"구분": "'휴지계수'"}, "칸": "분자"}}, + {"열쇠": "Q_분모3^표", "기호": "Q", "아래": "분모3", "위": "표", "이름": "건설기계 조종원 노임 시간 환산 계수.분모", "단위": "", "연결": {"갈래": "표", "표": "CC000336", "조건": {"구분": "'휴지계수'"}, "칸": "분모"}}, {"열쇠": "P_기계주연료", "기호": "P", "아래": "기계주연료", "위": "", "이름": "EQ:{기계}.주연료", "단위": "", "연결": {"갈래": "요소", "키": "EQ:{S_기계}", "칸": "주연료"}}, {"열쇠": "P_자동차용경유", "기호": "P", "아래": "자동차용경유", "위": "", "이름": "자동차용경유단가", "단위": "", "연결": {"갈래": "요소", "키": "MO:{S_연료}:{S_지역}"}}, {"열쇠": "Q_기계조종원", "기호": "Q", "아래": "기계조종원", "위": "", "이름": "EQ:{기계}.조종원", "단위": "", "연결": {"갈래": "요소", "키": "EQ:{S_기계}", "칸": "조종원"}}, @@ -189,7 +205,7 @@ {"기호": "r_가산", "식": "만약(S_보정작업 == '해당 없음', 0, r_가산^표)"}, {"기호": "C_손료", "식": "버림(P_기계 × 1000 × ((k_기계상각비 + k_기계정비비) × (1 + r_가산 ÷ 100) + k_기계관리비) ÷ 10000000, 0)"}, {"기호": "C_집게", "식": "버림(P_집게 × 1000 × (k_집게상각비 + k_집게정비비 + k_집게관리비) ÷ 10000000, 0)"}, - {"기호": "Q_시간환산", "식": "Q_분자^표 ÷ Q_분모^표"} + {"기호": "Q_시간환산", "식": "Q_분자^표 ÷ Q_분모^표 × Q_분자2^표 ÷ Q_분모2^표 × Q_분자3^표 ÷ Q_분모3^표"} ], "금액": [ {"이름": "주연료", "단위": "ℓ", "비목": "재료비", "수량": "P_기계주연료", "단가": "P_자동차용경유", "출처": "건설품셈 공통 8-4-1"}, @@ -225,7 +241,7 @@ {"열쇠": "조합잡재료율", "기호": "r", "아래": "조합잡재료", "위": "", "이름": "조합잡재료율", "단위": "", "출처": "건설품셈 공통 8-4-8 주⑤", "연결": {"갈래": "표", "표": "CC000074", "조건": {"구분": "'불도저 및 굴착기 잡재료 비율'"}, "칸": "적용"}}, {"열쇠": "연료", "기호": "S", "아래": "연료", "위": "", "이름": "연료", "단위": "", "출처": "건설품셈 공통 8-4 주①㉰", "연결": {"갈래": "요소", "키": "EQ:{S_기계}", "칸": "주연료종류"}}, {"열쇠": "직종", "기호": "S", "아래": "직종", "위": "", "이름": "직종", "단위": "", "출처": "건설품셈 공통 8-1-3 5", "연결": {"갈래": "요소", "키": "EQ:{S_기계}", "칸": "조종원직종"}}, - {"열쇠": "시간환산", "기호": "Q", "아래": "시간환산", "위": "", "이름": "시간환산", "단위": "", "출처": "건설품셈 공통 1-1-3 2(일일 작업시간 8시간)", "연결": {"갈래": "식"}}, + {"열쇠": "시간환산", "기호": "Q", "아래": "시간환산", "위": "", "이름": "시간환산", "단위": "", "출처": "질의회신 건협기술-2230(1일 8시간 · 상여 · 휴지)", "연결": {"갈래": "식"}}, {"열쇠": "r_가산^표", "기호": "r", "아래": "가산", "위": "표", "이름": "기계손료의 보정.가산비율", "단위": "%", "연결": {"갈래": "표", "표": "CC000034", "조건": {"기종": "'굴착기(무한궤도) 및 로더(무한궤도)'", "작업": "S_보정작업"}, "칸": "가산비율"}}, {"열쇠": "P_기계", "기호": "P", "아래": "기계", "위": "", "이름": "EQ:{기계}.가격", "단위": "", "연결": {"갈래": "요소", "키": "EQ:{S_기계}", "칸": "가격"}}, {"열쇠": "k_기계상각비", "기호": "k", "아래": "기계상각비", "위": "", "이름": "EQ:{기계}.상각비계수", "단위": "", "연결": {"갈래": "요소", "키": "EQ:{S_기계}", "칸": "상각비계수"}}, @@ -237,6 +253,10 @@ {"열쇠": "k_브레이커관리", "기호": "k", "아래": "브레이커관리", "위": "", "이름": "EQ:{브레이커}.관리비계수", "단위": "", "연결": {"갈래": "요소", "키": "EQ:{S_브레이커}", "칸": "관리비계수"}}, {"열쇠": "Q_분자^표", "기호": "Q", "아래": "분자", "위": "표", "이름": "건설기계 조종원 노임 시간 환산 계수.분자", "단위": "", "연결": {"갈래": "표", "표": "CC000336", "조건": {"구분": "'시간환산'"}, "칸": "분자"}}, {"열쇠": "Q_분모^표", "기호": "Q", "아래": "분모", "위": "표", "이름": "건설기계 조종원 노임 시간 환산 계수.분모", "단위": "", "연결": {"갈래": "표", "표": "CC000336", "조건": {"구분": "'시간환산'"}, "칸": "분모"}}, + {"열쇠": "Q_분자2^표", "기호": "Q", "아래": "분자2", "위": "표", "이름": "건설기계 조종원 노임 시간 환산 계수.분자", "단위": "", "연결": {"갈래": "표", "표": "CC000336", "조건": {"구분": "'상여계수'"}, "칸": "분자"}}, + {"열쇠": "Q_분모2^표", "기호": "Q", "아래": "분모2", "위": "표", "이름": "건설기계 조종원 노임 시간 환산 계수.분모", "단위": "", "연결": {"갈래": "표", "표": "CC000336", "조건": {"구분": "'상여계수'"}, "칸": "분모"}}, + {"열쇠": "Q_분자3^표", "기호": "Q", "아래": "분자3", "위": "표", "이름": "건설기계 조종원 노임 시간 환산 계수.분자", "단위": "", "연결": {"갈래": "표", "표": "CC000336", "조건": {"구분": "'휴지계수'"}, "칸": "분자"}}, + {"열쇠": "Q_분모3^표", "기호": "Q", "아래": "분모3", "위": "표", "이름": "건설기계 조종원 노임 시간 환산 계수.분모", "단위": "", "연결": {"갈래": "표", "표": "CC000336", "조건": {"구분": "'휴지계수'"}, "칸": "분모"}}, {"열쇠": "P_기계주연료", "기호": "P", "아래": "기계주연료", "위": "", "이름": "EQ:{기계}.주연료", "단위": "", "연결": {"갈래": "요소", "키": "EQ:{S_기계}", "칸": "주연료"}}, {"열쇠": "P_자동차용경유", "기호": "P", "아래": "자동차용경유", "위": "", "이름": "자동차용경유단가", "단위": "", "연결": {"갈래": "요소", "키": "MO:{S_연료}:{S_지역}"}}, {"열쇠": "Q_기계조종원", "기호": "Q", "아래": "기계조종원", "위": "", "이름": "EQ:{기계}.조종원", "단위": "", "연결": {"갈래": "요소", "키": "EQ:{S_기계}", "칸": "조종원"}}, @@ -246,7 +266,7 @@ {"기호": "r_가산", "식": "만약(S_보정작업 == '해당 없음', 0, r_가산^표)"}, {"기호": "C_손료", "식": "버림(P_기계 × 1000 × ((k_기계상각비 + k_기계정비비) × (1 + r_가산 ÷ 100) + k_기계관리비) ÷ 10000000, 0)"}, {"기호": "C_브레이커", "식": "버림(P_브레이커 × 1000 × (k_브레이커상각 + k_브레이커정비 + k_브레이커관리) ÷ 10000000, 0)"}, - {"기호": "Q_시간환산", "식": "Q_분자^표 ÷ Q_분모^표"} + {"기호": "Q_시간환산", "식": "Q_분자^표 ÷ Q_분모^표 × Q_분자2^표 ÷ Q_분모2^표 × Q_분자3^표 ÷ Q_분모3^표"} ], "금액": [ {"이름": "주연료", "단위": "ℓ", "비목": "재료비", "수량": "P_기계주연료", "단가": "P_자동차용경유", "출처": "건설품셈 공통 8-4-1"}, @@ -372,13 +392,17 @@ {"열쇠": "조종원직종", "기호": "S", "아래": "조종원직종", "위": "", "이름": "조종원직종", "단위": "", "출처": "건설품셈 공통 8-1-3 5", "연결": {"갈래": "선택", "목록": ["1048", "1049", "1050"], "값": "1048"}}, {"열쇠": "손료", "기호": "C", "아래": "손료", "위": "", "이름": "손료", "단위": "", "출처": "건설품셈 공통 8-1-5 12 · 8-1-6 4", "연결": {"갈래": "식"}}, {"열쇠": "연료", "기호": "S", "아래": "연료", "위": "", "이름": "연료", "단위": "", "출처": "건설품셈 공통 8-4 주①㉰", "연결": {"갈래": "요소", "키": "EQ:{S_기계}", "칸": "주연료종류"}}, - {"열쇠": "시간환산", "기호": "Q", "아래": "시간환산", "위": "", "이름": "시간환산", "단위": "", "출처": "건설품셈 공통 1-1-3 2(일일 작업시간 8시간)", "연결": {"갈래": "식"}}, + {"열쇠": "시간환산", "기호": "Q", "아래": "시간환산", "위": "", "이름": "시간환산", "단위": "", "출처": "질의회신 건협기술-2230(1일 8시간 · 상여 · 휴지)", "연결": {"갈래": "식"}}, {"열쇠": "P_기계", "기호": "P", "아래": "기계", "위": "", "이름": "EQ:{기계}.가격", "단위": "", "연결": {"갈래": "요소", "키": "EQ:{S_기계}", "칸": "가격"}}, {"열쇠": "k_기계상각비", "기호": "k", "아래": "기계상각비", "위": "", "이름": "EQ:{기계}.상각비계수", "단위": "", "연결": {"갈래": "요소", "키": "EQ:{S_기계}", "칸": "상각비계수"}}, {"열쇠": "k_기계정비비", "기호": "k", "아래": "기계정비비", "위": "", "이름": "EQ:{기계}.정비비계수", "단위": "", "연결": {"갈래": "요소", "키": "EQ:{S_기계}", "칸": "정비비계수"}}, {"열쇠": "k_기계관리비", "기호": "k", "아래": "기계관리비", "위": "", "이름": "EQ:{기계}.관리비계수", "단위": "", "연결": {"갈래": "요소", "키": "EQ:{S_기계}", "칸": "관리비계수"}}, {"열쇠": "Q_분자^표", "기호": "Q", "아래": "분자", "위": "표", "이름": "건설기계 조종원 노임 시간 환산 계수.분자", "단위": "", "연결": {"갈래": "표", "표": "CC000336", "조건": {"구분": "'시간환산'"}, "칸": "분자"}}, {"열쇠": "Q_분모^표", "기호": "Q", "아래": "분모", "위": "표", "이름": "건설기계 조종원 노임 시간 환산 계수.분모", "단위": "", "연결": {"갈래": "표", "표": "CC000336", "조건": {"구분": "'시간환산'"}, "칸": "분모"}}, + {"열쇠": "Q_분자2^표", "기호": "Q", "아래": "분자2", "위": "표", "이름": "건설기계 조종원 노임 시간 환산 계수.분자", "단위": "", "연결": {"갈래": "표", "표": "CC000336", "조건": {"구분": "'상여계수'"}, "칸": "분자"}}, + {"열쇠": "Q_분모2^표", "기호": "Q", "아래": "분모2", "위": "표", "이름": "건설기계 조종원 노임 시간 환산 계수.분모", "단위": "", "연결": {"갈래": "표", "표": "CC000336", "조건": {"구분": "'상여계수'"}, "칸": "분모"}}, + {"열쇠": "Q_분자3^표", "기호": "Q", "아래": "분자3", "위": "표", "이름": "건설기계 조종원 노임 시간 환산 계수.분자", "단위": "", "연결": {"갈래": "표", "표": "CC000336", "조건": {"구분": "'휴지계수'"}, "칸": "분자"}}, + {"열쇠": "Q_분모3^표", "기호": "Q", "아래": "분모3", "위": "표", "이름": "건설기계 조종원 노임 시간 환산 계수.분모", "단위": "", "연결": {"갈래": "표", "표": "CC000336", "조건": {"구분": "'휴지계수'"}, "칸": "분모"}}, {"열쇠": "P_기계주연료", "기호": "P", "아래": "기계주연료", "위": "", "이름": "EQ:{기계}.주연료", "단위": "", "연결": {"갈래": "요소", "키": "EQ:{S_기계}", "칸": "주연료"}}, {"열쇠": "P_자동차용경유", "기호": "P", "아래": "자동차용경유", "위": "", "이름": "자동차용경유단가", "단위": "", "연결": {"갈래": "요소", "키": "MO:{S_연료}:{S_지역}"}}, {"열쇠": "Q_기계조종원", "기호": "Q", "아래": "기계조종원", "위": "", "이름": "EQ:{기계}.조종원", "단위": "", "연결": {"갈래": "요소", "키": "EQ:{S_기계}", "칸": "조종원"}}, @@ -387,7 +411,7 @@ ], "식": [ {"기호": "C_손료", "식": "버림(P_기계 × 1000 × (k_기계상각비 + k_기계정비비 + k_기계관리비) ÷ 10000000, 0)"}, - {"기호": "Q_시간환산", "식": "Q_분자^표 ÷ Q_분모^표"} + {"기호": "Q_시간환산", "식": "Q_분자^표 ÷ Q_분모^표 × Q_분자2^표 ÷ Q_분모2^표 × Q_분자3^표 ÷ Q_분모3^표"} ], "금액": [ {"이름": "주연료", "단위": "ℓ", "비목": "재료비", "수량": "P_기계주연료", "단가": "P_자동차용경유", "출처": "건설품셈 공통 8-4-1"}, @@ -416,19 +440,23 @@ {"열쇠": "지역", "기호": "S", "아래": "지역", "위": "", "이름": "지역", "단위": "", "출처": "건설품셈 공통 8-1-7 5", "연결": {"갈래": "선택", "목록": ["전국평균", "전국", "서울", "경기", "강원", "충북", "충남", "전북", "경북", "경남", "부산", "제주", "대구", "인천", "대전", "울산", "세종", "전남광주"], "값": "전국평균"}}, {"열쇠": "손료", "기호": "C", "아래": "손료", "위": "", "이름": "손료", "단위": "", "출처": "건설품셈 공통 8-1-5 12 · 8-1-6 4", "연결": {"갈래": "식"}}, {"열쇠": "직종", "기호": "S", "아래": "직종", "위": "", "이름": "직종", "단위": "", "출처": "건설품셈 공통 8-1-3 5", "연결": {"갈래": "요소", "키": "EQ:{S_기계}", "칸": "조종원직종"}}, - {"열쇠": "시간환산", "기호": "Q", "아래": "시간환산", "위": "", "이름": "시간환산", "단위": "", "출처": "건설품셈 공통 1-1-3 2(일일 작업시간 8시간)", "연결": {"갈래": "식"}}, + {"열쇠": "시간환산", "기호": "Q", "아래": "시간환산", "위": "", "이름": "시간환산", "단위": "", "출처": "질의회신 건협기술-2230(1일 8시간 · 상여 · 휴지)", "연결": {"갈래": "식"}}, {"열쇠": "P_기계", "기호": "P", "아래": "기계", "위": "", "이름": "EQ:{기계}.가격", "단위": "", "연결": {"갈래": "요소", "키": "EQ:{S_기계}", "칸": "가격"}}, {"열쇠": "k_기계상각비", "기호": "k", "아래": "기계상각비", "위": "", "이름": "EQ:{기계}.상각비계수", "단위": "", "연결": {"갈래": "요소", "키": "EQ:{S_기계}", "칸": "상각비계수"}}, {"열쇠": "k_기계정비비", "기호": "k", "아래": "기계정비비", "위": "", "이름": "EQ:{기계}.정비비계수", "단위": "", "연결": {"갈래": "요소", "키": "EQ:{S_기계}", "칸": "정비비계수"}}, {"열쇠": "k_기계관리비", "기호": "k", "아래": "기계관리비", "위": "", "이름": "EQ:{기계}.관리비계수", "단위": "", "연결": {"갈래": "요소", "키": "EQ:{S_기계}", "칸": "관리비계수"}}, {"열쇠": "Q_분자^표", "기호": "Q", "아래": "분자", "위": "표", "이름": "건설기계 조종원 노임 시간 환산 계수.분자", "단위": "", "연결": {"갈래": "표", "표": "CC000336", "조건": {"구분": "'시간환산'"}, "칸": "분자"}}, {"열쇠": "Q_분모^표", "기호": "Q", "아래": "분모", "위": "표", "이름": "건설기계 조종원 노임 시간 환산 계수.분모", "단위": "", "연결": {"갈래": "표", "표": "CC000336", "조건": {"구분": "'시간환산'"}, "칸": "분모"}}, + {"열쇠": "Q_분자2^표", "기호": "Q", "아래": "분자2", "위": "표", "이름": "건설기계 조종원 노임 시간 환산 계수.분자", "단위": "", "연결": {"갈래": "표", "표": "CC000336", "조건": {"구분": "'상여계수'"}, "칸": "분자"}}, + {"열쇠": "Q_분모2^표", "기호": "Q", "아래": "분모2", "위": "표", "이름": "건설기계 조종원 노임 시간 환산 계수.분모", "단위": "", "연결": {"갈래": "표", "표": "CC000336", "조건": {"구분": "'상여계수'"}, "칸": "분모"}}, + {"열쇠": "Q_분자3^표", "기호": "Q", "아래": "분자3", "위": "표", "이름": "건설기계 조종원 노임 시간 환산 계수.분자", "단위": "", "연결": {"갈래": "표", "표": "CC000336", "조건": {"구분": "'휴지계수'"}, "칸": "분자"}}, + {"열쇠": "Q_분모3^표", "기호": "Q", "아래": "분모3", "위": "표", "이름": "건설기계 조종원 노임 시간 환산 계수.분모", "단위": "", "연결": {"갈래": "표", "표": "CC000336", "조건": {"구분": "'휴지계수'"}, "칸": "분모"}}, {"열쇠": "Q_기계조종원", "기호": "Q", "아래": "기계조종원", "위": "", "이름": "EQ:{기계}.조종원", "단위": "", "연결": {"갈래": "요소", "키": "EQ:{S_기계}", "칸": "조종원"}}, {"열쇠": "w_기계운전사", "기호": "w", "아래": "기계운전사", "위": "", "이름": "건설기계운전사노임", "단위": "", "연결": {"갈래": "요소", "키": "LB:{S_직종}"}} ], "식": [ {"기호": "C_손료", "식": "버림(P_기계 × 1000 × (k_기계상각비 + k_기계정비비 + k_기계관리비) ÷ 10000000, 0)"}, - {"기호": "Q_시간환산", "식": "Q_분자^표 ÷ Q_분모^표"} + {"기호": "Q_시간환산", "식": "Q_분자^표 ÷ Q_분모^표 × Q_분자2^표 ÷ Q_분모2^표 × Q_분자3^표 ÷ Q_분모3^표"} ], "금액": [ {"이름": "조종원", "단위": "인", "비목": "노무비", "수량": "Q_기계조종원 × Q_시간환산", "단가": "w_기계운전사", "출처": "건설품셈 공통 8-1-3 6"}, @@ -2263,11 +2291,15 @@ {"열쇠": "전력 계약종별", "기호": "S", "아래": "전력계약종별", "위": "", "이름": "전력 계약종별", "단위": "", "출처": "유가전력 한전 전기요금표 2025-01-01", "연결": {"갈래": "선택", "목록": ["일반용(갑)Ⅰ 저압", "주택용 저압"], "값": "일반용(갑)Ⅰ 저압"}}, {"열쇠": "손료", "기호": "C", "아래": "손료", "위": "", "이름": "손료", "단위": "", "출처": "건설품셈 공통 8-1-5 12 · 8-1-6 4", "연결": {"갈래": "식"}}, {"열쇠": "직종", "기호": "S", "아래": "직종", "위": "", "이름": "직종", "단위": "", "출처": "건설품셈 공통 8-1-3 5", "연결": {"갈래": "요소", "키": "EQ:{S_기계}", "칸": "조종원직종", "계약종별": "S_전력계약종별"}}, - {"열쇠": "시간환산", "기호": "Q", "아래": "시간환산", "위": "", "이름": "시간환산", "단위": "", "출처": "건설품셈 공통 1-1-3 2(일일 작업시간 8시간)", "연결": {"갈래": "식"}}, + {"열쇠": "시간환산", "기호": "Q", "아래": "시간환산", "위": "", "이름": "시간환산", "단위": "", "출처": "질의회신 건협기술-2230(1일 8시간 · 상여 · 휴지)", "연결": {"갈래": "식"}}, {"열쇠": "P_기계", "기호": "P", "아래": "기계", "위": "", "이름": "EQ:{기계}.가격", "단위": "", "연결": {"갈래": "요소", "키": "EQ:{S_기계}", "칸": "가격", "계약종별": "S_전력계약종별"}}, {"열쇠": "k_기계손료", "기호": "k", "아래": "기계손료", "위": "", "이름": "EQ:{기계}.손료계수", "단위": "", "연결": {"갈래": "요소", "키": "EQ:{S_기계}", "칸": "손료계수", "계약종별": "S_전력계약종별"}}, {"열쇠": "Q_분자^표", "기호": "Q", "아래": "분자", "위": "표", "이름": "건설기계 조종원 노임 시간 환산 계수.분자", "단위": "", "연결": {"갈래": "표", "표": "CC000336", "조건": {"구분": "'시간환산'"}, "칸": "분자"}}, {"열쇠": "Q_분모^표", "기호": "Q", "아래": "분모", "위": "표", "이름": "건설기계 조종원 노임 시간 환산 계수.분모", "단위": "", "연결": {"갈래": "표", "표": "CC000336", "조건": {"구분": "'시간환산'"}, "칸": "분모"}}, + {"열쇠": "Q_분자2^표", "기호": "Q", "아래": "분자2", "위": "표", "이름": "건설기계 조종원 노임 시간 환산 계수.분자", "단위": "", "연결": {"갈래": "표", "표": "CC000336", "조건": {"구분": "'상여계수'"}, "칸": "분자"}}, + {"열쇠": "Q_분모2^표", "기호": "Q", "아래": "분모2", "위": "표", "이름": "건설기계 조종원 노임 시간 환산 계수.분모", "단위": "", "연결": {"갈래": "표", "표": "CC000336", "조건": {"구분": "'상여계수'"}, "칸": "분모"}}, + {"열쇠": "Q_분자3^표", "기호": "Q", "아래": "분자3", "위": "표", "이름": "건설기계 조종원 노임 시간 환산 계수.분자", "단위": "", "연결": {"갈래": "표", "표": "CC000336", "조건": {"구분": "'휴지계수'"}, "칸": "분자"}}, + {"열쇠": "Q_분모3^표", "기호": "Q", "아래": "분모3", "위": "표", "이름": "건설기계 조종원 노임 시간 환산 계수.분모", "단위": "", "연결": {"갈래": "표", "표": "CC000336", "조건": {"구분": "'휴지계수'"}, "칸": "분모"}}, {"열쇠": "P_기계전력", "기호": "P", "아래": "기계전력", "위": "", "이름": "EQ:{기계}.전력", "단위": "", "연결": {"갈래": "요소", "키": "EQ:{S_기계}", "칸": "전력", "계약종별": "S_전력계약종별"}}, {"열쇠": "P_전력", "기호": "P", "아래": "전력", "위": "", "이름": "전력단가", "단위": "", "연결": {"갈래": "요소", "재료": {"이름": "전력량요금", "계약종별": "입력"}, "단위": "㎾h", "계약종별": "S_전력계약종별"}}, {"열쇠": "Q_기계조종원", "기호": "Q", "아래": "기계조종원", "위": "", "이름": "EQ:{기계}.조종원", "단위": "", "연결": {"갈래": "요소", "키": "EQ:{S_기계}", "칸": "조종원", "계약종별": "S_전력계약종별"}}, @@ -2276,7 +2308,7 @@ ], "식": [ {"기호": "C_손료", "식": "버림(P_기계 × 1000 × k_기계손료 ÷ 10000000, 0)"}, - {"기호": "Q_시간환산", "식": "Q_분자^표 ÷ Q_분모^표"} + {"기호": "Q_시간환산", "식": "Q_분자^표 ÷ Q_분모^표 × Q_분자2^표 ÷ Q_분모2^표 × Q_분자3^표 ÷ Q_분모3^표"} ], "금액": [ {"이름": "전력", "단위": "㎾h", "비목": "재료비", "수량": "P_기계전력", "단가": "P_전력", "출처": "건설품셈 공통 8-1-6 3 가"}, @@ -2409,11 +2441,15 @@ {"열쇠": "손료", "기호": "C", "아래": "손료", "위": "", "이름": "손료", "단위": "", "출처": "건설품셈 공통 8-1-5 12 · 8-1-6 4", "연결": {"갈래": "식"}}, {"열쇠": "연료", "기호": "S", "아래": "연료", "위": "", "이름": "연료", "단위": "", "출처": "건설품셈 공통 8-4 주①㉰", "연결": {"갈래": "요소", "키": "EQ:{S_기계}", "칸": "주연료종류", "계약종별": "S_전력계약종별"}}, {"열쇠": "직종", "기호": "S", "아래": "직종", "위": "", "이름": "직종", "단위": "", "출처": "건설품셈 공통 8-1-3 5", "연결": {"갈래": "요소", "키": "EQ:{S_기계}", "칸": "조종원직종", "계약종별": "S_전력계약종별"}}, - {"열쇠": "시간환산", "기호": "Q", "아래": "시간환산", "위": "", "이름": "시간환산", "단위": "", "출처": "건설품셈 공통 1-1-3 2(일일 작업시간 8시간)", "연결": {"갈래": "식"}}, + {"열쇠": "시간환산", "기호": "Q", "아래": "시간환산", "위": "", "이름": "시간환산", "단위": "", "출처": "질의회신 건협기술-2230(1일 8시간 · 상여 · 휴지)", "연결": {"갈래": "식"}}, {"열쇠": "P_EQ기계", "기호": "P", "아래": "EQ기계", "위": "", "이름": "EQ:{기계}", "단위": "", "연결": {"갈래": "요소", "키": "EQ:{S_기계}", "칸": "가격", "계약종별": "S_전력계약종별"}}, {"열쇠": "P_EQ기계2", "기호": "P", "아래": "EQ기계2", "위": "", "이름": "EQ:{기계}", "단위": "", "연결": {"갈래": "요소", "키": "EQ:{S_기계}", "칸": "손료계수", "계약종별": "S_전력계약종별"}}, {"열쇠": "Q_분자^표", "기호": "Q", "아래": "분자", "위": "표", "이름": "분자", "단위": "", "연결": {"갈래": "표", "표": "CC000336", "조건": {"구분": "'시간환산'"}, "칸": "분자"}}, {"열쇠": "Q_분모^표", "기호": "Q", "아래": "분모", "위": "표", "이름": "분모", "단위": "", "연결": {"갈래": "표", "표": "CC000336", "조건": {"구분": "'시간환산'"}, "칸": "분모"}}, + {"열쇠": "Q_분자2^표", "기호": "Q", "아래": "분자2", "위": "표", "이름": "분자", "단위": "", "연결": {"갈래": "표", "표": "CC000336", "조건": {"구분": "'상여계수'"}, "칸": "분자"}}, + {"열쇠": "Q_분모2^표", "기호": "Q", "아래": "분모2", "위": "표", "이름": "분모", "단위": "", "연결": {"갈래": "표", "표": "CC000336", "조건": {"구분": "'상여계수'"}, "칸": "분모"}}, + {"열쇠": "Q_분자3^표", "기호": "Q", "아래": "분자3", "위": "표", "이름": "분자", "단위": "", "연결": {"갈래": "표", "표": "CC000336", "조건": {"구분": "'휴지계수'"}, "칸": "분자"}}, + {"열쇠": "Q_분모3^표", "기호": "Q", "아래": "분모3", "위": "표", "이름": "분모", "단위": "", "연결": {"갈래": "표", "표": "CC000336", "조건": {"구분": "'휴지계수'"}, "칸": "분모"}}, {"열쇠": "P_EQ기계3", "기호": "P", "아래": "EQ기계3", "위": "", "이름": "EQ:{기계}", "단위": "", "연결": {"갈래": "요소", "키": "EQ:{S_기계}", "칸": "주연료", "계약종별": "S_전력계약종별"}}, {"열쇠": "P_MO연료지역", "기호": "P", "아래": "MO연료지역", "위": "", "이름": "MO:{연료}:{지역}", "단위": "", "연결": {"갈래": "요소", "키": "MO:{S_연료}:{S_지역}", "계약종별": "S_전력계약종별"}}, {"열쇠": "P_EQ기계4", "기호": "P", "아래": "EQ기계4", "위": "", "이름": "EQ:{기계}", "단위": "", "연결": {"갈래": "요소", "키": "EQ:{S_기계}", "칸": "전력", "계약종별": "S_전력계약종별"}}, @@ -2424,7 +2460,7 @@ ], "식": [ {"기호": "C_손료", "식": "버림(P_EQ기계 × 1000 × P_EQ기계2 ÷ 10000000, 0)"}, - {"기호": "Q_시간환산", "식": "Q_분자^표 ÷ Q_분모^표"} + {"기호": "Q_시간환산", "식": "Q_분자^표 ÷ Q_분모^표 × Q_분자2^표 ÷ Q_분모2^표 × Q_분자3^표 ÷ Q_분모3^표"} ], "금액": [ {"이름": "주연료", "단위": "ℓ", "비목": "재료비", "수량": "P_EQ기계3", "단가": "P_MO연료지역", "출처": "건설품셈 공통 8-4-8"}, diff --git a/resources/master_data/수식문서/수식_산림품셈_10장_자재․장비_운반.json b/resources/master_data/수식문서/수식_산림품셈_10장_자재․장비_운반.json index 4b28f9a92..cd2bfd44d 100644 --- a/resources/master_data/수식문서/수식_산림품셈_10장_자재․장비_운반.json +++ b/resources/master_data/수식문서/수식_산림품셈_10장_자재․장비_운반.json @@ -481,13 +481,17 @@ {"열쇠": "손료", "기호": "C", "아래": "손료", "위": "", "이름": "손료", "단위": "", "출처": "산림품셈 10-9 · 건설품셈 공통 8-1-5 12", "연결": {"갈래": "식"}}, {"열쇠": "비율", "기호": "r", "아래": "비율", "위": "", "이름": "비율", "단위": "", "출처": "산림품셈 10-9", "연결": {"갈래": "표", "표": "QF000330", "조건": {"구분": "'2.5톤 덤프 주 연료 대비'"}, "칸": "비율"}}, {"열쇠": "직종", "기호": "S", "아래": "직종", "위": "", "이름": "직종", "단위": "", "연결": {"갈래": "요소", "키": "EQ000069", "칸": "조종원직종"}}, - {"열쇠": "시간환산", "기호": "Q", "아래": "시간환산", "위": "", "이름": "시간환산", "단위": "", "출처": "건설품셈 공통 1-1-3 2(일일 작업시간 8시간)", "연결": {"갈래": "식"}}, + {"열쇠": "시간환산", "기호": "Q", "아래": "시간환산", "위": "", "이름": "시간환산", "단위": "", "출처": "질의회신 건협기술-2230(1일 8시간 · 상여 · 휴지)", "연결": {"갈래": "식"}}, {"열쇠": "P_덤프트럭", "기호": "P", "아래": "덤프트럭", "위": "", "이름": "EQ000655.가격", "단위": "", "연결": {"갈래": "요소", "키": "EQ000655", "칸": "가격"}}, {"열쇠": "k_덤프트럭", "기호": "k", "아래": "덤프트럭", "위": "", "이름": "EQ000069.상각비계수", "단위": "", "연결": {"갈래": "요소", "키": "EQ000069", "칸": "상각비계수"}}, {"열쇠": "k_덤프트럭2", "기호": "k", "아래": "덤프트럭2", "위": "", "이름": "EQ000069.정비비계수", "단위": "", "연결": {"갈래": "요소", "키": "EQ000069", "칸": "정비비계수"}}, {"열쇠": "k_덤프트럭3", "기호": "k", "아래": "덤프트럭3", "위": "", "이름": "EQ000069.관리비계수", "단위": "", "연결": {"갈래": "요소", "키": "EQ000069", "칸": "관리비계수"}}, {"열쇠": "Q_분자^표", "기호": "Q", "아래": "분자", "위": "표", "이름": "건설기계 조종원 노임 시간 환산 계수.분자", "단위": "", "연결": {"갈래": "표", "표": "CC000336", "조건": {"구분": "'시간환산'"}, "칸": "분자"}}, {"열쇠": "Q_분모^표", "기호": "Q", "아래": "분모", "위": "표", "이름": "건설기계 조종원 노임 시간 환산 계수.분모", "단위": "", "연결": {"갈래": "표", "표": "CC000336", "조건": {"구분": "'시간환산'"}, "칸": "분모"}}, + {"열쇠": "Q_분자2^표", "기호": "Q", "아래": "분자2", "위": "표", "이름": "건설기계 조종원 노임 시간 환산 계수.분자", "단위": "", "연결": {"갈래": "표", "표": "CC000336", "조건": {"구분": "'상여계수'"}, "칸": "분자"}}, + {"열쇠": "Q_분모2^표", "기호": "Q", "아래": "분모2", "위": "표", "이름": "건설기계 조종원 노임 시간 환산 계수.분모", "단위": "", "연결": {"갈래": "표", "표": "CC000336", "조건": {"구분": "'상여계수'"}, "칸": "분모"}}, + {"열쇠": "Q_분자3^표", "기호": "Q", "아래": "분자3", "위": "표", "이름": "건설기계 조종원 노임 시간 환산 계수.분자", "단위": "", "연결": {"갈래": "표", "표": "CC000336", "조건": {"구분": "'휴지계수'"}, "칸": "분자"}}, + {"열쇠": "Q_분모3^표", "기호": "Q", "아래": "분모3", "위": "표", "이름": "건설기계 조종원 노임 시간 환산 계수.분모", "단위": "", "연결": {"갈래": "표", "표": "CC000336", "조건": {"구분": "'휴지계수'"}, "칸": "분모"}}, {"열쇠": "P_덤프트럭2", "기호": "P", "아래": "덤프트럭2", "위": "", "이름": "EQ000069.주연료", "단위": "", "연결": {"갈래": "요소", "키": "EQ000069", "칸": "주연료"}}, {"열쇠": "P_자동차용경유", "기호": "P", "아래": "자동차용경유", "위": "", "이름": "자동차용경유단가", "단위": "", "연결": {"갈래": "요소", "키": "MO:경유:{S_지역}"}}, {"열쇠": "Q_덤프트럭", "기호": "Q", "아래": "덤프트럭", "위": "", "이름": "EQ000069.조종원", "단위": "", "연결": {"갈래": "요소", "키": "EQ000069", "칸": "조종원"}}, @@ -496,7 +500,7 @@ ], "식": [ {"기호": "C_손료", "식": "버림(P_덤프트럭 × 1000 × (k_덤프트럭 + k_덤프트럭2 + k_덤프트럭3) ÷ 10000000, 0)"}, - {"기호": "Q_시간환산", "식": "Q_분자^표 ÷ Q_분모^표"} + {"기호": "Q_시간환산", "식": "Q_분자^표 ÷ Q_분모^표 × Q_분자2^표 ÷ Q_분모2^표 × Q_분자3^표 ÷ Q_분모3^표"} ], "금액": [ {"이름": "주연료", "단위": "ℓ", "비목": "재료비", "수량": "P_덤프트럭2 × r_비율 ÷ 100", "단가": "P_자동차용경유", "출처": "산림품셈 10-9"}, diff --git a/resources/tester/test_713_4_source_match.py b/resources/tester/test_713_4_source_match.py index 80b9cb0ee..ae78c9f00 100644 --- a/resources/tester/test_713_4_source_match.py +++ b/resources/tester/test_713_4_source_match.py @@ -1,6 +1,6 @@ """713-4 지킴 — 정본 로직 원문 대조 정리(내역 15) 뒤 꼴. -조종원 = 하루 노임 × 1/8(질의회신 상여 · 휴지계수 없음) · +조종원 = 하루 노임 × 1/8 × 16/12 × 25/20(840-5 · 요구 864 경쟁사와 같게 — 713-4 의 1/8 만 꼴을 되돌림) · 지운 키(쪼갠 로직 · 쪽 표 · 터파기 작업량)를 쓰는 곳 0 · 방제 8-3 열 여덟 모두 로직 · 플랜트 13-1-1 표 하나. """ @@ -57,11 +57,11 @@ def test_지운_키는_어디에도_없음() -> None: assert not pattern.search(path.read_text(encoding="utf-8", errors="ignore")), path.name -def test_조종원_시간환산_본문대로() -> None: +def test_조종원_시간환산_세_계수() -> None: rows = _rows(cm.load()) for key in MACHINE: text = str(rows[key]["중간"]) - assert "상여계수" not in text and "휴지계수" not in text, key + assert "'시간환산'" in text and "상여계수" in text and "휴지계수" in text, key def test_방제_열_여덟_플랜트_한_표() -> None: diff --git a/resources/tester/test_796_1_formula_model.py b/resources/tester/test_796_1_formula_model.py index ddbdc6ae7..3b2401716 100644 --- a/resources/tester/test_796_1_formula_model.py +++ b/resources/tester/test_796_1_formula_model.py @@ -93,7 +93,7 @@ def test_굴착기_블록_Q_노재경(): assert item["이름식"]["우"]["꼴"] == "분수" # 1 / 작업량 cut = mcut.engine(whole(), EXCAVATOR, raw(EXCAVATOR)) assert eq["합계"] == {k: cut[k] for k in mf.BUNDLE} - assert eq["합계"]["계"] == D("2190.0") + assert eq["합계"]["계"] == D("2791.0") # 조종원 1/8 × 16/12 × 25/20(840-5 · 옛 2190) def test_지게_N_M_42917(): diff --git a/resources/tester/test_802_2_convert.py b/resources/tester/test_802_2_convert.py index ce22e5a4e..843ed3db7 100644 --- a/resources/tester/test_802_2_convert.py +++ b/resources/tester/test_802_2_convert.py @@ -57,8 +57,8 @@ def test_옵션_표본_모든_갈래_한_번_이상(result): info = result["요약"] assert info["옵션 표본"] == want == info["옵션 같음"] + info["옵션 다름"] assert ( - info["옵션 같음"] >= 18400 and info["옵션 다름"] == info["채택된 차이"] == 1 - ) # 813-2: 지운 6 문서 표본 빠져 18,438 + info["옵션 같음"] >= 18400 and info["옵션 다름"] == info["채택된 차이"] == 3 + ) # 813-2: 지운 6 문서 표본 빠져 18,438 · 840-5 조종원 환산 뒤 채택 셋 def _whole(): @@ -72,7 +72,11 @@ def test_다름_목록_까닭(result): diff = [x for x in result["대조"] if not x["같음"]] assert all(x["까닭"] != "까닭 모름" for x in diff) # 채택된 차이(요구 714-3) — 식 속 단가산출 부름 값 = 끝수 뒤 · 재료비 1 원 · 저장은 함 - assert [(x["로직"], x["옵션"], x["채택"]) for x in diff] == [("GC000100", "지역=경북", True)] + assert [(x["로직"], x["옵션"], x["채택"]) for x in diff] == [ + ("GC000091", "지역=울산", True), + ("GC000093", "지역=경북", True), + ("GC000100", "지역=전북", True), + ] # 840-5 조종원 1/8 × 16/12 × 25/20 뒤 — 같은 까닭 1 원 셋(옛 GC000100 경북 = 같아짐) assert diff[0]["까닭"].startswith(build.RAW_SUB) assert diff[0]["옛"]["재료비"] != diff[0]["새"]["재료비"] assert abs(diff[0]["새"]["재료비"] - diff[0]["옛"]["재료비"]) == 1 diff --git a/resources/tester/test_821_2_machine_save.py b/resources/tester/test_821_2_machine_save.py index cf9ebf143..1cc972cdb 100644 --- a/resources/tester/test_821_2_machine_save.py +++ b/resources/tester/test_821_2_machine_save.py @@ -82,7 +82,9 @@ def test_새_기계_단위_출처_자동_계수_서버_재계산(copy: Path) -> client = _app() lists = client.get("/api/m01/machine/lists").json() assert {"경유", "휘발유"} <= {f["value"] for f in lists["fuels"]} - assert any(j["value"] == "1048" for j in lists["jobs"]) and lists["hour"] == 0.125 + assert ( + any(j["value"] == "1048" for j in lists["jobs"]) and abs(lists["hour"] - 5 / 24) < 1e-12 + ) # 840-5 found = client.get("/api/m01/machine/find", params={"q": "굴착기 1.0㎥"}).json()["rows"] seed = next(r for r in found if r["키"] == "EQ000022") before = _files(copy) diff --git a/resources/tester/test_821_3_my_machine_screen.py b/resources/tester/test_821_3_my_machine_screen.py index 10039bce0..2e6993bde 100644 --- a/resources/tester/test_821_3_my_machine_screen.py +++ b/resources/tester/test_821_3_my_machine_screen.py @@ -42,7 +42,9 @@ def test_lists_same_as_m01(): assert set(got) == {"fuels", "jobs", "power", "hour"} for k in got: assert got[k] == want[k], k - assert got["fuels"] and got["jobs"] and got["hour"] == 0.125 + assert ( + got["fuels"] and got["jobs"] and abs(got["hour"] - 5 / 24) < 1e-12 + ) # 1/8 × 16/12 × 25/20(840-5) def test_find_mine_first_and_number(world): @@ -111,7 +113,7 @@ def _stack_doc(key, no): def test_stack_line_copy(world): """⑤ 줄 쌓기로 고른 내 기계 — 미리 셈 됨 · 저장 때 값 사본에 박힘 → - 고쳐도 금액 그대로 + 단가새값 → [단가 갱신] 바뀐 자원 1 → 지워도 마지막 값 + 지운키.""" + 고치면 개인 층은 자동 따라옴(836-2) → 지우면 저장한 사본 값.""" from M02_MasterTemplete import M02_Doc_Sk_Path as path from M02_MasterTemplete import M02_Labor_Ref as refs @@ -132,12 +134,10 @@ def test_stack_line_copy(world): assert key in [r["키"] for r in copy["단가"]] # 값 사본에 박힘 _at(me, mym.update, key, {**made["줄"], "값": {**made["줄"]["값"], "가격": 3000}}) now = _at(me, path.read, sk) - assert now["셈"]["계"] == was and now["사본"]["단가새값"] is True - job = _at(me, path.preview, sk, {"할일": "단가갱신"}) - assert [x["키"] for x in job["바뀐자원"]] == [key] and job["후"]["계"] > was + assert now["셈"]["계"] > was # 836-2 개인 층 = 자동 따라옴([단가 갱신] 없음) _at(me, mym.delete, key) gone = _at(me, path.read, sk) - assert gone["셈"]["계"] == was and gone["사본"]["지운키"] == [key] + assert gone["셈"]["계"] == was # 지운 키 = 저장한 사본 줄 값 def _mine_row(): diff --git a/resources/tester/test_821_4_import_my_machine.py b/resources/tester/test_821_4_import_my_machine.py index 21441b831..947923914 100644 --- a/resources/tester/test_821_4_import_my_machine.py +++ b/resources/tester/test_821_4_import_my_machine.py @@ -124,7 +124,8 @@ WANT = ("파워트로웰", "3.73kW") def test_generator_ours_formula(world) -> None: - """발전기 5.5kW 내 기계 시간당 노무비 = 우리 노임 ÷ 8 × 1 인(EST 의 ×16/12 ×25/20 안 씀).""" + """발전기 5.5kW 내 기계 시간당 노무비 = 우리 노임 × 1/8 × 16/12 × 25/20 × 1 인(우리 식이 한 번 · 840-5 — + EST 구성의 환산을 가져와 또 곱하지 않음 = 0.347 아님).""" me = world["personal"] me.mkdir(parents=True, exist_ok=True) body = mm.body(_main()["기계구성"][GEN], 0, _line("발전기", "5.5kW"), "main.estx") @@ -133,7 +134,7 @@ def test_generator_ours_formula(world) -> None: hour = mh.hourly(key) lb = m01.loaded()[1].numbers["LB"][_labor("일반기계운전사")] assert hour["ok"] and hour["로직"] == "GC000268" - assert abs(hour["노무비"] - float(lb["값"]) / 8) < 1 + assert abs(hour["노무비"] - float(lb["값"]) * 5 / 24) < 1 def test_make_flow(world) -> None: diff --git a/resources/tester/test_822_2_machine_hourly_all.py b/resources/tester/test_822_2_machine_hourly_all.py index b525b0bdd..8525d0c2c 100644 --- a/resources/tester/test_822_2_machine_hourly_all.py +++ b/resources/tester/test_822_2_machine_hourly_all.py @@ -39,7 +39,10 @@ POWER, POWER_CREW, ONE_FUEL, DIRECT = "GC001196", "GC001197", "GC001198", "GC001 FUEL_POWER, FUEL_POWER_CREW = "GC001200", "GC001201" NAME = "sub1임시기계822" # 안 옮긴 기계 — 822-2 전 금액(전국평균 · 기본 옵션) -KEEP = {"EQ000001": 68550, "EQ000022": 108958} +KEEP = { + "EQ000001": 92158, + "EQ000022": 132566, +} # 840-5 조종원 1/8 × 16/12 × 25/20(옛 68550 · 108958) def _rows() -> dict[str, dict]: @@ -196,7 +199,7 @@ def test_power_line_jumbo_drill(): elec = Decimal("135") * Decimal("114.4") misc = int(elec * 6) / Decimal(100) assert h["경비"] == loss and h["재료비"] == int(elec + misc) - assert h["노무비"] == int(_wage("1048") / 8) + assert h["노무비"] == int(_wage("1048") * 5 / 24) # 1/8 × 16/12 × 25/20(840-5) p = _preview("EQ000347") assert (p["노무비"], p["재료비"], p["경비"], p["계"]) == ( h["노무비"], @@ -237,10 +240,10 @@ def test_power_my_machine(world): # ⑥ · ⑦ 옮긴 기계 견본 ───────────────────────────────────────────────── def test_moved_samples(): plant = mh.hourly("EQ000188") # 아스팔트 믹싱플랜트 60t — 연료 + 조종원 2 인 - assert plant["로직"] == "GC000268" and plant["노무비"] == int(_wage("1048") * 2 / 8) + assert plant["로직"] == "GC000268" and plant["노무비"] == int(_wage("1048") * 2 * 5 / 24) assert plant["재료비"] > 0 tower = mh.hourly("EQ000165") # 타워크레인 — 건설기계운전사 조종원 줄(요구 799) - assert tower["로직"] == "GC000276" and tower["노무비"] == int(_wage("1048") / 8) + assert tower["로직"] == "GC000276" and tower["노무비"] == int(_wage("1048") * 5 / 24) spray = m01.loaded()[1].get("EQ000198") # 아스팔트 스프레어 — 직종 빈 칸 = 직종 입력 식 assert mh.pick(spray)["키"] == "GC000275" and "조종원직종" in json.dumps( mh.pick(spray)["입력"], ensure_ascii=False diff --git a/resources/tester/test_836_1_machine_cover.py b/resources/tester/test_836_1_machine_cover.py index 0cbf1cf35..363f6bf5c 100644 --- a/resources/tester/test_836_1_machine_cover.py +++ b/resources/tester/test_836_1_machine_cover.py @@ -89,7 +89,7 @@ def test_굴착기_덮음_손셈(links): return {"노무비": int(labor), "재료비": int(mats), "경비": int(exp)} fuel_amt = _cut1(fuel * oil) - crew1 = _cut1(D(1) / 8 * wage) + crew1 = _cut1(D(1) / 8 * 16 / 12 * 25 / 20 * wage) # 840-5 misc = _cut1(fuel_amt * rate / 100) want = total(crew1, fuel_amt + misc, loss(price)) got = mc.calc(_one("GC000267", NO), links) @@ -104,7 +104,7 @@ def test_굴착기_덮음_손셈(links): f20 = _cut1(20 * oil) assert run({"값": {"주연료": 20}}) == total(crew1, f20 + _cut1(f20 * rate / 100), loss(price)) assert run({"조종원": [{"키": "LB:1048", "인": 2}]}) == total( - _cut1(D(2) / 8 * wage), fuel_amt + misc, loss(price) + _cut1(D(2) / 8 * 16 / 12 * 25 / 20 * wage), fuel_amt + misc, loss(price) ) assert run({"조종원": []}) == total(0, fuel_amt + misc, loss(price)) assert run({"잡품": [{"줄": "주연료", "율": 30}]}) == total( diff --git a/resources/tester/test_839_1_machine_symbol_name.py b/resources/tester/test_839_1_machine_symbol_name.py index 409922eaa..5e6168b73 100644 --- a/resources/tester/test_839_1_machine_symbol_name.py +++ b/resources/tester/test_839_1_machine_symbol_name.py @@ -53,7 +53,9 @@ def test_16_문서_이름_전수_겹침_없음(): assert all("_" not in v and "^" not in v for v in alias.values()), (k, alias) forms |= {i for i in ids if not i.startswith("P_EQ기계")} assert not g["틀림"], (k, g["틀림"][:3]) # 식 글 왕복 = 같은 식 - assert len(forms) == 58 # 64 꼴 = 사전 58 + 「P_EQ기계~6」 읽는 칸 이름 6 + assert ( + len(forms) == 62 + ) # 68 꼴 = 사전 62(840-5 상여 · 휴지 분자 · 분모 넷 더함) + 「P_EQ기계~6」 6 def test_대표_이름(): diff --git a/resources/tester/test_masterdata_check.py b/resources/tester/test_masterdata_check.py index 275db2c64..b1738edaf 100644 --- a/resources/tester/test_masterdata_check.py +++ b/resources/tester/test_masterdata_check.py @@ -89,10 +89,10 @@ def test_굴착기(whole): whole, EXCAVATOR, {"기계": "0201-0070", "지역": "전국평균", "보정작업": "해당 없음"} ) assert [cents(got[k]) for k in ("노무비", "재료비", "경비", "계")] == [ - "35412.13", # 조종원 = 하루 노임 × 1/8(713-4 · 질의회신 상여 · 휴지계수 뺌) + "59020.21", # 조종원 = 하루 노임 × 1/8 × 16/12 × 25/20(840-5 · 옛 1/8 = 35412.13) "26130.11", "24554.00", - "86096.24", + "109704.32", ] @@ -103,7 +103,7 @@ def test_굴착기_암석_보정(whole): {"기계": "0201-0070", "지역": "전국평균", "보정작업": "암석작업(연암・보통암・경암)"}, ) assert got["중간"]["손료"] == 28322 - assert cents(got["계"]) == "89864.24" + assert cents(got["계"]) == "113472.32" # 840-5 조종원 환산 def test_불도저_덤프_로더_손료_보정(whole): @@ -148,10 +148,10 @@ def test_노체포설(whole): assert mf.run(whole, "GF000118", {})["결과"] == Decimal("24.255") got = mf.run(whole, "GF000119", {"지역": "전국평균", "보정작업": "해당 없음"}) assert [cents(got[k]) for k in ("노무비", "재료비", "경비", "계")] == [ - "1459.99", # 조종원 = 하루 노임 × 1/8(713-4) + "2433.32", # 조종원 = 하루 노임 × 1/8 × 16/12 × 25/20(840-5) "1077.31", "1012.33", - "3549.63", + "4522.96", ] @@ -191,7 +191,7 @@ def test_굴착기_부착용_집게(whole): {"기계": "0201-0060", "집게": "7206-0070", "지역": "전국평균", "보정작업": "해당 없음"}, ) assert got["중간"]["집게손료"] == 5786 - assert cents(got["계"]) == "86467.61" + assert cents(got["계"]) == "110075.69" # 840-5 조종원 환산 def test_불도저_운반_작업량(whole): From 453dd4fe129a83b38bde8201b5bd7332df202640 Mon Sep 17 00:00:00 2001 From: umsangdon Date: Sun, 15 Nov 2026 16:31:24 +0900 Subject: [PATCH 2/3] =?UTF-8?q?test(840-5):=20=EC=A4=84=20=EC=8C=93?= =?UTF-8?q?=EA=B8=B0=20GF000099=20=EA=B8=88=EC=95=A1=20=EA=B8=B0=EB=8C=80?= =?UTF-8?q?=EA=B0=92=20=3D=20=EC=A1=B0=EC=A2=85=EC=9B=90=201/8=20=C3=97=20?= =?UTF-8?q?16/12=20=C3=97=2025/20=20=EB=92=A4=20=EA=B0=92(5286=20=E2=86=92?= =?UTF-8?q?=205651=20=C2=B7=20Cm=20=EA=B3=A0=EC=B9=A8=205434=20=E2=86=92?= =?UTF-8?q?=205840)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 낡은 시험 고침: 840-5 조종원 환산으로 굴착기 시간당이 올라 측구터파기 금액이 바뀜 Co-Authored-By: Claude Opus 5.5 --- resources/tester/test_804_2_line_stack.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/resources/tester/test_804_2_line_stack.py b/resources/tester/test_804_2_line_stack.py index 67385f733..f48cc2c10 100644 --- a/resources/tester/test_804_2_line_stack.py +++ b/resources/tester/test_804_2_line_stack.py @@ -191,9 +191,9 @@ def test_틀풀기_전수_금액같음_Cm고침(world): "펼침", [{**world["cases"]["GF000099"], "기호": "Q_작업", "고침": [{"기호": "Cm", "글": "20"}]}], )[0][0] - assert one["전"]["계"] == 5286 and one["후"]["계"] == 5286 + assert one["전"]["계"] == 5651 and one["후"]["계"] == 5651 # 840-5 조종원 환산(옛 5286) assert one["옮김"][-1] == "Q_작업" and {"q", "K", "f", "E", "Cm"} <= set(one["옮김"]) - assert one["고친"]["계"] == 5434 # Cm 18 → 20 = Q 58.212 → 52.391 + assert one["고친"]["계"] == 5840 # Cm 18 → 20 = Q 58.212 → 52.391(840-5 · 옛 5434) gone = _ts("펼침", [{**world["cases"]["GF000099"], "기호": "Q_작업", "지움": ["Cm"]}])[0][0] assert gone["고친"]["계"] is None and "정의 필요" in gone["표지"] # 지워도 오류 없음 @@ -217,7 +217,7 @@ def test_저장_쌓기_다시읽어같음(client, tmp_path): laid = got["차림문서"] res = client.post("/api/m01/logic/doc/save", json={"문서": laid}) assert res.status_code == 200, res.text - assert res.json()["셈"]["계"] == read["셈"]["계"] == 5286 + assert res.json()["셈"]["계"] == read["셈"]["계"] == 5651 # 840-5(옛 5286) again = client.get("/api/m01/logic/doc", params={"key": "GF000099"}).json() assert ( again["문서"]["쌓기"] == laid["쌓기"] and _ts("왕복", [{"문서": again["문서"]}])[0]["같음"] From 207be5bd71d61275f504be8d414548f761905333 Mon Sep 17 00:00:00 2001 From: umsangdon Date: Sun, 15 Nov 2026 16:40:08 +0900 Subject: [PATCH 3/3] =?UTF-8?q?feat(840-4):=20=EB=82=B4=20=EC=9E=AC?= =?UTF-8?q?=EB=A3=8C=20=3D=20=EC=9E=AC=EB=A3=8C=20DB(M01)=EC=97=90=20?= =?UTF-8?q?=EC=97=86=EB=8A=94=20=EC=9E=90=EC=9E=AC=EB=A5=BC=20=EB=93=B1?= =?UTF-8?q?=EB=A1=9D=ED=95=98=EB=8A=94=20=EC=9E=90=EB=A6=AC=20=E2=80=94=20?= =?UTF-8?q?=EA=B0=9C=EC=9D=B8=EA=B4=80=EB=A6=AC=20=E2=80=BA=20=EC=9E=90?= =?UTF-8?q?=EC=9E=AC=20=EB=93=B1=EB=A1=9D=20=EC=B9=B8(=EB=A7=8C=EB=93=A4?= =?UTF-8?q?=EA=B8=B0=20=C2=B7=20=EA=B3=A0=EC=B9=98=EA=B8=B0=20=C2=B7=20?= =?UTF-8?q?=EC=A7=80=EC=9A=B0=EA=B8=B0)=20=C2=B7=20M01=20=EA=B3=BC=20?= =?UTF-8?q?=EA=B0=99=EC=9D=80=20=EC=9D=B4=EB=A6=84=20=C2=B7=20=EA=B7=9C?= =?UTF-8?q?=EA=B2=A9=20=C2=B7=20=EB=8B=A8=EC=9C=84=20=EB=A7=89=EC=9D=8C=20?= =?UTF-8?q?=C2=B7=20=EB=8B=A8=EA=B0=80=EC=82=B0=EC=B6=9C=20=EC=9E=AC?= =?UTF-8?q?=EB=A3=8C=20=EA=B3=A0=EB=A5=B4=EA=B8=B0=20=E3=80=8C=EB=82=B4=20?= =?UTF-8?q?=EC=9E=AC=EB=A3=8C=E3=80=8D=20=C2=B7=20=EA=B0=92=20=EB=B0=94?= =?UTF-8?q?=EA=BE=B8=EB=A9=B4=20=EC=9E=90=EB=8F=99=20=EB=94=B0=EB=9D=BC?= =?UTF-8?q?=EC=98=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 서버 `M02_Material_Own`: M01 자재품목과 이름 · 규격 · 단위 같음 = 409(빈칸 · 대소문자 · ㎏ = kg 무시) · 고치기 · 지우기(쓰는 곳 있으면 409 · 밑줄 폴더 보관본 뺌) · 목록 줄마다 쓰는 곳 - 셈 색인: 단가산출 사본 색인 · 사본 모으기 · 빈틈 채우기에 내 재료 줄을 내 기계처럼 얹음 → 개인 층 단가산출은 값 바뀌면 836-2 길로 따라옴 · 프로젝트 층 = 사본 값 - 화면: 개인관리 › 자재 위 「내 재료」 표 + 고침 칸(`M02_MyMaterial_View` · `M02_MyMaterial_Api`) · 아래 「값을 바로 적은 재료」 모아 보기는 읽기 그대로 · 찾아 넣기 재료 탭 M01 위 「내 재료」 묶음(줄 쌓기 개인 층) - M01 재료를 본떠 값만 바꾼 내 재료를 만드는 길 = 없었음(걷을 것 없음) 낡은 시험 고침: test_m02_material_own 목록 줄에 「쓰는곳」 칸 Co-Authored-By: Claude Opus 5.5 --- A00_Common/line_stack_pick.ts | 2 + M02_MasterTemplete/M02_Doc_Gap.py | 7 +- M02_MasterTemplete/M02_Item_Prices.py | 8 +- M02_MasterTemplete/M02_Labor_Router.py | 37 +++- .../M02_MasterTemplete_Find_Pick.ts | 22 +++ M02_MasterTemplete/M02_Material_Own.py | 146 +++++++++++++- M02_MasterTemplete/M02_MyItems_View.ts | 3 + M02_MasterTemplete/M02_MyMachine_Text.ts | 2 +- M02_MasterTemplete/M02_MyMaterial_Api.ts | 45 +++++ M02_MasterTemplete/M02_MyMaterial_View.ts | 185 ++++++++++++++++++ M02_MasterTemplete/M02_Sangeun_Copy.py | 5 +- resources/tester/test_840_4_my_material.py | 78 ++++++++ resources/tester/test_m02_material_own.py | 2 +- ui_template/ui_template_locale_m2.ts | 1 + 14 files changed, 527 insertions(+), 16 deletions(-) create mode 100644 M02_MasterTemplete/M02_MyMaterial_Api.ts create mode 100644 M02_MasterTemplete/M02_MyMaterial_View.ts create mode 100644 resources/tester/test_840_4_my_material.py diff --git a/A00_Common/line_stack_pick.ts b/A00_Common/line_stack_pick.ts index 4dbc376b0..c9db700a0 100644 --- a/A00_Common/line_stack_pick.ts +++ b/A00_Common/line_stack_pick.ts @@ -4,6 +4,7 @@ * 찾아 넣기 = M02 [항목 추가] 창 그대로(`openFindPicker` · 탭 M01 전체 · 찾기 칸 · 표) — 줄 갈래마다 그 종류만 * 인력(노임 LB) · 재료(자재 MT) · 기계(EQ → 시간당 경비 로직 + 기계='원문번호') · 다른 단가산출(M01 단가산출 로직) * 기계 + M02 개인 층(821-3) = 품셈 기계 위 「내 기계」 묶음 · 머리 [새 기계 만들기] — 고름 = 로직 + 기계='개인 EQP…' + * 재료 + M02 개인 층(840-4) = M01 재료 위 「내 재료」 묶음 — 고름 = 요소 PM 키(M01 재료와 같은 꼴) * M01 단가산출 기초 줄 쌓기 = 「M01 전체」 탭만 — M01 은 원천 기준값이라 M02 단가산출 · 일위대가를 부르지 않음(요구 680-1) * M02 단가산출 줄 쌓기(`take` 있음) · 다른 단가산출 = 탭 [M01 전체 | M02 단가산출 | 일위대가](위 층 · 자기 SK 뺌) — * 고름 = 복사해 펼침(`take(src)` · src = M01 키 | `참조|층|주인|키`) · 참조 줄 없음(요구 681) @@ -58,6 +59,7 @@ export function openStackPick( title: `${lt("Find_Title")} — ${lt(copy ? "K_복사" : (`K_${kind}` as never))}`, kinds: copy ? ["logic", "sk", "pre"] : [want], // M01 = M01 전체만(680-1) mine: kind === "기계" && m02?.layer === "personal" ? { owner: m02.owner } : undefined, + mineMat: kind === "재료" && m02?.layer === "personal" ? { owner: m02.owner } : undefined, // 840-4 내 재료 query: query.trim() || undefined, ref: copy ? (r) => diff --git a/M02_MasterTemplete/M02_Doc_Gap.py b/M02_MasterTemplete/M02_Doc_Gap.py index af21c0afc..1e20667d3 100644 --- a/M02_MasterTemplete/M02_Doc_Gap.py +++ b/M02_MasterTemplete/M02_Doc_Gap.py @@ -12,6 +12,7 @@ from __future__ import annotations from typing import Any from M02_MasterTemplete import M02_Doc_Copy as dc +from M02_MasterTemplete import M02_Material_Own as own from M02_MasterTemplete import M02_MyMachine as mym from M02_MasterTemplete import M02_Sangeun_Copy as skc @@ -46,7 +47,7 @@ def _have(copy: dict) -> set[str]: def gaps(copy: dict) -> list[str]: - """값 사본에 없는데 문서 셈이 읽은 M01 줄 키 + 문서의 내 기계 키(셈 틀림 = 읽은 데까지).""" + """값 사본에 없는데 문서 셈이 읽은 M01 줄 키 + 문서의 내 기계 · 내 재료(840-4) 키(셈 틀림 = 읽은 데까지).""" if "로직" not in copy or not isinstance(copy.get("문서"), dict): return [] seen = _Seen(dc.over(copy)) @@ -55,7 +56,7 @@ def gaps(copy: dict) -> list[str]: except dc._ERR: pass have = _have(copy) - return sorted({*seen.keys, *mym.keys_in(copy["문서"])} - have) + return sorted({*seen.keys, *mym.keys_in(copy["문서"]), *own.keys_in(copy["문서"])} - have) def fill(copy: dict) -> dict: @@ -76,7 +77,7 @@ def fill_imported(sk: dict, doc: Any) -> dict: return sk seen = _Seen(dim._index(sk, None, doc)) dim.run(doc, sk, index=seen) # 셈 틀림 = 읽은 데까지(run 이 받음) - keys = sorted({*seen.keys, *mym.keys_in(doc)} - _have(sk[skc.KEY])) + keys = sorted({*seen.keys, *mym.keys_in(doc), *own.keys_in(doc)} - _have(sk[skc.KEY])) if not keys: return sk return {**sk, skc.KEY: skc.grow(sk[skc.KEY], skc.snap([], keys))} diff --git a/M02_MasterTemplete/M02_Item_Prices.py b/M02_MasterTemplete/M02_Item_Prices.py index 1c3c33176..4a53c9aa9 100644 --- a/M02_MasterTemplete/M02_Item_Prices.py +++ b/M02_MasterTemplete/M02_Item_Prices.py @@ -241,8 +241,12 @@ def collect( `extra` = 마스터 밖 로직 줄(굳힌 단가산출 719-1) — 그 줄이 닿는 것만 모음(줄은 안 담음) · `over` = M02 로직 사본 줄(795-4) — 같은 키는 M01 줄 대신 · M01 에 없는 키 = `COPY_FILE`.""" files, whole = master() - whole = mym.whole(whole) # 지금 층 내 기계 줄(821-1) — 없으면 M01 그대로 - own = {str(r.get("키")): r for r in mym.added(whole)} + from M02_MasterTemplete import M02_Material_Own as mat + + whole = mat.whole( + mym.whole(whole) + ) # 지금 층 내 기계(821-1) · 내 재료(840-4) — 없으면 M01 그대로 + own = {str(r.get("키")): r for r in (*mym.added(whole), *mat.added(whole))} mine = {str(r.get("키")): r for r in over or []} if mine: from M02_MasterTemplete import M02_Sangeun_Copy as skc diff --git a/M02_MasterTemplete/M02_Labor_Router.py b/M02_MasterTemplete/M02_Labor_Router.py index 5db9ebb66..bcf92e6da 100644 --- a/M02_MasterTemplete/M02_Labor_Router.py +++ b/M02_MasterTemplete/M02_Labor_Router.py @@ -196,10 +196,13 @@ def own_materials( owner: str | None = None, session: Session = Depends(verify_session), ) -> dict: - """개인 자재 목록(148-2) — 개인 층만 · 회사 · 마스터 = 빈 목록.""" + """개인 자재 목록(148-2) — 개인 층만 · 회사 · 마스터 = 빈 목록 · 줄마다 쓰는 곳(840-4).""" if layer != "personal": return {"줄": []} - return {"줄": own.rows(_root(session, layer, owner))} + folder = _root(session, layer, owner) + rows = own.rows(folder) + used = own.usage(folder, [str(r.get("키")) for r in rows]) + return {"줄": [{**r, "쓰는곳": used[str(r.get("키"))]} for r in rows]} @router.post("/layers/{layer}/materials") @@ -215,3 +218,33 @@ def new_material( raise HTTPException(status_code=400, detail="신규 자재는 개인 층에서만") folder = _root(session, layer, owner, write=True) return _call(folder, own.add, folder, body) + + +@router.put("/layers/{layer}/materials/{key}") +def edit_material( + layer: Layer, + key: str, + body: dict[str, Any], + owner: str | None = None, + session: Session = Depends(verify_session), +) -> dict: + """내 재료 고침(840-4) — `{이름, 규격, 단위, 값}` · 키 그대로 · 개인 층만.""" + if layer != "personal": + raise HTTPException(status_code=400, detail="내 재료는 개인 층에서만") + folder = _root(session, layer, owner, write=True) + return _call(folder, own.update, folder, key, body) + + +@router.delete("/layers/{layer}/materials/{key}") +def drop_material( + layer: Layer, + key: str, + owner: str | None = None, + session: Session = Depends(verify_session), +) -> dict: + """내 재료 지움(840-4) — 쓰는 곳 있으면 409 · 개인 층만.""" + if layer != "personal": + raise HTTPException(status_code=400, detail="내 재료는 개인 층에서만") + folder = _root(session, layer, owner, write=True) + _call(folder, own.remove, folder, key) + return {"ok": True} diff --git a/M02_MasterTemplete/M02_MasterTemplete_Find_Pick.ts b/M02_MasterTemplete/M02_MasterTemplete_Find_Pick.ts index c6e0e57dd..45ebc0f92 100644 --- a/M02_MasterTemplete/M02_MasterTemplete_Find_Pick.ts +++ b/M02_MasterTemplete/M02_MasterTemplete_Find_Pick.ts @@ -28,6 +28,7 @@ import { t as L } from "@ui/ui_template_locale"; import { aliasNote, matchLogic } from "@ui/ui_template_logic_find"; import { openNewMaterial } from "./M02_Material_Pick"; import { listMyMachines, mineElement, type MyMachineRow } from "./M02_MyMachine_Api"; +import { listMyMaterials, type MyMaterial } from "./M02_MyMaterial_Api"; import { mt } from "./M02_MyMachine_Text"; import { openModal } from "@ui/ui_template_modal"; import { createTabs } from "@ui/ui_template_tabs"; @@ -91,6 +92,8 @@ export interface FindPickOptions { oil?: (e: M01Element) => void; /** 821-3 개인 층 내 기계 묶음(`machine` 과 함께) — 주인(null = 자기 층) */ mine?: { owner: string | null }; + /** 840-4 개인 층 내 재료(PM) 묶음 — 재료 고름 위 · 주인(null = 자기 층) */ + mineMat?: { owner: string | null }; } /** 727-1 기초마다 한 줄(옛 창 `Labor_Pick` 도 씀) — 규격 칸 = 「옵션 N」 */ @@ -194,6 +197,7 @@ export function openFindPicker(opt: FindPickOptions): void { let machines: PickItem[] = []; let oils: PickItem[] = []; // 785-3 유가전력 MO let mines: MyMachineRow[] = []; // 821-3 내 기계 + let mats: MyMaterial[] = []; // 840-4 내 재료 const lists: Record<"sk" | "pre", SangeunRow[] | null> = { sk: null, pre: null }; let shown: Line[] = []; let seq = 0; @@ -312,6 +316,16 @@ export function openFindPicker(opt: FindPickOptions): void { }; }); }; + /** 840-4 내 재료 줄 — M01 재료처럼 고름(코드 = PM 키 · 값 = 내 목록 값) */ + const matLines = (q: string): Line[] => { + const words = q.toLowerCase().split(/\s+/).filter(Boolean); + return elementLines( + "재료", + mats + .filter((r) => words.every((w) => `${r.이름} ${r.규격} ${r.키}`.toLowerCase().includes(w))) + .map((r) => ({ ref: r.키, 구분: L("M02_FindMyMaterial"), ...r })), + ).map((x) => ({ ...x, 출처: x.출처.replace(/^M01 /, "") })); + }; const docLines = (rows: SangeunRow[], kind: string): Line[] => rows.map((x) => ({ 품명: x.제목 || x.이름, @@ -329,6 +343,7 @@ export function openFindPicker(opt: FindPickOptions): void { shown = [ ...(ok("logic") ? logicLines(q) : []), ...(ok("labor") ? elementLines("인력", people) : []), + ...(opt.mineMat && ok("material") ? matLines(q) : []), ...(ok("material") ? elementLines("재료", goods) : []), ...(opt.oil && ok("oil") ? elementLines("유가", oils) : []), ...(opt.machine && opt.mine && ok("machine") ? mineLines(q) : []), @@ -456,6 +471,13 @@ export function openFindPicker(opt: FindPickOptions): void { draw(); }) .catch(fail); + if (opt.mineMat && ok("material")) + void listMyMaterials(opt.mineMat.owner) + .then((rows) => { + mats = rows; + draw(); + }) + .catch(fail); void upper(opt.layer, opt.owner) .then((scopes) => Promise.all( diff --git a/M02_MasterTemplete/M02_Material_Own.py b/M02_MasterTemplete/M02_Material_Own.py index 3eb6bab69..b5885a4cb 100644 --- a/M02_MasterTemplete/M02_Material_Own.py +++ b/M02_MasterTemplete/M02_Material_Own.py @@ -6,12 +6,16 @@ `master_material.element`). 품번 = `PM` + 6자리 — 모든 사람 통틀어 한 줄 번호(모든 개인 층 파일 가장 큰 번호 + 1 · `store._LOCK` 안 · 화면이 보낸 키는 안 받음). 신규 자재는 개인 층에서만 (회사 · 마스터 층 = 마스터 자재만). +840-4 내 재료 = M01 재료 DB 에 없는 자재를 등록하는 자리 — M01 과 이름 · 규격 · 단위가 같으면 막음(409) · +고치기 · 지우기(쓰는 곳 있으면 막음) · 단가산출 셈 색인에 M01 재료처럼 얹음(`whole` · `M02_Sangeun_Copy.whole_`) +→ 프로젝트 층 밖 SK 는 값이 바뀌면 자동 따라옴(836-2 `M02_Sk_AutoRenew`) · 프로젝트 층 = 사본 값. """ from __future__ import annotations import json import re +import unicodedata from pathlib import Path from typing import Any @@ -22,6 +26,7 @@ from M02_MasterTemplete import M02_Template_Layers as layers FILE = "개인자재.json" PREFIX = "PM" KEY = re.compile(rf"^{PREFIX}(\d{{6}})$") +FIND = re.compile(rf"(? float | int | None: return int(got) if got.is_integer() else got -def add(folder: str | Path, body: dict[str, Any]) -> dict[str, Any]: - """새 개인 자재 한 줄 → 그 줄(키는 서버) · 품명 · 단위 빈 = 400 · - 같은 품명 + 규격 + 단위 = 409.""" +def _norm(v: Any) -> str: + """빈칸 · 대소문자 · 겹낱자(㎏ = kg · ㎥ = m3) 무시.""" + return unicodedata.normalize("NFKC", " ".join(_text(v).split())).lower() + + +def m01_same(row: dict[str, Any]) -> dict[str, Any] | None: + """M01 재료 DB(자재품목 MT)에서 이름 · 규격 · 단위가 같은 줄(빈칸 · 대소문자 무시).""" + from M01_MasterData import M01_MasterData_Store as m01 + + want = tuple(_norm(row.get(k)) for k in COLS) + for r in (m01.loaded()[1].index.get("MT") or {}).values(): + if tuple(_norm(r.get(k)) for k in COLS) == want: + return r + return None + + +def _checked(body: dict[str, Any], others: list[dict[str, Any]]) -> dict[str, Any]: + """품명 · 단위 빈 = 400 · 내 목록 같은 줄 · M01 재료 DB 같은 줄 = 409.""" row = {k: _text(body.get(k)) for k in COLS} if not row["이름"] or not row["단위"]: raise store.StoreError(400, "품명 · 단위는 꼭 적음") row["값"] = _value(body.get("값")) + if any(all(_norm(x.get(k)) == _norm(row[k]) for k in COLS) for x in others): + raise store.StoreError(409, f"이미 있는 자재 「{row['이름']} {row['규격']}」") + if m01_same(row): + raise store.StoreError( + 409, + f"재료 DB 에 있는 자재 「{row['이름']} {row['규격']}」 — 재료 DB 자재를 고르고 " + "값은 단가산출 안 재료 줄에서 바꿈", + ) + return row + + +def add(folder: str | Path, body: dict[str, Any]) -> dict[str, Any]: + """새 개인 자재 한 줄 → 그 줄(키는 서버) · 품명 · 단위 빈 = 400 · + 같은 품명 + 규격 + 단위(내 목록 · M01 재료 DB) = 409.""" with store._LOCK: doc = read(folder) - if any(all(_text(x.get(k)) == row[k] for k in COLS) for x in doc["줄"]): - raise store.StoreError(409, f"이미 있는 자재 「{row['이름']} {row['규격']}」") + row = _checked(body, doc["줄"]) got = {"키": f"{PREFIX}{_last() + 1:06d}", **row} doc["줄"].append(got) atomic_write_json(Path(folder) / FILE, doc) return got +def update(folder: str | Path, key: str, body: dict[str, Any]) -> dict[str, Any]: + """내 재료 한 줄 고침(키 그대로) — 없는 키 = 404 · 검사는 새로 만들기와 같음.""" + with store._LOCK: + doc = read(folder) + at = next((i for i, r in enumerate(doc["줄"]) if r.get("키") == key), None) + if at is None: + raise store.StoreError(404, f"없는 내 재료 「{key}」") + row = _checked(body, [r for i, r in enumerate(doc["줄"]) if i != at]) + doc["줄"][at] = got = {"키": key, **row} + atomic_write_json(Path(folder) / FILE, doc) + return got + + +def _aside(path: Path, folder: str | Path) -> bool: + """밑줄 · 점 폴더(「_합친것」 등 파생 · 보관) — 쓰는 곳 아님.""" + return any(x.startswith(("_", ".")) for x in path.relative_to(folder).parts[:-1]) + + +def usage(folder: str | Path, keys: list[str]) -> dict[str, list[dict[str, Any]]]: + """그 층에서 키마다 쓰는 문서(단가산출 · 일위대가 …) {키: [{종류, 키, 이름}]} — 파일은 한 번씩 읽음.""" + out: dict[str, list[dict[str, Any]]] = {k: [] for k in keys} + for path in sorted(Path(folder).rglob("*.json")): # 종류 폴더 · 그 밑 폴더 — 내 목록 파일 뺌 + if path.name == FILE or _aside(path, folder): + continue + try: + text = path.read_text(encoding="utf-8") + except OSError: + continue + hit = [k for k in keys if k in text] + if not hit: + continue + try: + doc = json.loads(text) + except ValueError: + continue + name = (doc.get("이름") if isinstance(doc, dict) else None) or path.stem + for k in hit: + out[k].append({"종류": path.parent.name, "키": path.stem, "이름": name}) + return out + + +def uses(folder: str | Path, key: str) -> list[dict[str, Any]]: + return usage(folder, [key])[key] + + +def remove(folder: str | Path, key: str) -> None: + """내 재료 지움 — 쓰는 문서가 있으면 409(그 줄을 먼저 바꿈).""" + with store._LOCK: + doc = read(folder) + if not any(r.get("키") == key for r in doc["줄"]): + raise store.StoreError(404, f"없는 내 재료 「{key}」") + if used := uses(folder, key): + names = " · ".join(str(u["이름"]) for u in used[:3]) + raise store.StoreError(409, f"쓰는 곳 {len(used)} — {names} · 그 줄을 먼저 바꿈") + doc["줄"] = [r for r in doc["줄"] if r.get("키") != key] + atomic_write_json(Path(folder) / FILE, doc) + + +# ── 840-4 셈 색인(단가산출이 M01 재료처럼 읽음) ───────────────────────── +_MEMO: dict[str, tuple] = {} + + +def whole(base: Any) -> Any: + """셈 색인 + 지금 층(개인) 내 재료 줄 — 파일 · 바탕이 그대로면 같은 객체 · 개인 층 아님 = 바탕 그대로.""" + from decimal import Decimal + + from M02_MasterTemplete import M02_Sangeun_Copy as skc + + folder = store.root() + path = folder / FILE + try: + st = path.stat() + except OSError: + return base + if not _personal(folder): + return base + sig = (st.st_mtime_ns, st.st_size) + got = _MEMO.get(str(path)) + if got and got[0] == sig and got[1] is base: + return got[2] + raw = json.dumps(rows(folder), ensure_ascii=False) + mine = [ + r + for r in json.loads(raw, parse_float=Decimal, parse_int=Decimal) + if is_own(str(r.get("키"))) + ] + out = skc.Copy(base, mine, ("내재료", sig)) if mine else base + if len(_MEMO) > 16: # ponytail: 통째 비움 · 층이 많아지면 LRU 로 + _MEMO.clear() + _MEMO[str(path)] = (sig, base, out) + return out + + +def keys_in(doc: Any) -> list[str]: + """문서 안 내 재료 키(요소 이음 「PM…」 · 사본 빈틈 찍기).""" + return sorted(set(FIND.findall(json.dumps(doc, ensure_ascii=False, default=str)))) + + +def added(index: Any) -> list[dict]: + """`whole` 이 얹은 내 재료 줄(사본 모으기 `collect` 가 마스터 밖 줄로 담음).""" + return list((getattr(index, "index", {}).get(PREFIX) or {}).values()) + + # ── 148-3 — 저장 검사 · 셈 · 사본 · 층 사이 ───────────────────────────── BUNDLE = "재료_개인자재.json" # 사본 `단가.json` 안 이름 diff --git a/M02_MasterTemplete/M02_MyItems_View.ts b/M02_MasterTemplete/M02_MyItems_View.ts index 3cf894d95..0fe43446f 100644 --- a/M02_MasterTemplete/M02_MyItems_View.ts +++ b/M02_MasterTemplete/M02_MyItems_View.ts @@ -4,6 +4,7 @@ * 줄 = 그 층 단가산출 줄에 값을 바로 적은 재료(우리 자재에 안 이어진 것 · 서버 `M02_MyMachine_Look.items`) * 칸 = 이름 · 규격 · 단위 · 값 · 쓰는 단가산출 수 · 같은 이름 수(같은 이름 묶음 = 이름 차례로 붙어 보임) * 줄 누름 = 아래 쓰는 단가산출 목록 → 누르면 그 단가산출로(고치기는 거기서 · 여기선 저장 없음) + * 위 = 「내 재료」 등록 칸(840-4 · `M02_MyMaterial_View` — 재료 DB 에 없는 자재 등록) · 이 모아 보기는 그 아래 읽기 * ========================================================================== */ import { createDataTable } from "@ui/ui_template_data_table"; @@ -11,6 +12,7 @@ import { createInputField, el } from "@ui/ui_template_elements"; import { createFindList } from "@ui/ui_template_find_list"; import { createText } from "@ui/ui_template_text"; import { listMyItems, type MyItem, type Use } from "./M02_MyMachine_Api"; +import { buildMyMaterialBlock } from "./M02_MyMaterial_View"; import { mt } from "./M02_MyMachine_Text"; export interface MyItemsViewOptions { @@ -85,6 +87,7 @@ export function buildMyItemsView(opt: MyItemsViewOptions): HTMLElement { className: "m02-mymach-view", attrs: { "data-my-items": "" }, children: [ + buildMyMaterialBlock({ owner: opt.owner }), el("div", { className: "ui-toolbar", children: [search.root, hint] }), createText(mt("Items_Note"), "muted"), fail, diff --git a/M02_MasterTemplete/M02_MyMachine_Text.ts b/M02_MasterTemplete/M02_MyMachine_Text.ts index b4f313ab9..b447bcec5 100644 --- a/M02_MasterTemplete/M02_MyMachine_Text.ts +++ b/M02_MasterTemplete/M02_MyMachine_Text.ts @@ -74,7 +74,7 @@ const TEXT = { Why_Company: "개인용으로 복사해서 씀", // 내 품목 Items_Note: - "단가산출 줄에 값을 바로 적은 재료(우리 자재에 안 이어진 것) — 고치기는 그 단가산출에서 · 줄 누르면 쓰는 단가산출", + "단가산출 줄에 값을 바로 적은 재료(재료 DB · 내 재료에 안 이어진 것 · 읽기만) — 고치기는 그 단가산출에서 · 줄 누르면 쓰는 단가산출", Items_None: "내 품목 없음", Value: "값(원)", Unit: "단위", diff --git a/M02_MasterTemplete/M02_MyMaterial_Api.ts b/M02_MasterTemplete/M02_MyMaterial_Api.ts new file mode 100644 index 000000000..477530f6d --- /dev/null +++ b/M02_MasterTemplete/M02_MyMaterial_Api.ts @@ -0,0 +1,45 @@ +/* ============================================================================= + * M02_MyMaterial_Api.ts + * M02 내 재료(개인 자재 PM · PLAN 840-4) 서버 호출 — 개인 층만 · 서버 `M02_Labor_Router` 의 /layers/personal/materials + * 목록(줄마다 쓰는 곳) · 만들기 · 고치기 · 지우기(쓰는 곳 있으면 409) · M01 재료 DB 와 같은 이름 · 규격 · 단위 = 409 + * ========================================================================== */ + +import { call } from "./M02_MasterTemplete_Api_Fetch"; + +export interface MyMaterialUse { + 종류: string; + 키: string; + 이름: string; +} + +export interface MyMaterial { + 키: string; + 이름: string; + 규격: string; + 단위: string; + 값: number | null; + 쓰는곳?: MyMaterialUse[]; +} + +export type MyMaterialBody = Pick; + +const path = (owner: string | null, key = ""): string => + `/layers/personal/materials${key ? `/${encodeURIComponent(key)}` : ""}${ + owner ? `?owner=${encodeURIComponent(owner)}` : "" + }`; + +export const listMyMaterials = (owner: string | null): Promise => + call<{ 줄: MyMaterial[] }>(path(owner)).then((d) => d.줄); + +export const addMyMaterial = (owner: string | null, body: MyMaterialBody): Promise => + call(path(owner), { method: "POST", body: JSON.stringify(body) }); + +export const editMyMaterial = ( + owner: string | null, + key: string, + body: MyMaterialBody, +): Promise => + call(path(owner, key), { method: "PUT", body: JSON.stringify(body) }); + +export const dropMyMaterial = (owner: string | null, key: string): Promise => + call(path(owner, key), { method: "DELETE" }); diff --git a/M02_MasterTemplete/M02_MyMaterial_View.ts b/M02_MasterTemplete/M02_MyMaterial_View.ts new file mode 100644 index 000000000..165178c89 --- /dev/null +++ b/M02_MasterTemplete/M02_MyMaterial_View.ts @@ -0,0 +1,185 @@ +/* ============================================================================= + * M02_MyMaterial_View.ts + * M02 개인관리 › 자재 위 「내 재료」 등록 칸(PLAN 840-4 · 요구 854 · 862) — 개인 층 + * 내 재료 = 재료 DB(M01)에 없는 자재를 등록하는 곳 · M01 재료 값 바꿈은 단가산출 안 재료 줄에서 + * 표 = 이름 · 규격 · 단위 · 값 · 쓰는 곳 · 키 · 줄 누름 = 아래 칸에서 고치기 · [새 자재] = 빈 칸 + * [저장] = 서버(M01 과 이름 · 규격 · 단위 같으면 서버가 막음 — 까닭 그대로 알림) · [지우기] = 쓰는 곳 있으면 서버가 막음 + * 값을 고치면 그 재료를 쓰는 개인 층 단가산출이 따라옴(836-2 자동 따라옴 · 프로젝트 층 = 사본 값) + * 남의 층(owner 있음) = 읽기만 + * ========================================================================== */ + +import { createButton } from "@ui/ui_template_button"; +import { createDataTable } from "@ui/ui_template_data_table"; +import { createInputField, el, showConfirmDialog, showToast } from "@ui/ui_template_elements"; +import { createText } from "@ui/ui_template_text"; +import { createUnitPick, NO_UNIT } from "./DataFlow/M02_DF_UnitPick"; +import { + addMyMaterial, + dropMyMaterial, + editMyMaterial, + listMyMaterials, + type MyMaterial, +} from "./M02_MyMaterial_Api"; + +const TEXT = { + title: "내 재료", + note: "재료 DB(M01)에 없는 자재를 등록하는 곳 — 단가산출 재료 고르기 「내 재료」 로 씀 · M01 재료 값 바꿈은 단가산출 안 재료 줄에서", + none: "내 재료 없음", + add: "새 자재", + name: "품명", + spec: "규격", + unit: "단위", + value: "값(원)", + uses: "쓰는 곳", + key: "키", + save: "저장", + drop: "지우기", + cancel: "취소", + need: "품명 · 단위는 꼭 적음", + saved: "저장함 — 쓰는 단가산출이 새 값으로 셈", + dropped: "지움", + dropAsk: "「{name}」 을 지움", + readOnly: "남의 층 — 읽기만", +} as const; + +export interface MyMaterialBlockOptions { + owner: string | null; +} + +export function buildMyMaterialBlock(opt: MyMaterialBlockOptions): HTMLElement { + const canWrite = opt.owner === null; + let rows: MyMaterial[] = []; + let editing: MyMaterial | null = null; + let unit = ""; + const table = createDataTable({ + caption: TEXT.title, + columns: [ + { key: "이름", label: TEXT.name, width: 200 }, + { key: "규격", label: TEXT.spec, width: 140 }, + { key: "단위", label: TEXT.unit, width: 60 }, + { key: "값", label: TEXT.value, kind: "number", decimals: 2, trim: true, width: 100 }, + { key: "쓰는곳", label: TEXT.uses, kind: "number", decimals: 0, width: 70 }, + { key: "키", label: TEXT.key, width: 90 }, + ], + rows: [], + empty: TEXT.none, + onRowClick: (i) => canWrite && open(rows[i]), + }); + const name = createInputField({ label: TEXT.name, required: true }); + const spec = createInputField({ label: TEXT.spec }); + const price = createInputField({ label: TEXT.value, type: "number", min: 0 }); + const unitHost = el("span"); + const drawUnit = (): void => + unitHost.replaceChildren( + createUnitPick({ + value: unit || NO_UNIT, + ariaLabel: TEXT.unit, + onPick: (u) => { + unit = u === NO_UNIT ? "" : u; + drawUnit(); + }, + }), + ); + const head = createText(""); + const saveBtn = createButton({ label: TEXT.save, variant: "filled", onClick: () => void save() }); + const dropBtn = createButton({ + label: TEXT.drop, + variant: "danger-ghost", + onClick: () => void drop(), + }); + const form = el("div", { + className: "m02-pick__new", + attrs: { "data-my-material-form": "" }, + children: [ + head, + name.root, + spec.root, + el("div", { + className: "ui-field", + children: [el("label", { className: "ui-field__label", text: TEXT.unit }), unitHost], + }), + price.root, + el("div", { + className: "ui-btn-group", + children: [ + createButton({ label: TEXT.cancel, variant: "ghost", onClick: () => close() }), + dropBtn, + saveBtn, + ], + }), + ], + }); + form.hidden = true; + + function open(row: MyMaterial | null): void { + editing = row; + name.input.value = row?.이름 ?? ""; + spec.input.value = row?.규격 ?? ""; + price.input.value = row?.값 === null || row?.값 === undefined ? "" : String(row.값); + unit = row?.단위 ?? ""; + drawUnit(); + head.textContent = row ? `${row.키} ${row.이름}` : TEXT.add; + dropBtn.hidden = !row; + form.hidden = false; + name.input.focus(); + } + function close(): void { + editing = null; + form.hidden = true; + } + async function load(): Promise { + rows = await listMyMaterials(opt.owner).catch((e) => { + showToast(e instanceof Error ? e.message : String(e), "error"); + return []; + }); + table.setRows(rows.map((r) => ({ ...r, 쓰는곳: r.쓰는곳?.length ?? 0 }))); + } + async function save(): Promise { + const 이름 = name.input.value.trim(); + if (!이름 || !unit) return showToast(TEXT.need, "error"); + const raw = price.input.value.trim(); + const body = { + 이름, + 규격: spec.input.value.trim(), + 단위: unit, + 값: raw === "" ? null : Number(raw), + }; + saveBtn.disabled = true; + try { + if (editing) await editMyMaterial(opt.owner, editing.키, body); + else await addMyMaterial(opt.owner, body); + showToast(TEXT.saved, "success"); + close(); + await load(); + } catch (e) { + showToast(e instanceof Error ? e.message : String(e), "error"); + } finally { + saveBtn.disabled = false; + } + } + async function drop(): Promise { + if (!editing) return; + if (!(await showConfirmDialog(TEXT.dropAsk.replace("{name}", editing.이름), TEXT.drop))) return; + try { + await dropMyMaterial(opt.owner, editing.키); + showToast(TEXT.dropped, "success"); + close(); + await load(); + } catch (e) { + showToast(e instanceof Error ? e.message : String(e), "error"); + } + } + + const addBtn = createButton({ label: TEXT.add, variant: "filled", onClick: () => open(null) }); + addBtn.hidden = !canWrite; + void load(); + return el("div", { + attrs: { "data-my-materials": "" }, + children: [ + el("div", { className: "ui-toolbar", children: [addBtn] }), + createText(canWrite ? TEXT.note : TEXT.readOnly, "muted"), + table.root, + form, + ], + }); +} diff --git a/M02_MasterTemplete/M02_Sangeun_Copy.py b/M02_MasterTemplete/M02_Sangeun_Copy.py index 26e4c9d91..61434f8b6 100644 --- a/M02_MasterTemplete/M02_Sangeun_Copy.py +++ b/M02_MasterTemplete/M02_Sangeun_Copy.py @@ -45,10 +45,11 @@ READS: ContextVar[dict | None] = ContextVar("읽은줄", default=None) def whole_() -> Any: - """M01 색인 + 지금 층 내 기계 줄(821-1 · `M02_MyMachine.whole`).""" + """M01 색인 + 지금 층 내 기계 줄(821-1 · `M02_MyMachine.whole`) + 내 재료 줄(840-4 · `M02_Material_Own.whole`).""" + from M02_MasterTemplete import M02_Material_Own as own from M02_MasterTemplete import M02_MyMachine as mym - return mym.whole() + return own.whole(mym.whole()) def _fp(rows: Any) -> str: diff --git a/resources/tester/test_840_4_my_material.py b/resources/tester/test_840_4_my_material.py new file mode 100644 index 000000000..f81acc4e6 --- /dev/null +++ b/resources/tester/test_840_4_my_material.py @@ -0,0 +1,78 @@ +# ruff: noqa: F811 — 임시 층 fixture(world)를 test_m02_labor_ref 에서 받아 씀 +"""840-4 내 재료(개인 자재 PM) = M01 재료 DB 에 없는 자재 등록 자리 — 임시 storage. + +① M01 재료 DB 와 이름 · 규격 · 단위가 같으면 만들기 · 고치기 막음(409) · 품명 · 단위 빈 = 400 +② 단가산출 재료 줄이 내 재료를 M01 재료처럼 읽음(미리 셈 · 저장) +③ 내 재료 값을 고치면 개인 층 단가산출 금액이 따라옴(836-2 자동 따라옴 길) +④ 쓰는 곳 있으면 지움 막음(409) · 쓰는 곳 없으면 지움 · 없는 키 = 404. +""" + +from __future__ import annotations + +import pytest + +from M01_MasterData import M01_MasterData_Store as m01 +from M02_MasterTemplete import M02_MasterTemplete_Store as store +from M02_MasterTemplete import M02_Material_Own as own +from resources.tester.test_m02_labor_ref import world # noqa: F401 — 같은 임시 층 + +NAME = "sub4임시자재" + + +def _at(folder, fn, *args): + with store.at(folder): + return fn(*args) + + +def _code(fn, *args) -> int: + with pytest.raises(store.StoreError) as e: + fn(*args) + return e.value.status + + +def _doc(key: str, code: str) -> dict: + """줄 쌓기 재료 줄 하나(화면이 저장하는 꼴) — 단가 = 요소 이음(내 재료).""" + var = {"기호": "P", "아래": "재", "위": "", "이름": NAME, "단위": "", + "연결": {"갈래": "요소", "키": code, "단위": "kg"}} # fmt: skip + line = {"이름": NAME, "단위": "kg", "비목": "재료비", "수량": "2", "단가": "P_재"} + return {"꼴": "수식문서", "판": 1, "키": key, "이름": NAME, "결과단위": "원", + "변수": [var], "식": [], "금액": [line]} # fmt: skip + + +def test_m01_same_blocked(world): + me = world["personal"] + mt = next(r for r in m01.loaded()[1].index["MT"].values() if r.get("이름") and r.get("단위")) + same = {"이름": f" {mt['이름']} ", "규격": mt.get("규격") or "", "단위": mt["단위"], "값": 1} + assert _at(me, _code, own.add, me, same) == 409 + assert _at(me, _code, own.add, me, {"이름": NAME, "단위": ""}) == 400 + row = _at(me, own.add, me, {"이름": NAME, "규격": "x", "단위": "kg", "값": 1000}) + assert own.is_own(row["키"]) and own.m01_same(row) is None + assert _at(me, _code, own.update, me, row["키"], same) == 409 + assert _at(me, _code, own.update, me, "PM999999", {"이름": NAME, "단위": "kg"}) == 404 + assert ( + _at(me, _code, own.add, me, {"이름": NAME, "규격": "X", "단위": "㎏"}) == 409 + ) # 내 목록 같음 + + +def test_sk_reads_and_follows(world): + from M02_MasterTemplete import M02_Doc_Sk_Path as path + from M02_MasterTemplete import M02_Labor_Ref as refs + + me = world["personal"] + code = _at(me, own.add, me, {"이름": NAME, "규격": "y", "단위": "kg", "값": 1000})["키"] + sk = _at(me, lambda: refs.create("sangeun", {"이름": NAME, "단위": "식", "더한줄": []})["이름"]) + doc = _doc(sk, code) + assert _at(me, path.preview, sk, {"문서": doc})["셈"]["계"] == 2000 # 저장 전 미리 셈 + _at(me, path.save, sk, {"문서": doc, "판": _at(me, path.read, sk)["판"]}) + assert _at(me, path.read, sk)["셈"]["계"] == 2000 + _at(me, own.update, me, code, {"이름": NAME, "규격": "y", "단위": "kg", "값": 1500}) + assert _at(me, path.read, sk)["셈"]["계"] == 3000 # 값 바꿈 → 따라옴 + assert [u["키"] for u in _at(me, own.uses, me, code)] == [sk] + assert _at(me, _code, own.remove, me, code) == 409 # 쓰는 곳 있음 + _at(me, store.delete, "sangeun", sk) + aside = me / "_합친것" / f"{sk}.json" # 파생 · 보관 폴더 = 쓰는 곳 아님 + aside.parent.mkdir(parents=True, exist_ok=True) + aside.write_text(f'{{"이름": "x", "키": "{code}"}}', encoding="utf-8") + _at(me, own.remove, me, code) + assert all(r["키"] != code for r in own.rows(me)) + assert _at(me, _code, own.remove, me, code) == 404 diff --git a/resources/tester/test_m02_material_own.py b/resources/tester/test_m02_material_own.py index 82581f67f..a20d6b2a6 100644 --- a/resources/tester/test_m02_material_own.py +++ b/resources/tester/test_m02_material_own.py @@ -55,7 +55,7 @@ def test_추가하면_PM_6자리_키_목록에_보임_화면_키_무시(world): assert got.status_code == 200, got.text row = got.json() assert row == {"키": "PM000001", "이름": "앵커볼트", "규격": "M16", "단위": "EA", "값": 1200} - assert c.get(URL.format("personal")).json()["줄"] == [row] + assert c.get(URL.format("personal")).json()["줄"] == [{**row, "쓰는곳": []}] # 840-4 쓰는 곳 path = layers.personal_dir(7, 42) / own.FILE assert path.is_file() assert own.read(path.parent)["그룹"] == "재료" diff --git a/ui_template/ui_template_locale_m2.ts b/ui_template/ui_template_locale_m2.ts index 05a6bfbc9..53d2d2ee4 100644 --- a/ui_template/ui_template_locale_m2.ts +++ b/ui_template/ui_template_locale_m2.ts @@ -236,6 +236,7 @@ export const ui_locales_m2 = { M02_FindClass: ["구분", "Class"], M02_FindLogic: ["단가산출 기초", "Unit-cost logic"], M02_FindNewMaterial: ["신규 자재", "New material"], + M02_FindMyMaterial: ["내 재료", "My material"], M02_FindElementLater: ["일위대가 표 개편 뒤", "After the unit-cost table rework"], M02_FindOptions: ["옵션 {n}", "{n} options"], // 729-3 불러오기 — 안 이은 줄 · 일위대가표 띠