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; 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), + ), })), }; }