- 서버 새 길 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
144 lines
4.9 KiB
TypeScript
144 lines
4.9 KiB
TypeScript
/* =============================================================================
|
||
* 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;
|
||
}
|