From 221eb23e46400c3d2faf95af3ebc12ebd57b4dd3 Mon Sep 17 00:00:00 2001 From: umsangdon Date: Tue, 8 Sep 2026 23:10:40 +0900 Subject: [PATCH 1/2] =?UTF-8?q?feat(B06):=20=EC=82=AC=ED=86=A0=20=EC=9E=94?= =?UTF-8?q?=EB=9F=89=EC=97=90=20=E3=80=8C=EC=A7=80=EB=B0=98=20=EB=AA=A8?= =?UTF-8?q?=EB=A5=B4=EB=8A=94=20=EB=AA=AB=E3=80=8D=EC=9D=84=20=EA=B0=92?= =?UTF-8?q?=EC=9C=BC=EB=A1=9C=20=EB=83=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit B08 이 사토 운반을 덤프 단가로 가르려면 토사/암 갈래가 필요하다. 잔량에는 이미 `ea/rr/br` 이 실려 있으나(절토에서 남은 흙이라 구성비를 안다), **구조물 잔토로 생긴 몫만은 근거가 없다**(어느 지반에서 파낸 흙인지 판정하지 않는다). - `ground_unknown_m3 = volume − (ea+rr+br)` 을 잔량마다 함께 낸다. - ⚠ 안 내면 받는 쪽이 **0 으로 눅여 토사로 세기 쉽다** — 그래서 값으로 드러낸다. Co-Authored-By: Claude Opus 5 (1M context) --- common_util/common_util_mass_haul_settle.ts | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/common_util/common_util_mass_haul_settle.ts b/common_util/common_util_mass_haul_settle.ts index c69bb23f..5f99367f 100644 --- a/common_util/common_util_mass_haul_settle.ts +++ b/common_util/common_util_mass_haul_settle.ts @@ -237,6 +237,13 @@ export function haulPlanPayload(plan: HaulPlan): Record { ea_m3: round(residual.ea_m3), rr_m3: round(residual.rr_m3), br_m3: round(residual.br_m3), + // ⚠ **지반을 모르는 몫**(㎥) — 갈래 합과 총량의 차이다. 지금은 **구조물 잔토**가 + // 그것이다(어느 지반에서 파낸 흙인지 우리가 판정하지 않는다). 받는 쪽이 덤프 단가를 + // 토사/암으로 가를 때 **이 몫만 근거가 없다**는 것을 알아야 하므로 값으로 낸다 + // (2026-09-09 — 안 내면 0 으로 눅여 토사로 세기 쉽다). + ground_unknown_m3: round( + Math.max(residual.volume_m3 - (residual.ea_m3 + residual.rr_m3 + residual.br_m3), 0), + ), })), }; } From df6d33c46255f04271dcba83cb3c875d89c2b0bc Mon Sep 17 00:00:00 2001 From: umsangdon Date: Tue, 8 Sep 2026 23:15:08 +0900 Subject: [PATCH 2/2] =?UTF-8?q?feat(B06):=20=EA=B5=AC=EC=A1=B0=EB=AC=BC=20?= =?UTF-8?q?=EC=9E=94=ED=86=A0=EC=97=90=20=ED=84=B0=ED=8C=8C=EA=B8=B0=20?= =?UTF-8?q?=ED=86=A0=EC=A7=88=EC=9D=84=20=EC=9D=B4=EC=96=B4=EB=B0=9B?= =?UTF-8?q?=EC=95=84=20=EC=82=AC=ED=86=A0=20=EA=B0=88=EB=9E=98(ea/rr/br)?= =?UTF-8?q?=EB=A5=BC=20=EC=B1=84=EC=9B=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit B08 이 구조물터파기에서 이미 판정한 토질(`design.ground_type`, 품셈 9-13 3구분)을 잔토 측점값에 함께 실어 준다. **새 근거를 만드는 것이 아니라 이어받는 것**이라 두 자리가 같은 판정을 쓴다(두 벌로 짜면 갈린다). - `structure_spoil_points[].ground_type` (soil→ea · ripping_rock→rr · blasting_rock→br). 옛 이름(`ground`)·라벨(`ground_label`)도 함께 받는다. - ⚠ **섞여서 못 고른 구조물은 `null`** 로 오고 그 몫은 갈래 없이 담겨 `ground_unknown_m3` 로 드러난다 — 임의로 토사로 몰지 않는다. - ⚠ 한계는 그대로 넘어온다: 「터파기 깊이가 암반 경계선보다 깊은지」는 안 본다 (터파기 토질에 이미 있던 한계이고 새로 생기는 것이 아니다). 시험 8건(갈래별로 담김·못 고른 몫은 「모름」으로 남음 포함). Co-Authored-By: Claude Opus 5 (1M context) --- B06_Section/B06_Section_Server_Calc_Node.ts | 9 +++- common_util/common_util_mass_haul_balance.ts | 48 ++++++++++++++++---- 2 files changed, 48 insertions(+), 9 deletions(-) diff --git a/B06_Section/B06_Section_Server_Calc_Node.ts b/B06_Section/B06_Section_Server_Calc_Node.ts index e4cb1e78..faedf92b 100644 --- a/B06_Section/B06_Section_Server_Calc_Node.ts +++ b/B06_Section/B06_Section_Server_Calc_Node.ts @@ -42,7 +42,14 @@ interface ServerCalcInput { /** 구조물 터파기 잔토(㎥, 양수) — B08 이 낸다. 사토에 **더한다**. */ structure_spoil_m3?: number | null; /** 측점별 잔토 — 오면 **그 자리**에 얹는다(운반거리가 맞다). */ - structure_spoil_points?: Array<{ chainage_m: number; spoil_m3: number }> | null; + structure_spoil_points?: Array<{ + chainage_m: number; + spoil_m3: number; + /** 그 터파기의 토질 — B08 이 이미 판정한 값(품셈 9-13). `null` 이면 「지반 모름」. */ + ground_type?: string | null; + ground_label?: string | null; + ground?: string | null; + }> | null; }; } diff --git a/common_util/common_util_mass_haul_balance.ts b/common_util/common_util_mass_haul_balance.ts index 36cb6f3d..5bc1f8c0 100644 --- a/common_util/common_util_mass_haul_balance.ts +++ b/common_util/common_util_mass_haul_balance.ts @@ -394,6 +394,21 @@ function applyCollectedStoneDeduction(residuals: HaulResidual[], deduction: numb * * 돌려주는 값은 **실제로 더한 양(㎥)**. 받을 사토 잔량이 하나도 없으면 0 이다. */ +/** + * 터파기 토질 문자열 → 잔량의 지반 갈래 칸. 모르면 `null`(그 몫은 「지반 모름」으로 남는다). + * 받는 말은 B08 의 품셈 9-13 3구분과 우리 내부 이름 둘 다 받는다. + */ +function groundBucket(ground: string | null | undefined): "ea_m3" | "rr_m3" | "br_m3" | null { + const text = (ground ?? "").trim(); + if (!text) return null; + if (text === "토사" || text === "soil" || text === "ea") return "ea_m3"; + if (text === "암절취" || text === "리핑암" || text === "ripping_rock" || text === "rr") { + return "rr_m3"; + } + if (text === "발파암" || text === "blasting_rock" || text === "br") return "br_m3"; + return null; +} + function newSpoilResidual(fromM: number, toM: number, volumeM3: number): HaulResidual { // 잔토를 담을 사토 잔량이 없을 때 **새로 세운다**(2026-09-09 확정 ㉰). // ⚠ 지반유형 안분·자연방토는 0 이다 — 어느 지반에서 나온 흙인지 모르고, **실어 내는** 흙이다. @@ -430,7 +445,14 @@ function newSpoilResidual(fromM: number, toM: number, volumeM3: number): HaulRes function applyStructureSpoil( residuals: HaulResidual[], amount: number | null, - points: Array<{ chainage_m: number; spoil_m3: number }> | null, + points: Array<{ + chainage_m: number; + spoil_m3: number; + /** B08 이 보내는 이름은 `ground_type`(soil·ripping_rock·blasting_rock). 옛 이름·라벨도 받는다. */ + ground_type?: string | null; + ground_label?: string | null; + ground?: string | null; + }> | null, ): number { const spoilsOf = (): HaulResidual[] => residuals.filter((residual) => residual.kind === "spoil"); @@ -445,12 +467,13 @@ function applyStructureSpoil( const covering = spoils.find( (residual) => chainage >= residual.from_m - 1e-9 && chainage <= residual.to_m + 1e-9, ); - if (covering) { - covering.volume_m3 += value; - } else { - // 그 자리를 품는 사토가 없다 — 그 측점에 사토를 새로 세워 담는다. - residuals.push(newSpoilResidual(chainage, chainage, value)); - } + // 터파기 토질이 함께 오면 **그 갈래로 담는다** — B08 이 이미 판정한 값을 이어받는 것이라 + // 새 근거를 만드는 것이 아니다. 안 오면 갈래 없이 담겨 「지반 모름」으로 남는다. + const bucket = groundBucket(point?.ground_type ?? point?.ground ?? point?.ground_label); + const target = covering ?? newSpoilResidual(chainage, chainage, 0); + if (!covering) residuals.push(target); + target.volume_m3 += value; + if (bucket) target[bucket] += value; added += value; } return added; @@ -487,7 +510,16 @@ export function computeHaulPlan( collected_stone_deduction_m3?: number | null; structure_spoil_m3?: number | null; /** 측점별 구조물 잔토 — 오면 **이쪽이 이긴다**(그 자리 잔량에 얹어 운반거리를 맞춘다). */ - structure_spoil_points?: Array<{ chainage_m: number; spoil_m3: number }> | null; + structure_spoil_points?: Array<{ + chainage_m: number; + spoil_m3: number; + /** 그 터파기의 토질 — B08 이 `design.ground_type` 으로 이미 판정한 값(품셈 9-13 3구분). + * **새 근거를 만드는 것이 아니라 이어받는 것**이다. 섞여서 못 고른 구조물은 `null` 로 + * 오고, 그 몫은 「지반 모름」(`ground_unknown_m3`)으로 남는다. */ + ground_type?: string | null; + ground_label?: string | null; + ground?: string | null; + }> | null; }, ): HaulPlan | null { const points = result.points;