Files
Aislo/B08_Quantity/B08_Quantity_Milestone_Calc.ts
T
eomsangdonandClaude Opus 5.5 09b57398b7 feat(b08): 거리이정표 km 자동 = 카카오 길찾기 REST · 서버 단독 · 사용자 km 우선 · 도로 구분 고르기 (244-7)
- 서버 새 길 POST …/quantity/milestone/directions — 두 좌표 → 카카오 길찾기 → km 소수 1자리 · 키 없음 · 실패 = 까닭 한 줄 · 키 서버만 · 응답 원문 · 경로 좌표 안 돌려줌
- 거래처 모달 [길찾기] · [등록] 뒤 한 번 — 거래처 → 기준점 · 기준점 → 시점(기준점 없으면 거래처 → 시점) 자동 km(auto_km 만 저장)
- 사용자 km 우선 · 다르면 호버 「자동 값 ○○ km」 · 도로 구분 안 고름 = 합에 안 듦 · 표 합계 칸 「도로 구분 고르기」
- 표 합 파이썬 · TS 짝 같이 고침 · 새 시험 test_244_7

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01STqEqztUthVFBmEP18wBSz
2026-10-10 22:11:36 +09:00

144 lines
4.9 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/* =============================================================================
* B08_Quantity_Milestone_Calc.ts
* B08 거리이정표 표 합(PLAN 244-1) — 화면 없음 · 저장 안 함 · import 없음(시험이 node 로 돌림).
*
* ⚠ 짝 = `B08_Quantity_Milestone_Calc.py`(같은 식) — 한쪽을 고치면 다른 쪽도 ·
* 거울 시험 `resources/tester/test_244_1_b08_milestone.py`.
* 조작 중은 이 파일이 바로 셈 · [저장] · 읽기 때 서버가 파이썬 짝으로 다시 셈.
*
* 표 한 줄 = 경로(업체) 구간 + (기준점 거침이면 기준점 → 시점 공통 구간) + (목적지 = 중점이면
* 시점 → 중점 = 중점 측점 km) · 도로 구분 셋마다 합 · 포장 = 2차 + 1차 · 합계 = 포장 + 비포장 ·
* 끝수 = 소수 셋째 자리(반올림)
* 구간 km = 사용자 값 · 없으면 [길찾기] 자동 값(244-7) · 도로 구분 안 고른 구간 = 합에 안 넣고
* pick = 참(화면 「도로 구분 고르기」)
* ========================================================================== */
export const ROADS = ["2차포장", "1차포장", "비포장"] as const;
export type Road = (typeof ROADS)[number];
/** 8방 — 기준점(없으면 공사지) 기준 지도 방위 */
export const DIRS = ["N", "NE", "E", "SE", "S", "SW", "W", "NW"] as const;
export type Dir = (typeof DIRS)[number];
export type Dest = "start" | "mid";
export interface Leg {
/** null = 사용자가 안 고름(자동 판정 없음 · 244-7) */
road: Road | null;
/** 사용자 값 — 있으면 우선 */
km: number | null;
/** [길찾기] 자동 값(km 만 저장 · 경로 좌표 · 응답은 저장 안 함) */
auto_km?: number | null;
}
export interface MilestoneRow {
item: string;
dest: Dest;
note: string;
}
export interface MilestoneRoute {
id: string;
/** 업체 이름 = 하위 탭 이름 */
name: string;
address: string;
/** 기준점 거침(기준점 없으면 무시 · 시점으로 바로) */
via_base: boolean;
dir: Dir | null;
lon?: number | null;
lat?: number | null;
/** 업체 → 기준점(거침) · 시점 구간 */
legs: Leg[];
rows: MilestoneRow[];
}
export interface MilestoneData {
site: { mid_station_m: number | null; mid_road: Road };
base: {
enabled: boolean;
name: string;
address: string;
dir: Dir | null;
lon?: number | null;
lat?: number | null;
/** 기준점 → 시점 공통 구간 */
legs: Leg[];
};
routes: MilestoneRoute[];
}
export interface MilestoneTableRow {
route: string;
index: number;
item: string;
company: string;
dest: Dest;
s2: number;
s1: number;
un: number;
paved: number;
unpaved: number;
total: number;
note: string;
/** 도로 구분 안 고른 구간이 있음 */
pick: boolean;
}
const KEYS: Record<string, "s2" | "s1" | "un"> = { "2차포장": "s2", "1차포장": "s1", 비포장: "un" };
/** 소수 셋째 자리 반올림(파이썬 짝 floor(x × 1000 + 0.5) 과 같음) */
export const r3 = (value: number): number => Math.floor(value * 1000 + 0.5) / 1000;
/** 구간 km — 사용자 값 우선 · 없으면 자동 값 */
export const legKm = (leg: Leg): number | null => leg.km ?? leg.auto_km ?? null;
/** 도로 구분마다 더함 — 안 고른 구간이 있으면 참 */
function addLegs(sums: Record<"s2" | "s1" | "un", number>, legs: Leg[] | undefined): boolean {
let pick = false;
for (const leg of legs ?? []) {
const key = leg.road ? KEYS[leg.road] : undefined;
if (key) sums[key] += Number(legKm(leg) ?? 0);
else pick = true;
}
return pick;
}
/** 시점 → 중점 km — 사용자 측점(없으면 기본 = 노선 ÷ 2) ÷ 1000 */
export function midKm(data: MilestoneData, midDefaultM: number | null): number {
const station = data.site?.mid_station_m ?? midDefaultM;
return Number(station ?? 0) / 1000;
}
/** 표 줄 — 경로 차례 · 경로 안 줄 차례 */
export function computeMilestone(
data: MilestoneData,
midDefaultM: number | null,
): MilestoneTableRow[] {
const base = data.base;
const mid: Leg = { road: data.site?.mid_road ?? "비포장", km: midKm(data, midDefaultM) };
const out: MilestoneTableRow[] = [];
for (const route of data.routes ?? []) {
(route.rows ?? []).forEach((row, index) => {
const sums = { s2: 0, s1: 0, un: 0 };
let pick = addLegs(sums, route.legs);
if (base?.enabled && route.via_base !== false) pick = addLegs(sums, base.legs) || pick;
if (row.dest === "mid") addLegs(sums, [mid]);
const paved = sums.s2 + sums.s1;
out.push({
route: route.id,
index,
item: row.item ?? "",
company: route.name ?? "",
dest: row.dest ?? "start",
s2: r3(sums.s2),
s1: r3(sums.s1),
un: r3(sums.un),
paved: r3(paved),
unpaved: r3(sums.un),
total: r3(paved + sums.un),
note: row.note ?? "",
pick,
});
});
}
return out;
}