Merge remote-tracking branch 'origin/main_laptop_1' into sub_laptop_2

This commit is contained in:
2026-10-01 23:37:14 +09:00
36 changed files with 1750 additions and 1206 deletions
@@ -0,0 +1,785 @@
{
"key": "SAMPLE-masonry",
"name": "견본 · 돌쌓기 메쌓기(인력)",
"resultUnit": "원/㎡",
"boxes": [
{
"id": "in-var",
"kind": "input",
"name": "쌓기 조건",
"color": "#2f80ed",
"unit": "-",
"x": 40,
"y": 40,
"input": {
"mode": "variable",
"elements": [],
"variables": [
{
"name": "뒷길이",
"type": "choice",
"unit": "㎝",
"example": 30,
"choices": [25, 30, 35, 45, 55, 60, 75]
},
{
"name": "돌",
"type": "choice",
"unit": "-",
"example": "깬돌",
"choices": ["깬잡석", "견치돌", "깬돌", "호박돌 및 야면석"]
},
{
"name": "쌓기",
"type": "choice",
"unit": "-",
"example": "골쌓기",
"choices": ["골쌓기", "켜쌓기"]
},
{
"name": "높이",
"type": "number",
"unit": "m",
"example": 4.5
}
]
}
},
{
"id": "in-poom",
"kind": "input",
"name": "메쌓기 품표",
"color": "#2f80ed",
"unit": "-",
"x": 40,
"y": 300,
"input": {
"mode": "group",
"group": "소요량",
"elements": [],
"table": {
"key": "QF000421"
},
"variables": []
}
},
{
"id": "in-rate",
"kind": "input",
"name": "높이 증가율표",
"color": "#2f80ed",
"unit": "-",
"x": 40,
"y": 460,
"input": {
"mode": "group",
"group": "소요량",
"elements": [],
"table": {
"key": "QF000422"
},
"variables": []
}
},
{
"id": "in-labor",
"kind": "input",
"name": "인력",
"color": "#2f80ed",
"unit": "원/일",
"x": 40,
"y": 620,
"input": {
"mode": "group",
"group": "인력",
"variables": [],
"elements": [
{
"ref": "LB000033",
"name": "석공",
"spec": "",
"unit": "원/일"
},
{
"ref": "LB000002",
"name": "보통인부",
"spec": "",
"unit": "원/일"
}
]
}
},
{
"id": "fn-rate",
"kind": "function",
"name": "증가율 찾기",
"color": "#7b4fd6",
"unit": "%",
"x": 340,
"y": 420,
"func": {
"sheet": {
"id": "cf-rate",
"이름": "증가율 찾기",
"칸": {
"A1": {
"값": "이름"
},
"B1": {
"값": "값"
},
"C1": {
"값": "설명"
},
"A2": {
"값": "높이"
},
"C2": {
"값": "받는 값 · m"
},
"A3": {
"값": "표에서 찾은 증가율"
},
"B3": {
"식": "SUMIFS($G$2:$G$100,$E$2:$E$100,\"<\"&B2,$F$2:$F$100,\">=\"&B2)"
},
"C3": {
"값": "높이가 「아래 < 높이 ≤ 위」 인 줄"
},
"A4": {
"값": "증가율"
},
"B4": {
"식": "IF(B2<=3,0,B3)"
},
"C4": {
"값": "3m 까지는 할증 없음"
}
}
},
"inputs": [
{
"port": "높이",
"cell": "B2"
},
{
"port": "증가율표",
"cell": "E1"
}
],
"variables": [],
"output": {
"cell": "B4",
"name": "증가율",
"unit": "%"
}
}
},
{
"id": "fn-poom0",
"kind": "function",
"name": "석공 품",
"color": "#7b4fd6",
"unit": "인/㎡",
"x": 640,
"y": 80,
"func": {
"sheet": {
"id": "cf-poom0",
"이름": "석공 품",
"칸": {
"A1": {
"값": "이름"
},
"B1": {
"값": "값"
},
"C1": {
"값": "설명"
},
"A2": {
"값": "뒷길이"
},
"C2": {
"값": "받는 값 · ㎝"
},
"A3": {
"값": "돌"
},
"C3": {
"값": "받는 값"
},
"A4": {
"값": "쌓기"
},
"C4": {
"값": "받는 값"
},
"A5": {
"값": "증가율"
},
"C5": {
"값": "받는 값 · %"
},
"A6": {
"값": "표에서 찾은 품"
},
"B6": {
"식": "SUMIFS($H$2:$H$200,$E$2:$E$200,B2,$F$2:$F$200,B3,$G$2:$G$200,B4)"
},
"C6": {
"값": "세 조건이 맞는 줄의 품"
},
"A7": {
"값": "석공 품"
},
"B7": {
"식": "B6*(1+B5/100)"
},
"C7": {
"값": "높이 할증을 얹음"
}
}
},
"inputs": [
{
"port": "뒷길이",
"cell": "B2"
},
{
"port": "돌",
"cell": "B3"
},
{
"port": "쌓기",
"cell": "B4"
},
{
"port": "증가율",
"cell": "B5"
},
{
"port": "품표",
"cell": "E1"
}
],
"variables": [],
"output": {
"cell": "B7",
"name": "석공 품",
"unit": "인/㎡"
}
}
},
{
"id": "fn-poom1",
"kind": "function",
"name": "보통인부 품",
"color": "#7b4fd6",
"unit": "인/㎡",
"x": 640,
"y": 300,
"func": {
"sheet": {
"id": "cf-poom1",
"이름": "보통인부 품",
"칸": {
"A1": {
"값": "이름"
},
"B1": {
"값": "값"
},
"C1": {
"값": "설명"
},
"A2": {
"값": "뒷길이"
},
"C2": {
"값": "받는 값 · ㎝"
},
"A3": {
"값": "돌"
},
"C3": {
"값": "받는 값"
},
"A4": {
"값": "쌓기"
},
"C4": {
"값": "받는 값"
},
"A5": {
"값": "증가율"
},
"C5": {
"값": "받는 값 · %"
},
"A6": {
"값": "표에서 찾은 품"
},
"B6": {
"식": "SUMIFS($I$2:$I$200,$E$2:$E$200,B2,$F$2:$F$200,B3,$G$2:$G$200,B4)"
},
"C6": {
"값": "세 조건이 맞는 줄의 품"
},
"A7": {
"값": "보통인부 품"
},
"B7": {
"식": "B6*(1+B5/100)"
},
"C7": {
"값": "높이 할증을 얹음"
}
}
},
"inputs": [
{
"port": "뒷길이",
"cell": "B2"
},
{
"port": "돌",
"cell": "B3"
},
{
"port": "쌓기",
"cell": "B4"
},
{
"port": "증가율",
"cell": "B5"
},
{
"port": "품표",
"cell": "E1"
}
],
"variables": [],
"output": {
"cell": "B7",
"name": "보통인부 품",
"unit": "인/㎡"
}
}
},
{
"id": "fn-pay0",
"kind": "function",
"name": "석공 노무비",
"color": "#7b4fd6",
"unit": "원/㎡",
"x": 940,
"y": 80,
"func": {
"sheet": {
"id": "cf-pay0",
"이름": "석공 노무비",
"칸": {
"A1": {
"값": "이름"
},
"B1": {
"값": "값"
},
"C1": {
"값": "설명"
},
"A2": {
"값": "석공 품"
},
"C2": {
"값": "받는 값 · 인/㎡"
},
"A3": {
"값": "석공 노임"
},
"C3": {
"값": "받는 값 · 원/인"
},
"A4": {
"값": "석공 노무비"
},
"B4": {
"식": "B2*B3"
},
"C4": {
"값": "품 × 노임"
}
}
},
"inputs": [
{
"port": "석공 품",
"cell": "B2"
},
{
"port": "석공 노임",
"cell": "B3"
}
],
"variables": [],
"output": {
"cell": "B4",
"name": "석공 노무비",
"unit": "원/㎡"
}
}
},
{
"id": "fn-pay1",
"kind": "function",
"name": "보통인부 노무비",
"color": "#7b4fd6",
"unit": "원/㎡",
"x": 940,
"y": 300,
"func": {
"sheet": {
"id": "cf-pay1",
"이름": "보통인부 노무비",
"칸": {
"A1": {
"값": "이름"
},
"B1": {
"값": "값"
},
"C1": {
"값": "설명"
},
"A2": {
"값": "보통인부 품"
},
"C2": {
"값": "받는 값 · 인/㎡"
},
"A3": {
"값": "보통인부 노임"
},
"C3": {
"값": "받는 값 · 원/인"
},
"A4": {
"값": "보통인부 노무비"
},
"B4": {
"식": "B2*B3"
},
"C4": {
"값": "품 × 노임"
}
}
},
"inputs": [
{
"port": "보통인부 품",
"cell": "B2"
},
{
"port": "보통인부 노임",
"cell": "B3"
}
],
"variables": [],
"output": {
"cell": "B4",
"name": "보통인부 노무비",
"unit": "원/㎡"
}
}
},
{
"id": "fn-sum",
"kind": "function",
"name": "노무비 합",
"color": "#7b4fd6",
"unit": "원/㎡",
"x": 1240,
"y": 190,
"func": {
"sheet": {
"id": "cf-sum",
"이름": "노무비 합",
"칸": {
"A1": {
"값": "이름"
},
"B1": {
"값": "값"
},
"C1": {
"값": "설명"
},
"A2": {
"값": "석공 노무비"
},
"C2": {
"값": "받는 값"
},
"A3": {
"값": "보통인부 노무비"
},
"C3": {
"값": "받는 값"
},
"A4": {
"값": "노무비"
},
"B4": {
"식": "B2+B3"
},
"C4": {
"값": "둘을 더함"
}
}
},
"inputs": [
{
"port": "석공 노무비",
"cell": "B2"
},
{
"port": "보통인부 노무비",
"cell": "B3"
}
],
"variables": [],
"output": {
"cell": "B4",
"name": "노무비",
"unit": "원/㎡"
}
}
},
{
"id": "out-pay",
"kind": "output",
"name": "노무비",
"color": "#27ae60",
"unit": "원/㎡",
"x": 1520,
"y": 190,
"output": {
"cost": "노무비",
"unit": "원/㎡"
}
}
],
"links": [
{
"id": "l1",
"from": {
"box": "in-var",
"name": "높이"
},
"to": {
"box": "fn-rate",
"name": "높이"
}
},
{
"id": "l2",
"from": {
"box": "in-rate",
"name": "표"
},
"to": {
"box": "fn-rate",
"name": "증가율표"
}
},
{
"id": "l3",
"from": {
"box": "in-var",
"name": "뒷길이"
},
"to": {
"box": "fn-poom0",
"name": "뒷길이"
}
},
{
"id": "l4",
"from": {
"box": "in-var",
"name": "돌"
},
"to": {
"box": "fn-poom0",
"name": "돌"
}
},
{
"id": "l5",
"from": {
"box": "in-var",
"name": "쌓기"
},
"to": {
"box": "fn-poom0",
"name": "쌓기"
}
},
{
"id": "l6",
"from": {
"box": "fn-rate",
"name": "증가율"
},
"to": {
"box": "fn-poom0",
"name": "증가율"
}
},
{
"id": "l7",
"from": {
"box": "in-poom",
"name": "표"
},
"to": {
"box": "fn-poom0",
"name": "품표"
}
},
{
"id": "l8",
"from": {
"box": "in-var",
"name": "뒷길이"
},
"to": {
"box": "fn-poom1",
"name": "뒷길이"
}
},
{
"id": "l9",
"from": {
"box": "in-var",
"name": "돌"
},
"to": {
"box": "fn-poom1",
"name": "돌"
}
},
{
"id": "l10",
"from": {
"box": "in-var",
"name": "쌓기"
},
"to": {
"box": "fn-poom1",
"name": "쌓기"
}
},
{
"id": "l11",
"from": {
"box": "fn-rate",
"name": "증가율"
},
"to": {
"box": "fn-poom1",
"name": "증가율"
}
},
{
"id": "l12",
"from": {
"box": "in-poom",
"name": "표"
},
"to": {
"box": "fn-poom1",
"name": "품표"
}
},
{
"id": "l13",
"from": {
"box": "fn-poom0",
"name": "석공 품"
},
"to": {
"box": "fn-pay0",
"name": "석공 품"
}
},
{
"id": "l14",
"from": {
"box": "in-labor",
"name": "석공"
},
"to": {
"box": "fn-pay0",
"name": "석공 노임"
}
},
{
"id": "l15",
"from": {
"box": "fn-poom1",
"name": "보통인부 품"
},
"to": {
"box": "fn-pay1",
"name": "보통인부 품"
}
},
{
"id": "l16",
"from": {
"box": "in-labor",
"name": "보통인부"
},
"to": {
"box": "fn-pay1",
"name": "보통인부 노임"
}
},
{
"id": "l17",
"from": {
"box": "fn-pay0",
"name": "석공 노무비"
},
"to": {
"box": "fn-sum",
"name": "석공 노무비"
}
},
{
"id": "l18",
"from": {
"box": "fn-pay1",
"name": "보통인부 노무비"
},
"to": {
"box": "fn-sum",
"name": "보통인부 노무비"
}
},
{
"id": "l19",
"from": {
"box": "fn-sum",
"name": "노무비"
},
"to": {
"box": "out-pay",
"name": "값"
}
}
],
"userElements": []
}
@@ -0,0 +1,146 @@
{
"key": "SAMPLE-rate",
"name": "견본 · 공구손료(비율)",
"resultUnit": "원",
"boxes": [
{
"id": "in-var",
"kind": "input",
"name": "바탕 값",
"color": "#2f80ed",
"unit": "-",
"x": 40,
"y": 80,
"input": {
"mode": "variable",
"elements": [],
"variables": [
{
"name": "노무비",
"type": "number",
"unit": "원",
"example": 100000
},
{
"name": "공구손료율",
"type": "number",
"unit": "%",
"example": 3
}
]
}
},
{
"id": "fn-rate",
"kind": "function",
"name": "공구손료",
"color": "#7b4fd6",
"unit": "원",
"x": 340,
"y": 80,
"func": {
"sheet": {
"id": "cf-tool",
"이름": "공구손료",
"칸": {
"A1": {
"값": "이름"
},
"B1": {
"값": "값"
},
"C1": {
"값": "설명"
},
"A2": {
"값": "노무비"
},
"C2": {
"값": "받는 값 · 원"
},
"A3": {
"값": "공구손료율"
},
"C3": {
"값": "받는 값 · %"
},
"A4": {
"값": "공구손료"
},
"B4": {
"식": "B2*B3/100"
},
"C4": {
"값": "노무비 × 율 ÷ 100"
}
}
},
"inputs": [
{
"port": "노무비",
"cell": "B2"
},
{
"port": "공구손료율",
"cell": "B3"
}
],
"variables": [],
"output": {
"cell": "B4",
"name": "공구손료",
"unit": "원"
}
}
},
{
"id": "out-mat",
"kind": "output",
"name": "재료비",
"color": "#27ae60",
"unit": "원",
"x": 640,
"y": 80,
"output": {
"cost": "재료비",
"unit": "원"
}
}
],
"links": [
{
"id": "l1",
"from": {
"box": "in-var",
"name": "노무비"
},
"to": {
"box": "fn-rate",
"name": "노무비"
}
},
{
"id": "l2",
"from": {
"box": "in-var",
"name": "공구손료율"
},
"to": {
"box": "fn-rate",
"name": "공구손료율"
}
},
{
"id": "l3",
"from": {
"box": "fn-rate",
"name": "공구손료"
},
"to": {
"box": "out-mat",
"name": "값"
}
}
],
"userElements": []
}
@@ -0,0 +1,200 @@
{
"key": "SAMPLE-value",
"name": "견본 · 덤프트럭 시간당 작업량(값)",
"resultUnit": "㎥/시간",
"boxes": [
{
"id": "in-var",
"kind": "input",
"name": "덤프트럭 조건",
"color": "#2f80ed",
"unit": "-",
"x": 40,
"y": 80,
"input": {
"mode": "variable",
"elements": [],
"variables": [
{
"name": "q",
"type": "number",
"unit": "㎥",
"example": 6
},
{
"name": "f",
"type": "number",
"unit": "-",
"example": 1.2
},
{
"name": "E",
"type": "number",
"unit": "-",
"example": 0.9
},
{
"name": "Cm",
"type": "number",
"unit": "분",
"example": 25
}
]
}
},
{
"id": "fn-q",
"kind": "function",
"name": "시간당 작업량",
"color": "#7b4fd6",
"unit": "㎥/시간",
"x": 340,
"y": 80,
"func": {
"sheet": {
"id": "cf-q",
"이름": "시간당 작업량",
"칸": {
"A1": {
"값": "이름"
},
"B1": {
"값": "값"
},
"C1": {
"값": "설명"
},
"A2": {
"값": "q"
},
"C2": {
"값": "적재량 · ㎥"
},
"A3": {
"값": "f"
},
"C3": {
"값": "토량환산계수"
},
"A4": {
"값": "E"
},
"C4": {
"값": "작업효율"
},
"A5": {
"값": "Cm"
},
"C5": {
"값": "1회 사이클 시간 · 분"
},
"A6": {
"값": "시간당 작업량"
},
"B6": {
"식": "ROUND(60*B2*B3*B4/B5,3)"
},
"C6": {
"값": "60 × q × f × E ÷ Cm · 소수 셋째 자리"
}
}
},
"inputs": [
{
"port": "q",
"cell": "B2"
},
{
"port": "f",
"cell": "B3"
},
{
"port": "E",
"cell": "B4"
},
{
"port": "Cm",
"cell": "B5"
}
],
"variables": [],
"output": {
"cell": "B6",
"name": "시간당 작업량",
"unit": "㎥/시간"
}
}
},
{
"id": "out-q",
"kind": "output",
"name": "시간당 작업량",
"color": "#27ae60",
"unit": "㎥/시간",
"x": 640,
"y": 80,
"output": {
"cost": "값",
"unit": "㎥/시간"
}
}
],
"links": [
{
"id": "l1",
"from": {
"box": "in-var",
"name": "q"
},
"to": {
"box": "fn-q",
"name": "q"
}
},
{
"id": "l2",
"from": {
"box": "in-var",
"name": "f"
},
"to": {
"box": "fn-q",
"name": "f"
}
},
{
"id": "l3",
"from": {
"box": "in-var",
"name": "E"
},
"to": {
"box": "fn-q",
"name": "E"
}
},
{
"id": "l4",
"from": {
"box": "in-var",
"name": "Cm"
},
"to": {
"box": "fn-q",
"name": "Cm"
}
},
{
"id": "l5",
"from": {
"box": "fn-q",
"name": "시간당 작업량"
},
"to": {
"box": "out-q",
"name": "값"
}
}
],
"userElements": []
}
+423
View File
@@ -0,0 +1,423 @@
{
"key": "SAMPLE-ground",
"name": "견본 · 지반에 따라 굴삭기 / 브레이커",
"resultUnit": "원/㎥",
"boxes": [
{
"id": "in-ground",
"kind": "input",
"name": "지반 · 굴착량",
"color": "#2f80ed",
"unit": "-",
"x": 40,
"y": 60,
"input": {
"mode": "variable",
"elements": [],
"variables": [
{
"name": "지반",
"type": "choice",
"unit": "-",
"example": "토사",
"choices": ["토사", "암"]
},
{
"name": "굴착량",
"type": "number",
"unit": "㎥",
"example": 100
}
]
}
},
{
"id": "in-cost",
"kind": "input",
"name": "장비 시간당 경비",
"color": "#2f80ed",
"unit": "원/시간",
"x": 40,
"y": 300,
"input": {
"mode": "variable",
"elements": [],
"variables": [
{
"name": "굴삭기 시간당 경비",
"type": "number",
"unit": "원/시간",
"example": 60000
},
{
"name": "브레이커 시간당 경비",
"type": "number",
"unit": "원/시간",
"example": 80000
}
]
}
},
{
"id": "fn-exc",
"kind": "function",
"name": "굴삭기로 파기",
"color": "#7b4fd6",
"unit": "원",
"x": 380,
"y": 40,
"func": {
"sheet": {
"id": "s-fn-exc",
"이름": "굴삭기로 파기",
"칸": {
"A1": {
"값": "이름"
},
"B1": {
"값": "값"
},
"C1": {
"값": "설명"
},
"A2": {
"값": "굴착량"
},
"C2": {
"값": "받는 값 · 지반이 토사일 때만 옴"
},
"A3": {
"값": "시간당 경비"
},
"C3": {
"값": "받는 값 · 원/시간"
},
"A4": {
"값": "시간당 작업량"
},
"B4": {
"값": 30
},
"C4": {
"값": "㎥/시간 · 고침"
},
"A5": {
"값": "작업 시간"
},
"B5": {
"식": "B2/B4"
},
"C5": {
"값": "시간"
},
"A6": {
"값": "굴삭기 경비"
},
"B6": {
"식": "B5*B3"
},
"C6": {
"값": "원"
}
}
},
"inputs": [
{
"port": "굴착량",
"cell": "B2"
},
{
"port": "시간당 경비",
"cell": "B3"
}
],
"variables": [],
"output": {
"cell": "B6",
"name": "굴삭기 경비",
"unit": "원"
}
}
},
{
"id": "fn-brk",
"kind": "function",
"name": "브레이커로 깨기",
"color": "#7b4fd6",
"unit": "원",
"x": 380,
"y": 300,
"func": {
"sheet": {
"id": "s-fn-brk",
"이름": "브레이커로 깨기",
"칸": {
"A1": {
"값": "이름"
},
"B1": {
"값": "값"
},
"C1": {
"값": "설명"
},
"A2": {
"값": "굴착량"
},
"C2": {
"값": "받는 값 · 지반이 토사가 아닐 때만 옴"
},
"A3": {
"값": "시간당 경비"
},
"C3": {
"값": "받는 값 · 원/시간"
},
"A4": {
"값": "시간당 작업량"
},
"B4": {
"값": 8
},
"C4": {
"값": "㎥/시간 · 고침"
},
"A5": {
"값": "작업 시간"
},
"B5": {
"식": "B2/B4"
},
"C5": {
"값": "시간"
},
"A6": {
"값": "브레이커 경비"
},
"B6": {
"식": "B5*B3"
},
"C6": {
"값": "원"
}
}
},
"inputs": [
{
"port": "굴착량",
"cell": "B2"
},
{
"port": "시간당 경비",
"cell": "B3"
}
],
"variables": [],
"output": {
"cell": "B6",
"name": "브레이커 경비",
"unit": "원"
}
}
},
{
"id": "fn-sum",
"kind": "function",
"name": "1㎥ 경비",
"color": "#7b4fd6",
"unit": "원/㎥",
"x": 720,
"y": 170,
"func": {
"sheet": {
"id": "s-fn-sum",
"이름": "1㎥ 경비",
"칸": {
"A1": {
"값": "이름"
},
"B1": {
"값": "값"
},
"C1": {
"값": "설명"
},
"A2": {
"값": "굴삭기 경비"
},
"C2": {
"값": "받는 값 · 안 오면 빈 칸 = 0"
},
"A3": {
"값": "브레이커 경비"
},
"C3": {
"값": "받는 값 · 안 오면 빈 칸 = 0"
},
"A4": {
"값": "굴착량"
},
"C4": {
"값": "받는 값 · ㎥"
},
"A5": {
"값": "1㎥ 경비"
},
"B5": {
"식": "(B2+B3)/B4"
},
"C5": {
"값": "둘 중 온 것 ÷ 굴착량"
}
}
},
"inputs": [
{
"port": "굴삭기 경비",
"cell": "B2"
},
{
"port": "브레이커 경비",
"cell": "B3"
},
{
"port": "굴착량",
"cell": "B4"
}
],
"variables": [],
"output": {
"cell": "B5",
"name": "1㎥ 경비",
"unit": "원/㎥"
}
}
},
{
"id": "out-exp",
"kind": "output",
"name": "경비",
"color": "#27ae60",
"unit": "원/㎥",
"x": 1040,
"y": 170,
"output": {
"cost": "경비",
"unit": "원/㎥",
"round": {
"places": 1,
"method": "버림"
}
}
}
],
"links": [
{
"id": "l1",
"from": {
"box": "in-ground",
"name": "굴착량"
},
"to": {
"box": "fn-exc",
"name": "굴착량"
},
"rule": {
"kind": "gate",
"from": {
"box": "in-ground",
"name": "지반"
},
"op": "=",
"value": "토사"
}
},
{
"id": "l2",
"from": {
"box": "in-ground",
"name": "굴착량"
},
"to": {
"box": "fn-brk",
"name": "굴착량"
},
"rule": {
"kind": "gate",
"from": {
"box": "in-ground",
"name": "지반"
},
"op": "≠",
"value": "토사"
}
},
{
"id": "l3",
"from": {
"box": "in-cost",
"name": "굴삭기 시간당 경비"
},
"to": {
"box": "fn-exc",
"name": "시간당 경비"
}
},
{
"id": "l4",
"from": {
"box": "in-cost",
"name": "브레이커 시간당 경비"
},
"to": {
"box": "fn-brk",
"name": "시간당 경비"
}
},
{
"id": "l5",
"from": {
"box": "fn-exc",
"name": "굴삭기 경비"
},
"to": {
"box": "fn-sum",
"name": "굴삭기 경비"
}
},
{
"id": "l6",
"from": {
"box": "fn-brk",
"name": "브레이커 경비"
},
"to": {
"box": "fn-sum",
"name": "브레이커 경비"
}
},
{
"id": "l7",
"from": {
"box": "in-ground",
"name": "굴착량"
},
"to": {
"box": "fn-sum",
"name": "굴착량"
}
},
{
"id": "l8",
"from": {
"box": "fn-sum",
"name": "1㎥ 경비"
},
"to": {
"box": "out-exp",
"name": "값"
}
}
],
"userElements": []
}
+207
View File
@@ -0,0 +1,207 @@
{
"key": "SAMPLE-hume",
"name": "견본 · 흄관 관경 조건 찾기",
"resultUnit": "원/m",
"boxes": [
{
"id": "in-size",
"kind": "input",
"name": "관경 · 연장",
"color": "#2f80ed",
"unit": "-",
"x": 40,
"y": 60,
"input": {
"mode": "variable",
"elements": [],
"variables": [
{
"name": "관경",
"type": "choice",
"unit": "mm",
"example": 600,
"choices": [400, 450, 500, 600, 700, 800, 900, 1000, 1100, 1200]
},
{
"name": "연장",
"type": "number",
"unit": "m",
"example": 10
}
]
}
},
{
"id": "in-pipe",
"kind": "input",
"name": "흄관",
"color": "#2f80ed",
"unit": "본",
"x": 40,
"y": 300,
"input": {
"mode": "group",
"group": "재료",
"variables": [],
"elements": [
{
"ref": "MT014991",
"name": "흄관",
"spec": "Φ600mm",
"unit": "본",
"mode": "rule",
"scope": {
"구분": "관·이음관",
"상세구분": "흄관",
"찾는말": "접속흄관"
},
"rule": {
"field": "규격",
"op": "포함",
"from": {
"box": "in-size",
"name": "관경"
}
},
"default": "MT014991",
"slot": "흄관"
}
]
}
},
{
"id": "fn-pipe",
"kind": "function",
"name": "흄관 재료비",
"color": "#7b4fd6",
"unit": "원/m",
"x": 380,
"y": 160,
"func": {
"sheet": {
"id": "s-fn-pipe",
"이름": "흄관 재료비",
"칸": {
"A1": {
"값": "이름"
},
"B1": {
"값": "값"
},
"C1": {
"값": "설명"
},
"A2": {
"값": "연장"
},
"C2": {
"값": "받는 값 · m"
},
"A3": {
"값": "흄관 단가"
},
"C3": {
"값": "받는 값 · 원/본(관경으로 찾은 품목)"
},
"A4": {
"값": "1본 길이"
},
"B4": {
"값": 2.5
},
"C4": {
"값": "m · 고침"
},
"A5": {
"값": "본수"
},
"B5": {
"식": "ROUNDUP(B2/B4,0)"
},
"C5": {
"값": "올림"
},
"A6": {
"값": "1m 재료비"
},
"B6": {
"식": "B5*B3/B2"
},
"C6": {
"값": "본수 × 단가 ÷ 연장"
}
}
},
"inputs": [
{
"port": "연장",
"cell": "B2"
},
{
"port": "흄관 단가",
"cell": "B3"
}
],
"variables": [],
"output": {
"cell": "B6",
"name": "1m 재료비",
"unit": "원/m"
}
}
},
{
"id": "out-mat",
"kind": "output",
"name": "재료비",
"color": "#27ae60",
"unit": "원/m",
"x": 720,
"y": 160,
"output": {
"cost": "재료비",
"unit": "원/m",
"round": {
"places": 1,
"method": "버림"
}
}
}
],
"links": [
{
"id": "l1",
"from": {
"box": "in-size",
"name": "연장"
},
"to": {
"box": "fn-pipe",
"name": "연장"
}
},
{
"id": "l2",
"from": {
"box": "in-pipe",
"name": "흄관"
},
"to": {
"box": "fn-pipe",
"name": "흄관 단가"
}
},
{
"id": "l3",
"from": {
"box": "fn-pipe",
"name": "1m 재료비"
},
"to": {
"box": "out-mat",
"name": "값"
}
}
],
"userElements": []
}
+302
View File
@@ -0,0 +1,302 @@
{
"key": "SAMPLE-linked",
"name": "견본 · 다른 로직 잇기(작업량 → 장비 시간)",
"resultUnit": "원/㎥",
"boxes": [
{
"id": "in-logic",
"kind": "input",
"name": "덤프트럭 시간당 작업량",
"color": "#2f80ed",
"unit": "㎥/시간",
"x": 40,
"y": 60,
"input": {
"mode": "logic",
"elements": [],
"variables": [],
"logic": {
"key": "value",
"name": "견본 · 덤프트럭 시간당 작업량(값)",
"outputs": [
{
"name": "시간당 작업량",
"unit": "㎥/시간"
}
],
"designs": [
{
"name": "q",
"unit": "㎥"
},
{
"name": "f",
"unit": "-"
},
{
"name": "E",
"unit": "-"
},
{
"name": "Cm",
"unit": "분"
}
]
}
}
},
{
"id": "in-var",
"kind": "input",
"name": "운반량 · 경비",
"color": "#2f80ed",
"unit": "-",
"x": 40,
"y": 300,
"input": {
"mode": "variable",
"elements": [],
"variables": [
{
"name": "운반량",
"type": "number",
"unit": "㎥",
"example": 120
},
{
"name": "덤프트럭 시간당 경비",
"type": "number",
"unit": "원/시간",
"example": 50000
}
]
}
},
{
"id": "fn-hours",
"kind": "function",
"name": "운반 시간",
"color": "#7b4fd6",
"unit": "시간",
"x": 380,
"y": 60,
"func": {
"sheet": {
"id": "s-fn-hours",
"이름": "운반 시간",
"칸": {
"A1": {
"값": "이름"
},
"B1": {
"값": "값"
},
"C1": {
"값": "설명"
},
"A2": {
"값": "시간당 작업량"
},
"C2": {
"값": "받는 값 · 다른 로직 출력"
},
"A3": {
"값": "운반량"
},
"C3": {
"값": "받는 값 · ㎥"
},
"A4": {
"값": "운반 시간"
},
"B4": {
"식": "B3/B2"
},
"C4": {
"값": "운반량 ÷ 시간당 작업량"
}
}
},
"inputs": [
{
"port": "시간당 작업량",
"cell": "B2"
},
{
"port": "운반량",
"cell": "B3"
}
],
"variables": [],
"output": {
"cell": "B4",
"name": "운반 시간",
"unit": "시간",
"round": {
"places": 2,
"method": "반올림"
}
}
}
},
{
"id": "fn-cost",
"kind": "function",
"name": "1㎥ 운반 경비",
"color": "#7b4fd6",
"unit": "원/㎥",
"x": 700,
"y": 180,
"func": {
"sheet": {
"id": "s-fn-cost",
"이름": "1㎥ 운반 경비",
"칸": {
"A1": {
"값": "이름"
},
"B1": {
"값": "값"
},
"C1": {
"값": "설명"
},
"A2": {
"값": "운반 시간"
},
"C2": {
"값": "받는 값 · 시간"
},
"A3": {
"값": "시간당 경비"
},
"C3": {
"값": "받는 값 · 원/시간"
},
"A4": {
"값": "운반량"
},
"C4": {
"값": "받는 값 · ㎥"
},
"A5": {
"값": "1㎥ 운반 경비"
},
"B5": {
"식": "B2*B3/B4"
},
"C5": {
"값": "시간 × 경비 ÷ 운반량"
}
}
},
"inputs": [
{
"port": "운반 시간",
"cell": "B2"
},
{
"port": "시간당 경비",
"cell": "B3"
},
{
"port": "운반량",
"cell": "B4"
}
],
"variables": [],
"output": {
"cell": "B5",
"name": "1㎥ 운반 경비",
"unit": "원/㎥"
}
}
},
{
"id": "out-exp",
"kind": "output",
"name": "경비",
"color": "#27ae60",
"unit": "원/㎥",
"x": 1020,
"y": 180,
"output": {
"cost": "경비",
"unit": "원/㎥",
"round": {
"places": 1,
"method": "버림"
}
}
}
],
"links": [
{
"id": "l1",
"from": {
"box": "in-logic",
"name": "시간당 작업량"
},
"to": {
"box": "fn-hours",
"name": "시간당 작업량"
}
},
{
"id": "l2",
"from": {
"box": "in-var",
"name": "운반량"
},
"to": {
"box": "fn-hours",
"name": "운반량"
}
},
{
"id": "l3",
"from": {
"box": "fn-hours",
"name": "운반 시간"
},
"to": {
"box": "fn-cost",
"name": "운반 시간"
}
},
{
"id": "l4",
"from": {
"box": "in-var",
"name": "덤프트럭 시간당 경비"
},
"to": {
"box": "fn-cost",
"name": "시간당 경비"
}
},
{
"id": "l5",
"from": {
"box": "in-var",
"name": "운반량"
},
"to": {
"box": "fn-cost",
"name": "운반량"
}
},
{
"id": "l6",
"from": {
"box": "fn-cost",
"name": "1㎥ 운반 경비"
},
"to": {
"box": "out-exp",
"name": "값"
}
}
],
"userElements": []
}
+785
View File
@@ -0,0 +1,785 @@
{
"key": "SAMPLE-masonry",
"name": "견본 · 돌쌓기 메쌓기(인력)",
"resultUnit": "원/㎡",
"boxes": [
{
"id": "in-var",
"kind": "input",
"name": "쌓기 조건",
"color": "#2f80ed",
"unit": "-",
"x": 40,
"y": 40,
"input": {
"mode": "variable",
"elements": [],
"variables": [
{
"name": "뒷길이",
"type": "choice",
"unit": "㎝",
"example": 30,
"choices": [25, 30, 35, 45, 55, 60, 75]
},
{
"name": "돌",
"type": "choice",
"unit": "-",
"example": "깬돌",
"choices": ["깬잡석", "견치돌", "깬돌", "호박돌 및 야면석"]
},
{
"name": "쌓기",
"type": "choice",
"unit": "-",
"example": "골쌓기",
"choices": ["골쌓기", "켜쌓기"]
},
{
"name": "높이",
"type": "number",
"unit": "m",
"example": 4.5
}
]
}
},
{
"id": "in-poom",
"kind": "input",
"name": "메쌓기 품표",
"color": "#2f80ed",
"unit": "-",
"x": 40,
"y": 300,
"input": {
"mode": "group",
"group": "소요량",
"elements": [],
"table": {
"key": "QF000421"
},
"variables": []
}
},
{
"id": "in-rate",
"kind": "input",
"name": "높이 증가율표",
"color": "#2f80ed",
"unit": "-",
"x": 40,
"y": 460,
"input": {
"mode": "group",
"group": "소요량",
"elements": [],
"table": {
"key": "QF000422"
},
"variables": []
}
},
{
"id": "in-labor",
"kind": "input",
"name": "인력",
"color": "#2f80ed",
"unit": "원/일",
"x": 40,
"y": 620,
"input": {
"mode": "group",
"group": "인력",
"variables": [],
"elements": [
{
"ref": "LB000033",
"name": "석공",
"spec": "",
"unit": "원/일"
},
{
"ref": "LB000002",
"name": "보통인부",
"spec": "",
"unit": "원/일"
}
]
}
},
{
"id": "fn-rate",
"kind": "function",
"name": "증가율 찾기",
"color": "#7b4fd6",
"unit": "%",
"x": 340,
"y": 420,
"func": {
"sheet": {
"id": "cf-rate",
"이름": "증가율 찾기",
"칸": {
"A1": {
"값": "이름"
},
"B1": {
"값": "값"
},
"C1": {
"값": "설명"
},
"A2": {
"값": "높이"
},
"C2": {
"값": "받는 값 · m"
},
"A3": {
"값": "표에서 찾은 증가율"
},
"B3": {
"식": "SUMIFS($G$2:$G$100,$E$2:$E$100,\"<\"&B2,$F$2:$F$100,\">=\"&B2)"
},
"C3": {
"값": "높이가 「아래 < 높이 ≤ 위」 인 줄"
},
"A4": {
"값": "증가율"
},
"B4": {
"식": "IF(B2<=3,0,B3)"
},
"C4": {
"값": "3m 까지는 할증 없음"
}
}
},
"inputs": [
{
"port": "높이",
"cell": "B2"
},
{
"port": "증가율표",
"cell": "E1"
}
],
"variables": [],
"output": {
"cell": "B4",
"name": "증가율",
"unit": "%"
}
}
},
{
"id": "fn-poom0",
"kind": "function",
"name": "석공 품",
"color": "#7b4fd6",
"unit": "인/㎡",
"x": 640,
"y": 80,
"func": {
"sheet": {
"id": "cf-poom0",
"이름": "석공 품",
"칸": {
"A1": {
"값": "이름"
},
"B1": {
"값": "값"
},
"C1": {
"값": "설명"
},
"A2": {
"값": "뒷길이"
},
"C2": {
"값": "받는 값 · ㎝"
},
"A3": {
"값": "돌"
},
"C3": {
"값": "받는 값"
},
"A4": {
"값": "쌓기"
},
"C4": {
"값": "받는 값"
},
"A5": {
"값": "증가율"
},
"C5": {
"값": "받는 값 · %"
},
"A6": {
"값": "표에서 찾은 품"
},
"B6": {
"식": "SUMIFS($H$2:$H$200,$E$2:$E$200,B2,$F$2:$F$200,B3,$G$2:$G$200,B4)"
},
"C6": {
"값": "세 조건이 맞는 줄의 품"
},
"A7": {
"값": "석공 품"
},
"B7": {
"식": "B6*(1+B5/100)"
},
"C7": {
"값": "높이 할증을 얹음"
}
}
},
"inputs": [
{
"port": "뒷길이",
"cell": "B2"
},
{
"port": "돌",
"cell": "B3"
},
{
"port": "쌓기",
"cell": "B4"
},
{
"port": "증가율",
"cell": "B5"
},
{
"port": "품표",
"cell": "E1"
}
],
"variables": [],
"output": {
"cell": "B7",
"name": "석공 품",
"unit": "인/㎡"
}
}
},
{
"id": "fn-poom1",
"kind": "function",
"name": "보통인부 품",
"color": "#7b4fd6",
"unit": "인/㎡",
"x": 640,
"y": 300,
"func": {
"sheet": {
"id": "cf-poom1",
"이름": "보통인부 품",
"칸": {
"A1": {
"값": "이름"
},
"B1": {
"값": "값"
},
"C1": {
"값": "설명"
},
"A2": {
"값": "뒷길이"
},
"C2": {
"값": "받는 값 · ㎝"
},
"A3": {
"값": "돌"
},
"C3": {
"값": "받는 값"
},
"A4": {
"값": "쌓기"
},
"C4": {
"값": "받는 값"
},
"A5": {
"값": "증가율"
},
"C5": {
"값": "받는 값 · %"
},
"A6": {
"값": "표에서 찾은 품"
},
"B6": {
"식": "SUMIFS($I$2:$I$200,$E$2:$E$200,B2,$F$2:$F$200,B3,$G$2:$G$200,B4)"
},
"C6": {
"값": "세 조건이 맞는 줄의 품"
},
"A7": {
"값": "보통인부 품"
},
"B7": {
"식": "B6*(1+B5/100)"
},
"C7": {
"값": "높이 할증을 얹음"
}
}
},
"inputs": [
{
"port": "뒷길이",
"cell": "B2"
},
{
"port": "돌",
"cell": "B3"
},
{
"port": "쌓기",
"cell": "B4"
},
{
"port": "증가율",
"cell": "B5"
},
{
"port": "품표",
"cell": "E1"
}
],
"variables": [],
"output": {
"cell": "B7",
"name": "보통인부 품",
"unit": "인/㎡"
}
}
},
{
"id": "fn-pay0",
"kind": "function",
"name": "석공 노무비",
"color": "#7b4fd6",
"unit": "원/㎡",
"x": 940,
"y": 80,
"func": {
"sheet": {
"id": "cf-pay0",
"이름": "석공 노무비",
"칸": {
"A1": {
"값": "이름"
},
"B1": {
"값": "값"
},
"C1": {
"값": "설명"
},
"A2": {
"값": "석공 품"
},
"C2": {
"값": "받는 값 · 인/㎡"
},
"A3": {
"값": "석공 노임"
},
"C3": {
"값": "받는 값 · 원/인"
},
"A4": {
"값": "석공 노무비"
},
"B4": {
"식": "B2*B3"
},
"C4": {
"값": "품 × 노임"
}
}
},
"inputs": [
{
"port": "석공 품",
"cell": "B2"
},
{
"port": "석공 노임",
"cell": "B3"
}
],
"variables": [],
"output": {
"cell": "B4",
"name": "석공 노무비",
"unit": "원/㎡"
}
}
},
{
"id": "fn-pay1",
"kind": "function",
"name": "보통인부 노무비",
"color": "#7b4fd6",
"unit": "원/㎡",
"x": 940,
"y": 300,
"func": {
"sheet": {
"id": "cf-pay1",
"이름": "보통인부 노무비",
"칸": {
"A1": {
"값": "이름"
},
"B1": {
"값": "값"
},
"C1": {
"값": "설명"
},
"A2": {
"값": "보통인부 품"
},
"C2": {
"값": "받는 값 · 인/㎡"
},
"A3": {
"값": "보통인부 노임"
},
"C3": {
"값": "받는 값 · 원/인"
},
"A4": {
"값": "보통인부 노무비"
},
"B4": {
"식": "B2*B3"
},
"C4": {
"값": "품 × 노임"
}
}
},
"inputs": [
{
"port": "보통인부 품",
"cell": "B2"
},
{
"port": "보통인부 노임",
"cell": "B3"
}
],
"variables": [],
"output": {
"cell": "B4",
"name": "보통인부 노무비",
"unit": "원/㎡"
}
}
},
{
"id": "fn-sum",
"kind": "function",
"name": "노무비 합",
"color": "#7b4fd6",
"unit": "원/㎡",
"x": 1240,
"y": 190,
"func": {
"sheet": {
"id": "cf-sum",
"이름": "노무비 합",
"칸": {
"A1": {
"값": "이름"
},
"B1": {
"값": "값"
},
"C1": {
"값": "설명"
},
"A2": {
"값": "석공 노무비"
},
"C2": {
"값": "받는 값"
},
"A3": {
"값": "보통인부 노무비"
},
"C3": {
"값": "받는 값"
},
"A4": {
"값": "노무비"
},
"B4": {
"식": "B2+B3"
},
"C4": {
"값": "둘을 더함"
}
}
},
"inputs": [
{
"port": "석공 노무비",
"cell": "B2"
},
{
"port": "보통인부 노무비",
"cell": "B3"
}
],
"variables": [],
"output": {
"cell": "B4",
"name": "노무비",
"unit": "원/㎡"
}
}
},
{
"id": "out-pay",
"kind": "output",
"name": "노무비",
"color": "#27ae60",
"unit": "원/㎡",
"x": 1520,
"y": 190,
"output": {
"cost": "노무비",
"unit": "원/㎡"
}
}
],
"links": [
{
"id": "l1",
"from": {
"box": "in-var",
"name": "높이"
},
"to": {
"box": "fn-rate",
"name": "높이"
}
},
{
"id": "l2",
"from": {
"box": "in-rate",
"name": "표"
},
"to": {
"box": "fn-rate",
"name": "증가율표"
}
},
{
"id": "l3",
"from": {
"box": "in-var",
"name": "뒷길이"
},
"to": {
"box": "fn-poom0",
"name": "뒷길이"
}
},
{
"id": "l4",
"from": {
"box": "in-var",
"name": "돌"
},
"to": {
"box": "fn-poom0",
"name": "돌"
}
},
{
"id": "l5",
"from": {
"box": "in-var",
"name": "쌓기"
},
"to": {
"box": "fn-poom0",
"name": "쌓기"
}
},
{
"id": "l6",
"from": {
"box": "fn-rate",
"name": "증가율"
},
"to": {
"box": "fn-poom0",
"name": "증가율"
}
},
{
"id": "l7",
"from": {
"box": "in-poom",
"name": "표"
},
"to": {
"box": "fn-poom0",
"name": "품표"
}
},
{
"id": "l8",
"from": {
"box": "in-var",
"name": "뒷길이"
},
"to": {
"box": "fn-poom1",
"name": "뒷길이"
}
},
{
"id": "l9",
"from": {
"box": "in-var",
"name": "돌"
},
"to": {
"box": "fn-poom1",
"name": "돌"
}
},
{
"id": "l10",
"from": {
"box": "in-var",
"name": "쌓기"
},
"to": {
"box": "fn-poom1",
"name": "쌓기"
}
},
{
"id": "l11",
"from": {
"box": "fn-rate",
"name": "증가율"
},
"to": {
"box": "fn-poom1",
"name": "증가율"
}
},
{
"id": "l12",
"from": {
"box": "in-poom",
"name": "표"
},
"to": {
"box": "fn-poom1",
"name": "품표"
}
},
{
"id": "l13",
"from": {
"box": "fn-poom0",
"name": "석공 품"
},
"to": {
"box": "fn-pay0",
"name": "석공 품"
}
},
{
"id": "l14",
"from": {
"box": "in-labor",
"name": "석공"
},
"to": {
"box": "fn-pay0",
"name": "석공 노임"
}
},
{
"id": "l15",
"from": {
"box": "fn-poom1",
"name": "보통인부 품"
},
"to": {
"box": "fn-pay1",
"name": "보통인부 품"
}
},
{
"id": "l16",
"from": {
"box": "in-labor",
"name": "보통인부"
},
"to": {
"box": "fn-pay1",
"name": "보통인부 노임"
}
},
{
"id": "l17",
"from": {
"box": "fn-pay0",
"name": "석공 노무비"
},
"to": {
"box": "fn-sum",
"name": "석공 노무비"
}
},
{
"id": "l18",
"from": {
"box": "fn-pay1",
"name": "보통인부 노무비"
},
"to": {
"box": "fn-sum",
"name": "보통인부 노무비"
}
},
{
"id": "l19",
"from": {
"box": "fn-sum",
"name": "노무비"
},
"to": {
"box": "out-pay",
"name": "값"
}
}
],
"userElements": []
}
+200
View File
@@ -0,0 +1,200 @@
{
"key": "SAMPLE-value",
"name": "견본 · 덤프트럭 시간당 작업량(값)",
"resultUnit": "㎥/시간",
"boxes": [
{
"id": "in-var",
"kind": "input",
"name": "덤프트럭 조건",
"color": "#2f80ed",
"unit": "-",
"x": 40,
"y": 80,
"input": {
"mode": "variable",
"elements": [],
"variables": [
{
"name": "q",
"type": "number",
"unit": "㎥",
"example": 6
},
{
"name": "f",
"type": "number",
"unit": "-",
"example": 1.2
},
{
"name": "E",
"type": "number",
"unit": "-",
"example": 0.9
},
{
"name": "Cm",
"type": "number",
"unit": "분",
"example": 25
}
]
}
},
{
"id": "fn-q",
"kind": "function",
"name": "시간당 작업량",
"color": "#7b4fd6",
"unit": "㎥/시간",
"x": 340,
"y": 80,
"func": {
"sheet": {
"id": "cf-q",
"이름": "시간당 작업량",
"칸": {
"A1": {
"값": "이름"
},
"B1": {
"값": "값"
},
"C1": {
"값": "설명"
},
"A2": {
"값": "q"
},
"C2": {
"값": "적재량 · ㎥"
},
"A3": {
"값": "f"
},
"C3": {
"값": "토량환산계수"
},
"A4": {
"값": "E"
},
"C4": {
"값": "작업효율"
},
"A5": {
"값": "Cm"
},
"C5": {
"값": "1회 사이클 시간 · 분"
},
"A6": {
"값": "시간당 작업량"
},
"B6": {
"식": "ROUND(60*B2*B3*B4/B5,3)"
},
"C6": {
"값": "60 × q × f × E ÷ Cm · 소수 셋째 자리"
}
}
},
"inputs": [
{
"port": "q",
"cell": "B2"
},
{
"port": "f",
"cell": "B3"
},
{
"port": "E",
"cell": "B4"
},
{
"port": "Cm",
"cell": "B5"
}
],
"variables": [],
"output": {
"cell": "B6",
"name": "시간당 작업량",
"unit": "㎥/시간"
}
}
},
{
"id": "out-q",
"kind": "output",
"name": "시간당 작업량",
"color": "#27ae60",
"unit": "㎥/시간",
"x": 640,
"y": 80,
"output": {
"cost": "값",
"unit": "㎥/시간"
}
}
],
"links": [
{
"id": "l1",
"from": {
"box": "in-var",
"name": "q"
},
"to": {
"box": "fn-q",
"name": "q"
}
},
{
"id": "l2",
"from": {
"box": "in-var",
"name": "f"
},
"to": {
"box": "fn-q",
"name": "f"
}
},
{
"id": "l3",
"from": {
"box": "in-var",
"name": "E"
},
"to": {
"box": "fn-q",
"name": "E"
}
},
{
"id": "l4",
"from": {
"box": "in-var",
"name": "Cm"
},
"to": {
"box": "fn-q",
"name": "Cm"
}
},
{
"id": "l5",
"from": {
"box": "fn-q",
"name": "시간당 작업량"
},
"to": {
"box": "out-q",
"name": "값"
}
}
],
"userElements": []
}
+184
View File
@@ -0,0 +1,184 @@
"""흐름 로직 왼쪽 「단가산출 상세」 줄 만들기(`M01_MasterData_UI_LogicLab_FlowRows.ts`) — 순수 부분.
화면 TS 를 그 자리에서 트랜스파일해 node 로 돌림 — 상대 경로 TS(DataFlow Round …)는 이어 트랜스파일 ·
화면 부품(@ui · @config)은 빈 흉내. 견본 로직 · 계산 결과는 이 시험 안에 둠(마스터 내용에 기대지 않음).
"""
import json
import subprocess
from pathlib import Path
import pytest
ROOT = Path(__file__).resolve().parents[2]
SOURCE = ROOT / "M01_MasterData" / "M01_MasterData_UI_LogicLab_FlowRows.ts"
RUNNER = r"""
const fs = require("fs");
const path = require("path");
const [root, file] = process.argv.slice(1);
const ts = require(path.join(root, "config", "node_modules", "typescript"));
const stub = new Proxy({}, { get: (_t, k) => (k === "currentLanguageIndex" ? 0 : k === "API_BASE_URL" ? "http://127.0.0.1:1/api" : () => undefined) });
const cache = {};
function load(f) {
if (cache[f]) return cache[f].exports;
const js = ts.transpileModule(fs.readFileSync(f, "utf8"), {
compilerOptions: { module: ts.ModuleKind.CommonJS, target: ts.ScriptTarget.ES2020 },
}).outputText;
const box = (cache[f] = { exports: {} });
const req = (id) => {
if (id.startsWith(".")) {
const p = path.resolve(path.dirname(f), id);
return fs.existsSync(p + ".ts") ? load(p + ".ts") : stub;
}
return stub;
};
new Function("exports", "module", "require", js)(box.exports, box, req);
return box.exports;
}
const m = load(file);
const jobs = JSON.parse(fs.readFileSync(0, "utf8"));
const out = jobs.map((job) => m.flowRows(job.lg, job.ev, job.round));
process.stdout.write(JSON.stringify(out));
setTimeout(() => process.exit(0), 50);
"""
ROUND = {"item": {"places": 1, "method": "버림"}, "total": {"places": 0, "method": "버림"}}
def box(id_, kind, name, unit="원", **extra):
return {
"id": id_,
"kind": kind,
"name": name,
"color": "#000",
"unit": unit,
"x": 0,
"y": 0,
**extra,
}
def out_box(id_, name, cost, unit="원", round_=None):
output = {"cost": cost, "unit": unit}
if round_:
output["round"] = round_
return box(id_, "output", name, unit, output=output)
def func_box(id_, name, out_name, unit="시간"):
func = {
"sheet": {"id": "s-" + id_, "이름": name, "칸": {}},
"inputs": [],
"variables": [],
"output": {"cell": "B1", "name": out_name, "unit": unit},
}
return box(id_, "function", name, unit, func=func)
def logic(boxes):
return {
"key": None,
"name": "시험",
"resultUnit": "원/㎡",
"boxes": boxes,
"links": [],
"userElements": [],
}
def result(outputs, cost, ports=None, errors=None):
return {"ports": ports or {}, "outputs": outputs, "cost": cost, "errors": errors or []}
def run(jobs: list[dict]) -> list:
node = "node"
done = subprocess.run(
[node, "-e", RUNNER, str(ROOT), str(SOURCE)],
input=json.dumps(jobs),
capture_output=True,
text=True,
timeout=60,
check=False,
)
assert done.returncode == 0, done.stderr
return json.loads(done.stdout)
@pytest.fixture(scope="module")
def sample():
lg = logic(
[
out_box("o1", "인건비", "노무비"),
out_box("o2", "재료값", "재료비"),
out_box("o3", "시간당 작업량", "값", "㎥/시간"),
out_box("o4", "특별 끝수", "경비", round_={"places": 2, "method": "반올림"}),
func_box("f1", "운반 시간", "시간", "시간"),
]
)
ev = result(
{"o1": 3216.69, "o2": 100.04, "o3": 12.5, "o4": 1.239},
{
"노무비": 3216.69,
"재료비": 100.04,
"경비": 1.239,
"값들": {"시간당 작업량": 12.5},
"계": 3317.969,
},
ports={"f1": {"시간": 6.024}},
)
return run([{"lg": lg, "ev": ev, "round": ROUND}])[0]
def group(rows, key):
return next(g for g in rows["groups"] if g["key"] == key)
def test_group_order_and_costs_only(sample):
assert [g["key"] for g in sample["groups"]] == ["labor", "material", "cost", "plain"]
def test_line_uses_output_round(sample):
# 출력 끝수를 안 정한 비용 줄 = 기본 0.1원 버림 → 3,216.6 (정수로 반올림하지 않음)
assert group(sample, "labor")["lines"][0]["text"] == "3,216.6"
assert group(sample, "material")["lines"][0]["text"] == "100"
# 그 출력의 끝수가 있으면 그 자리대로(소수 둘째 반올림)
assert group(sample, "cost")["lines"][0]["text"] == "1.24"
def test_plain_value_is_not_rounded_and_has_no_subtotal(sample):
plain = group(sample, "plain")
assert plain["lines"][0]["text"] == "12.5"
assert plain["lines"][0]["unit"] == "㎥/시간"
assert plain["sub"] is None
def test_subtotal_and_total_floor_to_won(sample):
assert group(sample, "labor")["sub"] == 3216
assert group(sample, "labor")["subText"] == "3,216"
assert sample["total"] == 3317
assert sample["totalText"] == "3,317"
def test_function_rows(sample):
assert sample["funcs"] == [
{"box": "f1", "name": "운반 시간", "unit": "시간", "value": 6.024, "text": "6.024"}
]
def test_empty_group_is_hidden():
lg = logic([out_box("o1", "인건비", "노무비")])
ev = result({"o1": 10}, {"노무비": 10, "재료비": 0, "경비": 0, "값들": {}, "계": 10})
rows = run([{"lg": lg, "ev": ev, "round": ROUND}])[0]
assert [g["key"] for g in rows["groups"]] == ["labor"]
assert rows["total"] == 10
def test_missing_value_shows_dash():
lg = logic([out_box("o1", "인건비", "노무비")])
ev = result(
{"o1": None}, {"노무비": 0, "재료비": 0, "경비": 0, "값들": {}, "계": 0}, errors=["끊김"]
)
rows = run([{"lg": lg, "ev": ev, "round": ROUND}])[0]
assert group(rows, "labor")["lines"][0]["text"] == "—"
assert rows["errors"] == ["끊김"]
+2 -2
View File
@@ -1,4 +1,4 @@
"""M02 단가산출 다이어그램 견본(35-7) 시험 — `M02_MasterTemplete/CostFlow/_sample/*.json` · `M02_CF_Sample.ts`.
"""M02 단가산출 다이어그램 견본(35-7) 시험 — 시험 견본 `resources/tester/fixtures_df/costflow/*.json` · `M02_CF_Sample.ts`.
· 견본 셋 모양 — 상자(입력 · 함수 · 출력) · 알약 이름 규칙 · 선(받는 자리마다 하나 · 출력에서 안 나감 · 순환 없음)
· 견본 ① 원가 = 지금 `/calc`(GF000219 · 같은 예시값 · 끝수 원 미만) — 함수 상자 시트를 우리 스프레드시트 엔진
@@ -21,7 +21,7 @@ import pytest
ROOT = Path(__file__).resolve().parents[2]
FOLDER = ROOT / "M02_MasterTemplete" / "CostFlow"
SAMPLE_TS = FOLDER / "M02_CF_Sample.ts"
SAMPLES = {p.stem: json.loads(p.read_text(encoding="utf-8")) for p in sorted((FOLDER / "_sample").glob("*.json"))} # fmt: skip
SAMPLES = {p.stem: json.loads(p.read_text(encoding="utf-8")) for p in sorted((ROOT / "resources" / "tester" / "fixtures_df" / "costflow").glob("*.json"))} # fmt: skip
HOOKS = r"""
import { readFileSync } from "node:fs";
+230
View File
@@ -0,0 +1,230 @@
"""단가산출 흐름도 입력 판 — 다른 로직 입력 = 마스터 로직 고르기(순수 부분 · `M02_DF_InputPanel_Build.ts`).
화면 TS 를 그 자리에서 트랜스파일해 node 로 돌림. 견본 로직 줄 · 흐름은 이 시험 안에 둠(마스터 내용에 기대지 않음).
"""
import json
import shutil
import subprocess
from pathlib import Path
import pytest
ROOT = Path(__file__).resolve().parents[2]
SOURCE = ROOT / "M02_MasterTemplete" / "DataFlow" / "M02_DF_InputPanel_Build.ts"
RUNNER = r"""
const fs = require("fs");
const path = require("path");
const [root, file] = process.argv.slice(1);
const ts = require(path.join(root, "config", "node_modules", "typescript"));
const js = ts.transpileModule(fs.readFileSync(file, "utf8"), {
compilerOptions: { module: ts.ModuleKind.CommonJS, target: ts.ScriptTarget.ES2020 },
}).outputText;
const box = { exports: {} };
new Function("exports", "module", "require", js)(box.exports, box, () => ({}));
const m = box.exports;
(async () => {
const jobs = JSON.parse(fs.readFileSync(0, "utf8"));
const out = [];
for (const j of jobs) {
if (j.name === "wouldCycle") {
const graph = j.args[2];
out.push(await m.wouldCycle(j.args[0], j.args[1], async (k) => graph[k] ?? null));
} else out.push(await m[j.name](...j.args));
}
process.stdout.write(JSON.stringify(out));
})();
"""
def call(name: str, *args):
if shutil.which("node") is None:
pytest.skip("node 가 없음")
done = subprocess.run(
["node", "-e", RUNNER, str(ROOT), str(SOURCE)],
input=json.dumps([{"name": name, "args": list(args)}], ensure_ascii=False),
capture_output=True,
text=True,
encoding="utf-8",
check=True,
)
return json.loads(done.stdout)[0]
def row(key, name, book, chapter, no="", unit="원/㎡", **more):
return {
"file": f"로직_{book}.json",
"구분": book,
"상세구분": chapter,
"키": key,
"원문번호": no,
"이름": name,
"결과단위": unit,
**more,
}
ITEMS = [
row("GF000219", "돌쌓기 메쌓기(인력)", "산림품셈", "13장 구조물", "13-4-1"),
row("GF000220", "돌쌓기 찰쌓기(인력)", "산림품셈", "13장 구조물", "13-4-2"),
row("GF000025", "떼붙임(재배잔디)", "산림품셈", "05장 녹화", "5-12"),
row("GC000051", "보행자 안전통로", "건설품셈", "01장 공통", "1-3-9", "원/m"),
row("DF-0001", "내 흄관 로직", "흐름도", "내가 만든 것", "", "원/m"),
]
def keys(items):
return [i["키"] for i in items]
def test_찾기는_띄어쓰기로_나눈_말을_모두_포함():
got = call("filterLogics", ITEMS, {"text": "돌쌓기 메쌓기"})
assert keys(got) == ["GF000219"]
assert keys(call("filterLogics", ITEMS, {"text": "돌쌓기"})) == ["GF000219", "GF000220"]
assert keys(call("filterLogics", ITEMS, {"text": "돌 쌓기메쌓기"})) == [
"GF000219"
] # 띄어쓰기는 가리지 않음
assert keys(call("filterLogics", ITEMS, {"text": "13-4"})) == [
"GF000219",
"GF000220",
] # 원문번호
assert keys(call("filterLogics", ITEMS, {"text": "gc000051"})) == [
"GC000051"
] # 번호 · 대소문자
assert call("filterLogics", ITEMS, {"text": ""}) == ITEMS
def test_원문_장으로_거름():
assert keys(call("filterLogics", ITEMS, {"text": "", "book": "건설품셈"})) == ["GC000051"]
got = call("filterLogics", ITEMS, {"text": "", "book": "산림품셈", "chapter": "05장 녹화"})
assert keys(got) == ["GF000025"]
assert call("filterLogics", ITEMS, {"text": "메쌓기", "book": "건설품셈"}) == []
def test_원문_장_목록은_나온_차례():
assert call("logicBooks", ITEMS) == ["산림품셈", "건설품셈", "흐름도"]
assert call("logicChapters", ITEMS, "산림품셈") == ["13장 구조물", "05장 녹화"]
assert call("logicChapters", ITEMS, "") == [
"13장 구조물",
"05장 녹화",
"01장 공통",
"내가 만든 것",
]
def box(id_, kind, name, **more):
return {
"id": id_,
"kind": kind,
"name": name,
"color": "#fff",
"unit": "-",
"x": 0,
"y": 0,
**more,
}
FLOW = {
"key": "GF000219",
"name": "돌쌓기 메쌓기(인력)",
"resultUnit": "원/㎡",
"userElements": [],
"links": [],
"boxes": [
box(
"b1",
"input",
"입력",
input={
"mode": "variable",
"elements": [],
"variables": [
{"name": "뒷길이", "type": "number", "unit": "㎝", "example": 30},
{
"name": "돌",
"type": "choice",
"unit": "",
"choices": ["깬돌", "견치돌"],
"example": "깬돌",
},
],
},
),
box("o1", "output", "노무비", unit="원/㎡", output={"cost": "노무비", "unit": "원/㎡"}),
],
}
def test_흐름_로직_고르기는_출력_상자와_설계_때_정할_것():
ref = call("masterRef", "GF000219", FLOW, False, "원/㎡")
assert ref["key"] == "GF000219"
assert ref["outputs"] == [{"name": "노무비", "unit": "원/㎡"}]
assert ref["designs"] == [{"name": "뒷길이", "unit": "㎝"}, {"name": "돌", "unit": "-"}]
assert ref["examples"] == {"뒷길이": 30, "돌": "깬돌"}
def test_옛_로직_변환은_노무비_재료비_경비_계_넷():
ref = call("masterRef", "GF000219", FLOW, True, "원/㎡")
assert [o["name"] for o in ref["outputs"]] == ["노무비", "재료비", "경비", "계"]
assert {o["unit"] for o in ref["outputs"]} == {"원/㎡"}
assert [d["name"] for d in ref["designs"]] == ["뒷길이", "돌"] # 받는 알약은 흐름 그대로
def test_이어지지_않은_다른_로직_받는_알약도_설계_때_정할_것():
inner = {
"mode": "logic",
"elements": [],
"variables": [],
"logic": {
"key": "GF000250",
"outputs": [{"name": "작업량", "unit": "㎥/시간"}],
"designs": [{"name": "지역", "unit": "-"}, {"name": "기계", "unit": "-"}],
"examples": {"지역": "서울"},
},
}
flow = {**FLOW, "boxes": [*FLOW["boxes"], box("b2", "input", "작업량", input=inner)]}
flow["links"] = [
{"id": "l1", "from": {"box": "b1", "name": "돌"}, "to": {"box": "b2", "name": "기계"}}
]
ref = call("logicRefOf", "X", flow)
names = [d["name"] for d in ref["designs"]]
assert "지역" in names and "기계" not in names # 선이 이어진 받는 알약은 뺌
assert ref["examples"]["지역"] == "서울"
def test_자기_자신과_돌고_도는_로직은_막음():
graph = {
"A": {
"boxes": [
box(
"b",
"input",
"x",
input={"mode": "logic", "elements": [], "variables": [], "logic": {"key": "B"}},
)
]
},
"B": {
"boxes": [
box(
"b",
"input",
"x",
input={
"mode": "logic",
"elements": [],
"variables": [],
"logic": {"key": "SELF"},
},
)
]
},
"C": {"boxes": []},
}
assert call("wouldCycle", "SELF", "SELF", graph) is True # 자기 자신
assert call("wouldCycle", "SELF", "A", graph) is True # A → B → SELF
assert call("wouldCycle", "SELF", "C", graph) is False
assert call("wouldCycle", None, "A", graph) is False # 새 로직은 아직 키가 없어 돌 수 없음
assert call("wouldCycle", "SELF", "없음", graph) is False # 못 읽으면 막지 않음
+3 -1
View File
@@ -646,7 +646,9 @@ def test_쉬는_갈래와_문제_무게() -> None:
def _ground() -> dict:
return json.loads((CF / "_sample" / "ground.json").read_text(encoding="utf-8"))
return json.loads(
(ROOT / "resources" / "tester" / "fixtures_df" / "ground.json").read_text(encoding="utf-8")
)
def _server(lg: dict, values: dict) -> dict:
+3 -49
View File
@@ -1,4 +1,4 @@
"""M02 단가산출 흐름도 견본(35-9) 시험 — `M02_MasterTemplete/DataFlow/_sample/*.json` · `M02_DF_Sample.ts`.
"""M02 단가산출 흐름도 견본(35-9) 시험 — 시험 견본 `resources/tester/fixtures_df/*.json`(36-7 · 화면 견본 목록은 지움).
견본 넷(메쌓기 · 지반 가르기 · 흄관 조건 찾기 · 다른 로직 잇기) + 넷째가 부르는 작업량 로직(value)
· 모양 — 상자(입력 · 함수 · 출력) · 알약 이름 규칙 · 기본 색 · 선(받는 자리마다 하나 · 출력에서 안 나감 · 순환 없음)
@@ -6,7 +6,7 @@
· 흐름 — 출력에서 거꾸로 따라가면 닿는 출처(선 · 선 규칙의 조건 알약 · 재료 조건 알약 포함)가 견본 뜻과 같음
· 값(내부 확인) — 함수 상자 시트를 우리 스프레드시트 엔진(Node 번들)으로 차례대로 셈 · 선 규칙 · 끝수를 지남 ·
입력 값(노임 · 품셈 표 · 재료 단가)은 마스터에서 읽기만 · 메쌓기 = /calc GF000219 · value = /calc GF000133
· `sampleList` · `sampleLoad`(사본 · key 없음) — Node 로 바로 돌림 · 파일 안 씀
· 파일 안 씀
"""
from __future__ import annotations
@@ -14,7 +14,6 @@ from __future__ import annotations
import json
import math
import re
import subprocess
from decimal import Decimal
from pathlib import Path
@@ -22,8 +21,7 @@ import pytest
ROOT = Path(__file__).resolve().parents[2]
FOLDER = ROOT / "M02_MasterTemplete" / "DataFlow"
SAMPLE_TS = FOLDER / "M02_DF_Sample.ts"
SAMPLES = {p.stem: json.loads(p.read_text(encoding="utf-8")) for p in sorted((FOLDER / "_sample").glob("*.json"))} # fmt: skip
SAMPLES = {p.stem: json.loads(p.read_text(encoding="utf-8")) for p in sorted((ROOT / "resources" / "tester" / "fixtures_df").glob("*.json"))} # fmt: skip
NAMES = ["masonry", "ground", "hume", "linked", "value"]
BOX_COLOR = dict(
re.findall(
@@ -34,40 +32,6 @@ BOX_COLOR = dict(
)
)
HOOKS = r"""
import { readFileSync } from "node:fs";
import { fileURLToPath } from "node:url";
import { stripTypeScriptTypes } from "node:module";
export async function load(url, ctx, next) {
if (!url.endsWith(".ts")) return next(url, ctx);
let src = readFileSync(fileURLToPath(url), "utf8");
src = src.replace(/import\.meta\.glob(?:<[\s\S]*?>)?\([^)]*\)/g, "globalThis.__samples");
src = src.replace(/import\s*\{([^}]*)\}\s*from\s*"([^"]+)";/g, (m, names) => {
const list = names.split(",").map((x) => x.trim()).filter((x) => x && !x.startsWith("type "));
return list.length ? `const { ${list.join(", ")} } = globalThis.__stub;` : "";
});
return { format: "module", source: stripTypeScriptTypes(src, { mode: "transform" }), shortCircuit: true };
}
"""
def _node(body: str) -> object:
files = {f"./_sample/{k}.json": v for k, v in SAMPLES.items()}
script = (
"import { register } from 'node:module';\n"
f"register('data:text/javascript,' + encodeURIComponent({json.dumps(HOOKS)}));\n"
"globalThis.__stub = { currentLanguageIndex: 0 };\n"
f"globalThis.__samples = {json.dumps(files, ensure_ascii=False)};\n"
f"const S = await import({json.dumps(SAMPLE_TS.as_uri())});\n"
"const out = (x) => process.stdout.write(JSON.stringify(x));\n" + body
)
result = subprocess.run(
["node", "--no-warnings", "--input-type=module", "-e", script],
capture_output=True, text=True, encoding="utf-8", timeout=120, cwd=ROOT,
) # fmt: skip
assert result.returncode == 0, result.stderr[-2000:]
return json.loads(result.stdout)
# ── 알약 · 선 ─────────────────────────────────────────────────────────────
def _ports_out(box: dict) -> list[str]:
@@ -215,16 +179,6 @@ def test_견본_흐름_규칙_방식() -> None:
}
def test_견본_목록_열기() -> None:
got = _node(
"out({ list: S.sampleList(), one: S.sampleLoad('ground'), none: S.sampleLoad('없음') });"
)
assert [x["id"] for x in got["list"]] == NAMES
assert all(x["note"] for x in got["list"])
assert got["one"]["key"] is None and got["one"]["links"] == SAMPLES["ground"]["links"]
assert got["none"] is None
# ── 값(내부 확인) ─────────────────────────────────────────────────────────
@pytest.fixture(scope="module")
def master():