Merge remote-tracking branch 'origin/sub_laptop_2' into main_laptop_1
This commit is contained in:
@@ -172,6 +172,19 @@
|
||||
width: 6em;
|
||||
}
|
||||
|
||||
.m02-hopyo__unit {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: var(--spacing-4);
|
||||
}
|
||||
|
||||
/* 단위 환산 칸 앞뒤 글 — 「엑셀 1 ㎡ = 우리 [ ] 본」 */
|
||||
.m02-hopyo__unit-text {
|
||||
color: var(--color-text-secondary);
|
||||
font-size: var(--text-sm);
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.m02-hopyo__acts {
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/* =============================================================================
|
||||
* M02_MasterTemplete_Hopyo_CheckRows.ts
|
||||
* 확인 창 줄 표(PLAN 359-3) — 구조물 조합 일위대가 상세 꼴(`Labor_Rows` logicTable · matTable)에 품 비교 · 단추만 더함.
|
||||
* · 로직 표 = [✓] · 차례 · 구분(엑셀 갈래 + 상태) · 이름(우리 로직 · 배 · 옵션 글) · 별칭(엑셀 품명 = 만들 때 별칭) ·
|
||||
* · 로직 표 = [✓] · 차례 · 구분(엑셀 갈래 + 상태) · 이름(우리 로직 · 단위 환산 · 옵션 글) · 별칭(엑셀 품명 = 만들 때 별칭) ·
|
||||
* 수량산출(위 엑셀 / 아래 우리 = 엑셀 × 배) · 결과 단위 · 노무비 · 재료비 · 경비 · 합계 · 품 비교 · 단추
|
||||
* · 자재 표 = [✓] · 차례 · 코드 · 이름 · 규격 · 수량산출 · 단위 · 세 칸(자재구분 · 별도계상 · 운반비 · 글만) ·
|
||||
* 가격 다섯 · 엑셀 / 우리 금액 · 단추
|
||||
@@ -28,6 +28,7 @@ import { moneyCell, PRICE_SLOTS } from "./M02_MasterTemplete_Labor_Rows";
|
||||
import { tc } from "./M02_MasterTemplete_Labor_Text";
|
||||
import { formatNumber, formatPrice } from "../common_util/common_util_money";
|
||||
import { tableOf } from "./logic/M02_MasterTemplete_Hopyo_Draft";
|
||||
import { unitView, type UnitView } from "./logic/M02_MasterTemplete_Hopyo_Unit";
|
||||
|
||||
/** 드롭다운 — 저장 값(서버 뺌 까닭) → 화면 글 */
|
||||
export const OUT_TEXT: [string, string][] = [
|
||||
@@ -109,28 +110,50 @@ const stateChip = (line: HopyoLine): HTMLElement => {
|
||||
});
|
||||
};
|
||||
|
||||
/** 대상마다 한 줄 — 키 · 이름 · × 배 칸 · 몫 */
|
||||
/** 단위 환산 칸 — 「엑셀 1 ㎡ = 우리 [ ] 본」(장부 화면도 같은 꼴) */
|
||||
export function unitField(input: HTMLInputElement, v: Pick<UnitView, "from" | "to">): HTMLElement {
|
||||
input.title = L("M02_HopyoUnitTip");
|
||||
const word = (text: string): HTMLElement =>
|
||||
el("span", { className: "m02-hopyo__unit-text", text });
|
||||
return el("span", {
|
||||
className: "m02-hopyo__unit",
|
||||
children: [
|
||||
word(L("M02_HopyoUnitLead").replace("{from}", v.from || "?")),
|
||||
input,
|
||||
...(v.to ? [word(v.to)] : []),
|
||||
],
|
||||
});
|
||||
}
|
||||
|
||||
/** 대상마다 한 줄 — 키 · 이름 · 단위 환산(같음 = 글만 · 몫 줄 = 몫만 · 빈칸 · 추정 = 입력 칸) · 몫 */
|
||||
function partLines(line: HopyoLine, parts: HopyoPart[], ctx: RowsContext): HTMLElement[] {
|
||||
return parts.map((p, at) => {
|
||||
const v = unitView(p, line.단위, p.단위);
|
||||
const times = el("input", {
|
||||
className: "ui-input ui-input--sm m02-hopyo__times",
|
||||
attrs: { type: "number", step: "any", min: "0", value: p.배 == null ? "" : String(p.배) },
|
||||
}) as HTMLInputElement;
|
||||
times.title = "배 — 우리 수량 = 엑셀 수량 × 배";
|
||||
times.disabled = !ctx.canWrite;
|
||||
times.addEventListener("change", () =>
|
||||
ctx.times(line, at, times.value === "" ? null : Number(times.value)),
|
||||
);
|
||||
const key =
|
||||
p.로직 ?? p.자재 ?? (p.호표 !== undefined ? `제${p.호표}호표` : p.전국 ? "전국" : "");
|
||||
const note = v.estimated
|
||||
? v.thick !== null
|
||||
? L("M02_HopyoUnitThick").replace("{n}", String(v.thick))
|
||||
: L("M02_HopyoUnitEst")
|
||||
: "";
|
||||
return el("div", {
|
||||
className: "m02-hopyo__part",
|
||||
attrs: { title: p.어디 ?? "" },
|
||||
children: [
|
||||
el("span", { className: "m02-labrow__key", text: key }),
|
||||
el("strong", { text: p.이름 ?? "" }),
|
||||
times,
|
||||
...(p.몫 != null ? [small(`몫 ${p.몫}`)] : []),
|
||||
...(v.mode === "same" ? [small(L("M02_HopyoUnitSame"))] : []),
|
||||
...(v.mode === "input" ? [unitField(times, v)] : []),
|
||||
...(note ? [small(note)] : []),
|
||||
...(p.몫 != null && p.몫 !== 1 ? [small(`몫 ${p.몫}`)] : []),
|
||||
],
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
/* =============================================================================
|
||||
* M02_MasterTemplete_Hopyo_Ledger.ts
|
||||
* 일위대가 실무자료 별칭 장부 화면(PLAN 338-3 · 설계 4-4) — 본 칸 · 좌측 판 [별칭 장부] 로 엶.
|
||||
* · 표 = 엑셀 품명 · 규격 · 단위 | 갈래 | 우리 로직(여럿) · 옵션 · 배 · 몫 | 누가 · 때 · 처음 파일 | 층(위 층 회색 읽기만)
|
||||
* · 표 = 엑셀 품명 · 규격 · 단위 | 갈래 | 우리 로직(여럿) · 옵션 · 단위 환산 · 몫 | 누가 · 때 · 처음 파일 | 층(위 층 회색 읽기만)
|
||||
* · 뜻(359-4 · 머리 한 줄 · 좌측 단추 title) = 엑셀 품명 ↔ 우리 로직 · 자재 짝을 기억해 두었다가 같은 품명이 나오면 먼저 제안
|
||||
* · 할 일 = 찾기 · 갈래 거름 · 갈래 바꿈 · [로직 바꿈] · [항목 추가] · 배 · 몫 · [옵션] · [삭제] ·
|
||||
* · 할 일 = 찾기 · 갈래 거름 · 갈래 바꿈 · [로직 바꿈] · [항목 추가] · 단위 환산 · 몫 · [옵션] · [삭제] ·
|
||||
* 위 층 줄 [내 층 복사] · 갈래 글 = 확인 창 드롭다운과 같음(짝 · 로직없음 · 사용안함 · 저장 값 그대로)
|
||||
* · 고친 것 = sessionStorage `m02-hopyo-ledger:{층}:{주인}` → [저장] 때만 `PUT`(서버가 짝을 다시 검사)
|
||||
* · 안 둠 = 발주처 칸 · 자동 올림 · 자동 합침 · 쓰인 횟수 · 회사로 올리기.
|
||||
@@ -27,6 +27,7 @@ import { fetchLogicInputs } from "./M02_MasterTemplete_Labor_Api";
|
||||
import { dateTimeText } from "../common_util/common_util_time";
|
||||
import { openLaborInput } from "./M02_MasterTemplete_Labor_Input";
|
||||
import { openFindPicker } from "./M02_MasterTemplete_Find_Pick";
|
||||
import { unitField } from "./M02_MasterTemplete_Hopyo_CheckRows";
|
||||
import { formatNumber } from "../common_util/common_util_money";
|
||||
import "./M02_MasterTemplete_Hopyo.css";
|
||||
|
||||
@@ -178,12 +179,12 @@ export function buildHopyoLedger(opt: LedgerOptions): HTMLElement {
|
||||
if (mine) {
|
||||
const times = el("input", {
|
||||
className: "ui-input ui-input--sm m02-hopyo__times",
|
||||
attrs: { type: "number", step: "any", min: "0", value: String(p.배), title: "배" },
|
||||
attrs: { type: "number", step: "any", min: "0", value: String(p.배) },
|
||||
}) as HTMLInputElement;
|
||||
times.addEventListener("change", () =>
|
||||
edit(r, { 짝: r.짝.map((x, i) => (i === at ? { ...x, 배: Number(times.value) } : x)) }),
|
||||
);
|
||||
parts.push(times);
|
||||
parts.push(unitField(times, { from: r.원래.단위, to: "" }));
|
||||
if (p.로직)
|
||||
parts.push(
|
||||
createButton({
|
||||
@@ -369,7 +370,7 @@ export function buildHopyoLedger(opt: LedgerOptions): HTMLElement {
|
||||
"규격",
|
||||
"단위",
|
||||
"갈래",
|
||||
"우리 로직 · 배 · 몫 · 옵션",
|
||||
"우리 로직 · 단위 환산 · 몫 · 옵션",
|
||||
"누가 · 때 · 처음 파일",
|
||||
"층",
|
||||
"승인",
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
/* =============================================================================
|
||||
* M02_MasterTemplete_Hopyo_Unit.ts — 확인 창 대상 이름 옆 「단위 환산」 칸의 보이기 판정(PLAN 784-2 · 요구 635-2).
|
||||
* 순수 계산(화면 · fetch 없음) — 숨김 조건 · 두 단위 글 재료 · 추정 두께.
|
||||
* · same = 배 1 · 단위 같음(서버 갈래 같음 · 두 단위 · 단위표 1) → 칸 숨김 · 「단위 같음 ×1」
|
||||
* · quota = 배가 몫(0.1)과 같음 → 칸 숨김 · 「몫 0.1」 만
|
||||
* · input = 빈칸 · 추정 · 그 밖 → 입력 칸 「엑셀 1 ㎡ = 우리 [ ] 본」
|
||||
* 값 자체(배)는 안 바꿈 — 서버 `Match.factor` 가 낸 배 · 갈래를 보이기만 다르게.
|
||||
* ========================================================================== */
|
||||
|
||||
export interface UnitPart {
|
||||
배: number | null;
|
||||
몫?: number | null;
|
||||
환산?: { 배: number | null; 갈래: string } | null;
|
||||
단위?: string | null;
|
||||
}
|
||||
|
||||
export type UnitMode = "same" | "quota" | "input";
|
||||
|
||||
export interface UnitView {
|
||||
mode: UnitMode;
|
||||
/** 엑셀 단위 · 우리 단위(빈 글 = 모름) */
|
||||
from: string;
|
||||
to: string;
|
||||
/** 추정 갈래 — 두께 m(㎡ → ㎥ 일 때만 · 아니면 null) */
|
||||
thick: number | null;
|
||||
estimated: boolean;
|
||||
}
|
||||
|
||||
const plain = (u: string | null | undefined): string =>
|
||||
String(u ?? "")
|
||||
.replace(/\s+/g, "")
|
||||
.replace("㎡", "m2")
|
||||
.replace("㎥", "m3")
|
||||
.toLowerCase();
|
||||
const close = (a: number, b: number): boolean => Math.abs(a - b) < 1e-9;
|
||||
|
||||
export function unitView(
|
||||
p: UnitPart,
|
||||
from: string | null | undefined,
|
||||
to?: string | null,
|
||||
): UnitView {
|
||||
const f = String(from ?? "").trim();
|
||||
const t = String(to ?? p.단위 ?? "").trim();
|
||||
const quota = p.몫 != null && p.몫 !== 1; // 몫 1 = 몫 없음과 같음
|
||||
const kind = p.환산?.갈래;
|
||||
const unitSame = kind
|
||||
? kind === "같음" || kind === "두 단위" || (kind === "단위표" && p.환산?.배 === 1)
|
||||
: plain(f) !== "" && plain(f) === plain(t);
|
||||
const at = quota ? (p.몫 as number) : 1;
|
||||
const hidden = p.배 !== null && close(p.배, at) && unitSame;
|
||||
const estimated = kind === "추정";
|
||||
const thick =
|
||||
estimated && plain(f) === "m2" && plain(t) === "m3" && p.환산?.배 != null ? p.환산.배 : null;
|
||||
return {
|
||||
mode: hidden ? (quota ? "quota" : "same") : "input",
|
||||
from: f,
|
||||
to: t,
|
||||
thick,
|
||||
estimated,
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
/* 784-2 — `logic/M02_MasterTemplete_Hopyo_Unit.ts` 단위 환산 보이기 판정을 트랜스파일해 Node 로 돌림.
|
||||
* stdin = {cases: [{part, from, to}]} · stdout = unitView 결과 배열 JSON. */
|
||||
const fs = require("fs");
|
||||
const path = require("path");
|
||||
const { makeLoader } = require("./helper_ts_stub_loader.cjs");
|
||||
|
||||
const ROOT = path.join(__dirname, "..", "..");
|
||||
const { loadTs } = makeLoader({ root: ROOT });
|
||||
const unit = loadTs(
|
||||
path.join(ROOT, "M02_MasterTemplete", "logic", "M02_MasterTemplete_Hopyo_Unit.ts"),
|
||||
);
|
||||
const input = JSON.parse(fs.readFileSync(0, "utf8"));
|
||||
process.stdout.write(JSON.stringify(input.cases.map((c) => unit.unitView(c.part, c.from, c.to))));
|
||||
@@ -588,3 +588,38 @@ def test_779_세_보기_칠_뛰기_창_다시_씀():
|
||||
len(check.splitlines()) <= 663
|
||||
and len(_text("M02_MasterTemplete_Hopyo_Api.ts").splitlines()) <= 676
|
||||
)
|
||||
|
||||
|
||||
def test_단위_환산_칸_보이기_784_2():
|
||||
"""784-2 — 배 1 · 단위 같음 = 칸 숨김 · 몫 줄 = 몫만 · 빈칸 · 추정 · 단위 다름 = 입력 칸 + 두 단위 글 재료 ·
|
||||
화면(CheckRows · Ledger)은 「배」 대신 「단위 환산」 · 「엑셀 1 ㎡ = 우리 [ ] 본」 꼴."""
|
||||
cases = [
|
||||
{"part": {"배": 1, "환산": {"배": 1, "갈래": "같음"}}, "from": "㎡", "to": "㎡"},
|
||||
{"part": {"배": 1, "환산": {"배": 1, "갈래": "두 단위"}}, "from": "공/㎥", "to": "공㎥"},
|
||||
{"part": {"배": 0.1, "몫": 0.1, "환산": {"배": 1, "갈래": "같음"}}, "from": "㎥", "to": "㎥"},
|
||||
{"part": {"배": None, "환산": {"배": None, "갈래": "입력"}}, "from": "㎡", "to": "본"},
|
||||
{"part": {"배": 1, "환산": {"배": None, "갈래": "입력"}}, "from": "m", "to": "본"},
|
||||
{"part": {"배": 0.2, "환산": {"배": 0.2, "갈래": "추정"}}, "from": "㎡", "to": "㎥"},
|
||||
{"part": {"배": 100, "환산": {"배": 100, "갈래": "단위표"}}, "from": "m", "to": "cm"},
|
||||
{"part": {"배": 0.01, "몫": 0.1, "환산": {"배": 0.1, "갈래": "단위표"}}, "from": "a", "to": "b"},
|
||||
{"part": {"배": 1}, "from": "m2", "to": "㎡"},
|
||||
{"part": {"배": 1}, "from": "m", "to": "본"},
|
||||
{"part": {"배": 1, "몫": 1, "환산": {"배": 1, "갈래": "같음"}}, "from": "㎡", "to": "㎡"},
|
||||
] # fmt: skip
|
||||
got = _node("helper_m02_hopyo_unit.cjs", {"cases": cases})
|
||||
assert [g["mode"] for g in got] == [
|
||||
"same", "same", "quota", "input", "input", "input", "input", "input", "same", "input", "same",
|
||||
] # fmt: skip
|
||||
assert (got[3]["from"], got[3]["to"]) == ("㎡", "본") # 「엑셀 1 ㎡ = 우리 [ ] 본」
|
||||
assert got[5]["estimated"] and got[5]["thick"] == 0.2 # 「두께 0.2m 로 추정」
|
||||
assert not got[3]["estimated"] and got[3]["thick"] is None
|
||||
rows = _text("M02_MasterTemplete_Hopyo_CheckRows.ts")
|
||||
assert "unitView(p, line.단위, p.단위)" in rows and "M02_HopyoUnitSame" in rows
|
||||
assert "배 — 우리 수량" not in rows
|
||||
ledger = _text("M02_MasterTemplete_Hopyo_Ledger.ts")
|
||||
assert "unitField(times, { from: r.원래.단위" in ledger and 'title: "배"' not in ledger
|
||||
pure = _text("logic/M02_MasterTemplete_Hopyo_Unit.ts").split("*/", 1)[1]
|
||||
for word in ("document", "fetch", "sessionStorage", "import "):
|
||||
assert word not in pure, word
|
||||
for name in ("CheckRows", "Ledger"):
|
||||
assert len(_text(f"M02_MasterTemplete_Hopyo_{name}.ts").splitlines()) <= 700
|
||||
|
||||
@@ -163,6 +163,14 @@ export const ui_locales_m2 = {
|
||||
"Tree updated — name already taken, saved as “{value}”",
|
||||
],
|
||||
M02_HopyoImport: ["일위대가 불러오기", "Import unit-price sheets"],
|
||||
M02_HopyoUnitTip: [
|
||||
"단위 환산 — 우리 수량 = 엑셀 수량 × 이 값",
|
||||
"Unit conversion — our qty = Excel qty × this value",
|
||||
],
|
||||
M02_HopyoUnitSame: ["단위 같음 ×1", "Same unit ×1"],
|
||||
M02_HopyoUnitLead: ["엑셀 1 {from} = 우리", "Excel 1 {from} = ours"],
|
||||
M02_HopyoUnitThick: ["두께 {n}m 로 추정", "Estimated from thickness {n}m"],
|
||||
M02_HopyoUnitEst: ["규격으로 추정", "Estimated from the spec"],
|
||||
B01_Dashboard_CardPrecombo: ["일위대가", "Unit-cost items"],
|
||||
M02_PrecomboTitle: ["일위대가", "Unit-cost items"],
|
||||
B01_Dashboard_CardSangeun: ["단가산출", "Unit-price analyses"],
|
||||
|
||||
Reference in New Issue
Block a user