Merge remote-tracking branch 'origin/main_laptop_1' into CODEX
This commit is contained in:
@@ -90,6 +90,8 @@ def _est_line(ln: dict) -> dict:
|
||||
one = {k: ln.get(k) for k in _EST_KEYS if ln.get(k) not in (None, [], False, "")}
|
||||
if one.get("자원"):
|
||||
one["자원"] = {k: ln["자원"].get(k) for k in _RES_KEYS}
|
||||
if ln["자원"].get("같은구성"): # 816-2 같은 파일에서 구성이 같은 단독 기계
|
||||
one["자원"]["같은구성"] = ln["자원"]["같은구성"]
|
||||
return one
|
||||
|
||||
|
||||
|
||||
@@ -196,7 +196,7 @@ def hourly_link(eq: str, part: str) -> dict | None:
|
||||
logic = machine.logic_of(code)
|
||||
if not logic:
|
||||
return None
|
||||
given = {"기계": _lit(code)}
|
||||
given = {"기계": _lit(code)} if machine.takes_machine(logic) else {}
|
||||
for spec in logic.get("입력") or []:
|
||||
if spec.get("이름") != "기계":
|
||||
given[spec["이름"]] = _lit((spec.get("고르기") or [""])[0])
|
||||
|
||||
@@ -320,7 +320,8 @@ class Block:
|
||||
r |= {"단가": prices}
|
||||
if quoted := self.quote(r)[1]: # 815-1 K5 — 세 줄 견적 = 값묶음 그대로 · 표지 한 번
|
||||
self.mark(rows[0], quoted.removeprefix(NOTE))
|
||||
parts = em.machine_parts(r) if self.keys else [] # 812-9 기계 찾기 · 조합 = 줄 둘
|
||||
make = (rows[0].get("자원") or {}).get("같은구성") # 816-2 같은 파일 구성 대조
|
||||
parts = em.machine_parts(r, make) if self.keys else [] # 812-9 기계 찾기 · 조합 = 줄 둘
|
||||
r |= {"찾기": parts[0]} if parts else {}
|
||||
r |= {"조합첫": True} if len(parts) > 1 else {}
|
||||
head = next(
|
||||
@@ -366,7 +367,8 @@ class Block:
|
||||
price = (ln.get("연결단가") or [ln["결과"][-1]])[0]
|
||||
r = self.res(ln, ln["비목"], price if got else ln["결과"][-1])
|
||||
name = self.o.names.item(f"{ln.get('품명') or r.get('이름') or '줄'} {COMP[ln['비목']]}")
|
||||
parts = em.machine_parts(r) if self.keys else [] # 812-9 기계 찾기 · 조합 = 줄 둘
|
||||
make = (ln.get("자원") or {}).get("같은구성") # 816-2 같은 파일 구성 대조
|
||||
parts = em.machine_parts(r, make) if self.keys else [] # 812-9 기계 찾기 · 조합 = 줄 둘
|
||||
r |= {"찾기": parts[0]} if parts else {}
|
||||
r |= {"조합첫": True} if len(parts) > 1 else {}
|
||||
link, many = (parts[0]["키"], None) if parts else self.quote(r)
|
||||
|
||||
@@ -140,7 +140,7 @@ def _fixed(fx: dict, qty: str | None, rates: list[dict]) -> list[dict]:
|
||||
got = mh.hourly(str(link["기계"]))
|
||||
if not got.get("ok") or not got.get("로직"):
|
||||
return [_warn(f"기계 「{name}」 — {got.get('reason') or '시간당 경비 못 셈'}")]
|
||||
machine = {"기계": f"'{got['원문번호']}'"}
|
||||
machine = {"기계": f"'{got['원문번호']}'"} if mh.takes_machine(mh.logic_of(got["원문번호"]) or {}) else {} # fmt: skip
|
||||
return _pick("기계", qty, 키=got["로직"], 이름=name, 규격=spec, 입력=machine)
|
||||
price = fx.get("단가") or {}
|
||||
costs = [c for c in COSTS if Decimal(str(price.get(c) or 0))] or ["재료비"]
|
||||
|
||||
@@ -0,0 +1,256 @@
|
||||
"""M02 불러오기 재료 표기 다름 찾기(PLAN 817-1 · 요구 773-5) — `M02_Hopyo_Row._m01_picks` 재료 길이 부름.
|
||||
|
||||
서버 단독 · 읽기만 · 별칭 · 정확 일치 · 바탕 이름 유일 짝(`M02_Hopyo_Material.sure`)이 못 찾은 줄만.
|
||||
- `same(줄)` ㉮ 이음 = 이름이 같은 품목(표기만 다름) · 규격 수 묶음 같음 · 규격 낱말 안 부딪힘 · 그런 우리
|
||||
품목 하나(지역 · 관급/사급 줄은 한 품목) → 그 자재 줄 · 아니면 None.
|
||||
- `narrow(줄)` ㉯ 후보 좁힘 = 이름 같은 품목 묶음을 규격 낱말(어근 「초본류」 → 초본)을 품은 줄 · 수 묶음
|
||||
닮은 줄 · 덧말 적은 줄 차례로(자동 이음 아님 · 1 순위 맨 위).
|
||||
- `spec_first(후보, 줄)` = 후보 가운데 규격 수 묶음(둘 이상)이 줄과 같은 것 먼저.
|
||||
|
||||
이름 같음: 바탕(괄호 · 지역 · 관급/사급 꼬리 · 등급 「1급」 뗌 · 빈칸 · 점 「P.V.C」 · 소리 표기 `SPELL`) 같음 ·
|
||||
괄호 차례 「패널(유로폼)」 = 「유로폼패널」(글자 묶음 같음) · 괄호 이름 「서비스볼트(고장력볼트)」 = 고장력볼트 ·
|
||||
(약한 같음 — 수 묶음이 같을 때만) 끝 낱말 같음 「지수판」 ↔ 「PVC지수판」 · 앞 낱말 「구역화물자동차」 ↔ 구역화물 ·
|
||||
규격에 든 이름 「안고초」 ↔ 산림종자 안고초(가져온 규격 빔).
|
||||
수 묶음: 규격 + 이름 괄호의 수(차례 무관) · KS 번호 · 무게(kg · ㎏/포 · m/kg) · 제곱 꼬리(㎡ · ㎠) 뗌 ·
|
||||
천 단위 쉼표 · 같음 > 낱마다 단위 배(1 · 10 · 100 · 1000 — 3.6×3.0×3.0 m = 30×30×3600 mm) > 한쪽이 다른 쪽에 듦
|
||||
(이름 같을 때만 · 덧글 「2000 길이 · 20~30mm」).
|
||||
규격 낱말 부딪힘 = 두 쪽 다 괄호 · 규격 한글 낱말이 있고 두 글자 묶음이 하나도 안 겹침(「연강용」 ↔ 「주철용」) —
|
||||
상대 이름에 든 낱말은 뺌.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import re
|
||||
import unicodedata
|
||||
from functools import lru_cache
|
||||
from itertools import permutations
|
||||
from typing import Any
|
||||
|
||||
from M02_MasterTemplete import M02_Hopyo_Match as hm
|
||||
from M02_MasterTemplete import M02_Hopyo_Material as hmat
|
||||
|
||||
SPELL = (
|
||||
(r"칠렌", "틸렌"),
|
||||
(r"매쉬", "메쉬"),
|
||||
(r"신나(?![가-힣])", "신너"),
|
||||
(r"pipe", "파이프"),
|
||||
) # 소리 · 영문 표기
|
||||
NOISE = frozenset({"이내", "이하", "이상", "국산", "포함"})
|
||||
SCALES = (1, 10, 100, 1000)
|
||||
_STEM = re.compile(r"(?<=..)[류형용]$")
|
||||
_KS = re.compile(r"ks\s*[a-z]?\s*-?\s*\d+")
|
||||
_SQUARE = re.compile(r"(?:c|m)?m[23](?![\d.])")
|
||||
_THOUSAND = re.compile(r"(?<=\d),(?=\d{3}(?!\d))")
|
||||
_NUM = re.compile(r"\d+(?:\.\d+)?")
|
||||
_PAREN = re.compile(r"\(([^()]*)\)")
|
||||
|
||||
|
||||
def _low(text: Any) -> str:
|
||||
t = unicodedata.normalize("NFKC", str(text or "")).lower().replace("m/m", "mm")
|
||||
return hmat._APART.sub("", t)
|
||||
|
||||
|
||||
def _tidy(text: str) -> str:
|
||||
t = re.sub(r"(?<=[a-z])[.\s]+(?=[a-z])", "", text)
|
||||
for a, b in SPELL:
|
||||
t = re.sub(a, b, t)
|
||||
t = re.sub(r"\d+급", "", t)
|
||||
return re.sub(r"[^가-힣a-z0-9]", "", t)
|
||||
|
||||
|
||||
def _bag(text: str) -> tuple[float, ...]:
|
||||
t = _THOUSAND.sub("", _SQUARE.sub("", _KS.sub("", text)))
|
||||
return tuple(sorted(float(x) for x in _NUM.findall(t)))
|
||||
|
||||
|
||||
def _quals(text: str) -> frozenset[str]:
|
||||
words = (w for w in re.findall(r"[가-힣]{2,}", text) if not hmat._region(w))
|
||||
return frozenset(w for w in words if w not in hmat.SUPPLY and w not in NOISE)
|
||||
|
||||
|
||||
def _item(name: Any, spec: Any) -> dict[str, Any]:
|
||||
"""이름 · 규격 → 바탕 · 괄호 조각 · 수 묶음 · 규격 낱말 · 온 이름 글."""
|
||||
low, spec = _low(name), _low(spec)
|
||||
parens = _PAREN.findall(low)
|
||||
pieces = tuple(p for p in (_tidy(x) for x in parens for x in x.split(",")) if p)
|
||||
core = _tidy(hmat._bare(low))
|
||||
return {
|
||||
"core": core,
|
||||
"pieces": pieces,
|
||||
"chars": "".join(sorted(core + "".join(pieces))),
|
||||
"bag": _bag(f"{spec} {' '.join(parens)}"),
|
||||
"quals": _quals(f"{' '.join(pieces)} {spec}"),
|
||||
"whole": core + "".join(pieces),
|
||||
"spec": _tidy(spec),
|
||||
}
|
||||
|
||||
|
||||
@lru_cache(maxsize=2)
|
||||
def _book(_got: int) -> dict[str, Any]:
|
||||
rows, _ = hmat.pool()
|
||||
out: list[dict[str, Any]] = []
|
||||
by: dict[str, dict[str, list[int]]] = {
|
||||
"core": {},
|
||||
"chars": {},
|
||||
"piece": {},
|
||||
"tail": {},
|
||||
"spec": {},
|
||||
}
|
||||
for at, r in enumerate(rows):
|
||||
one = {**_item(r["one"]["이름"], r["one"]["규격"]), "row": r}
|
||||
out.append(one)
|
||||
c = one["core"]
|
||||
by["core"].setdefault(c, []).append(at)
|
||||
by["chars"].setdefault(one["chars"], []).append(at)
|
||||
by["spec"].setdefault(one["spec"], []).append(at)
|
||||
for p in one["pieces"]:
|
||||
by["piece"].setdefault(p, []).append(at)
|
||||
for i in range(1, len(c) - 1): # 끝 낱말(두 글자 이상)
|
||||
by["tail"].setdefault(c[i:], []).append(at)
|
||||
return {"줄": out, "찾기": by}
|
||||
|
||||
|
||||
def book() -> dict[str, Any]:
|
||||
return _book(id(hm.targets()))
|
||||
|
||||
|
||||
def _related(me: dict[str, Any]) -> dict[int, bool]:
|
||||
"""이름 같은 우리 줄 → {차례: 굳셈(True) · 약함(False)}."""
|
||||
by, c = book()["찾기"], me["core"]
|
||||
strong = [*by["core"].get(c, []), *by["chars"].get(me["chars"], []), *by["piece"].get(c, [])]
|
||||
if (
|
||||
c not in by["core"]
|
||||
): # 괄호 이름(「서비스볼트(고장력볼트)」) — 바탕 이름이 우리 표에 없을 때만
|
||||
strong += [i for p in me["pieces"] for i in by["core"].get(p, [])]
|
||||
weak = (
|
||||
[*by["tail"].get(c, [])] if len(c) >= 2 else []
|
||||
) # 우리 이름 끝 = 가져온 이름(「PVC지수판」)
|
||||
for k in range(
|
||||
2, len(c) - 1
|
||||
): # 우리 이름 = 가져온 이름 앞(「구역화물자동차」) · 뒤 덧말이 우리 낱말 아님
|
||||
if c[k:] not in by["core"] and c[k:] not in by["tail"]:
|
||||
weak += by["core"].get(c[:k], [])
|
||||
out = dict.fromkeys(weak, False)
|
||||
out.update(dict.fromkeys(strong, True))
|
||||
return out
|
||||
|
||||
|
||||
def _bigrams(words: frozenset[str]) -> set[str]:
|
||||
return {w[i : i + 2] for w in words for i in range(len(w) - 1)}
|
||||
|
||||
|
||||
def _qual_fit(me: dict[str, Any], x: dict[str, Any]) -> tuple[bool, bool, bool]:
|
||||
"""(부딪힘, 겹침, 낱말 있음) — 상대 온 이름에 든 낱말은 뺌."""
|
||||
a = frozenset(w for w in me["quals"] if _tidy(w) not in x["whole"])
|
||||
b = frozenset(w for w in x["quals"] if _tidy(w) not in me["whole"])
|
||||
share = bool(_bigrams(a) & _bigrams(b))
|
||||
return bool(a and b and not share), share, bool(a or b)
|
||||
|
||||
|
||||
def _near(x: float, y: float) -> bool:
|
||||
return any(abs(x * s - y) <= 1e-6 * max(abs(y), 1) or abs(y * s - x) <= 1e-6 * max(abs(x), 1) for s in SCALES) # fmt: skip
|
||||
|
||||
|
||||
def num_fit(a: tuple[float, ...], b: tuple[float, ...]) -> tuple[int, int]:
|
||||
"""(갈래 3 같음 · 2 단위 배 · 1 한쪽이 듦 · 0 다름, 맞은 수 개수)."""
|
||||
if a == b:
|
||||
return 3, len(a)
|
||||
if a and len(a) == len(b) and len(a) <= 6:
|
||||
if any(all(_near(x, y) for x, y in zip(a, p)) for p in permutations(b)):
|
||||
return 2, len(a)
|
||||
small, big = sorted((list(a), list(b)), key=len)
|
||||
if small and len(small) < len(big):
|
||||
rest = list(big)
|
||||
for x in small:
|
||||
if x not in rest:
|
||||
return 0, 0
|
||||
rest.remove(x)
|
||||
return 1, len(small)
|
||||
return 0, 0
|
||||
|
||||
|
||||
def _fold(x: dict[str, Any]) -> tuple:
|
||||
r = x["row"]
|
||||
return r["base"], r["key"][0], r["key"][2]
|
||||
|
||||
|
||||
def _scored(line: dict[str, Any]) -> tuple[dict[str, Any], list[tuple[tuple, dict[str, Any]]]]:
|
||||
me = _item(line.get("이름"), line.get("규격"))
|
||||
rows = book()["줄"]
|
||||
out = []
|
||||
for at, strong in _related(me).items():
|
||||
x = rows[at]
|
||||
clash, share, words = _qual_fit(me, x)
|
||||
if not clash:
|
||||
out.append(
|
||||
((strong, share, *num_fit(me["bag"], x["bag"]), words, x["core"] == me["core"]), x)
|
||||
)
|
||||
return me, out
|
||||
|
||||
|
||||
def _pick(group: list[dict[str, Any]], line: dict[str, Any]) -> dict[str, Any]:
|
||||
"""한 품목의 변형 줄들 → 하나(줄 관급/사급 › 줄 지역 › 서울 › 같은 단위 › 엑셀 차례 — `M02_Elem_Alias.one` 과 같은 차례)."""
|
||||
supply, regions = hmat._wants(line)
|
||||
unit = hm.mm.plain_unit(line.get("단위"))
|
||||
|
||||
def rank(r: dict[str, Any]) -> tuple:
|
||||
return (
|
||||
supply is not None and r["supply"] not in (None, supply),
|
||||
bool(regions) and not hmat._hit(r, regions),
|
||||
"서울" not in r["region"],
|
||||
r["key"][1] != unit,
|
||||
r["at"],
|
||||
)
|
||||
|
||||
return min((x["row"] for x in group), key=rank)["one"]
|
||||
|
||||
|
||||
def same(line: dict[str, Any]) -> dict[str, Any] | None:
|
||||
"""㉮ 표기만 다름 — 우리 품목 하나면 그 자재 줄(`hm.targets` 꼴) · 아니면 None."""
|
||||
me, scored = _scored(line)
|
||||
if not me["spec"]: # 가져온 규격 빔 = 규격에 든 이름(「안고초」)만 · 그 밖은 ㉯
|
||||
hits = [book()["줄"][i] for i in book()["찾기"]["spec"].get(me["whole"], [])]
|
||||
hits = [x for x in hits if not x["bag"] and not me["bag"]]
|
||||
return _pick(hits, line) if hits and len({_fold(x) for x in hits}) == 1 else None
|
||||
ok = []
|
||||
for (strong, share, tier, common, words, same_core), x in scored:
|
||||
if not tier or (tier == 1 and not strong):
|
||||
continue
|
||||
if not me["bag"] and not x["bag"] and not share and words:
|
||||
continue # 수 없는 규격 = 규격 낱말이 겹쳐야
|
||||
ok.append(((tier, common, share, strong, same_core), x))
|
||||
if not ok:
|
||||
return None
|
||||
best = max(k for k, _ in ok)
|
||||
group = [x for k, x in ok if k == best]
|
||||
return _pick(group, line) if len({_fold(x) for x in group}) == 1 else None
|
||||
|
||||
|
||||
def _stems(me: dict[str, Any]) -> set[str]:
|
||||
return {_STEM.sub("", w) for w in me["quals"]}
|
||||
|
||||
|
||||
def narrow(line: dict[str, Any], limit: int = 5) -> list[dict[str, Any]]:
|
||||
"""㉯ 이름 같은 품목 묶음(굳센 같음) → 규격 낱말 어근 품은 줄 › 수 묶음 닮음 › 덧말 적음 차례(품목마다 하나)."""
|
||||
me, scored = _scored(line)
|
||||
stems = _stems(me)
|
||||
rows = [(k, x) for k, x in scored if k[0]]
|
||||
rank = []
|
||||
for (_strong, _share, tier, common, _words, same_core), x in rows:
|
||||
text = x["whole"] + x["spec"]
|
||||
hit = sum(s in text for s in stems)
|
||||
rank.append(((-hit, -tier, -common, not same_core, len(x["spec"]), x["row"]["at"]), x))
|
||||
seen, out = set(), []
|
||||
for _k, x in sorted(rank, key=lambda t: t[0]):
|
||||
if (f := _fold(x)) not in seen:
|
||||
seen.add(f)
|
||||
out.append(_pick([y for _, y in rank if _fold(y) == f], line))
|
||||
return out[:limit]
|
||||
|
||||
|
||||
def spec_first(cands: list[dict[str, Any]], line: dict[str, Any]) -> list[dict[str, Any]]:
|
||||
"""후보 차례 — 규격 수 묶음(둘 이상)이 줄과 같은 것 먼저(「중하중그레이팅 395×995×52」)."""
|
||||
mine = _item(line.get("이름"), line.get("규격"))["bag"]
|
||||
if len(mine) < 2:
|
||||
return cands
|
||||
return sorted(cands, key=lambda c: _item(c.get("이름"), c.get("규격"))["bag"] != mine)
|
||||
@@ -103,7 +103,7 @@ def _m01_picks(kind: str, name: str, spec: str, unit: str, _whole: int) -> list[
|
||||
clean, apart = hmat.clean({"이름": name, "규격": spec, "단위": unit})
|
||||
sure, _ = hmat.sure(clean)
|
||||
got = [r["one"] for r in sure[:1] if not r["one"].get("전국")]
|
||||
got = got or hmat.candidates(clean, TOP)
|
||||
got = got or _found(clean)
|
||||
more = {"별도계상": True} if apart else {}
|
||||
return [
|
||||
pick("재료", x.get("자재"), x["이름"], x.get("규격"), x.get("단위"), **more)
|
||||
@@ -113,6 +113,23 @@ def _m01_picks(kind: str, name: str, spec: str, unit: str, _whole: int) -> list[
|
||||
return []
|
||||
|
||||
|
||||
def _found(line: dict[str, Any]) -> list[dict[str, Any]]:
|
||||
"""817-1 재료 — ㉮ 표기만 다름 하나 › 옛 후보 하나 › ㉯ 이름 같은 묶음 좁힘 + 규격 수 같은 후보 먼저 + 옛 후보."""
|
||||
from M02_MasterTemplete import M02_Hopyo_MatSame as ms
|
||||
|
||||
if one := ms.same(line):
|
||||
return [one]
|
||||
cands = hmat.candidates(line, 4 * TOP)
|
||||
if len({x.get("자재") for x in cands[:TOP]}) == 1:
|
||||
return cands[:TOP]
|
||||
out, seen = [], set()
|
||||
for x in [*ms.narrow(line, TOP), *ms.spec_first(cands, line)[:TOP], *cands[:TOP]]:
|
||||
if x.get("자재") not in seen:
|
||||
seen.add(x.get("자재"))
|
||||
out.append(x)
|
||||
return out
|
||||
|
||||
|
||||
def _alias(kind: str, name: str, spec: str) -> list[dict[str, Any]]:
|
||||
"""M01 별칭 칸 이음(`M02_Elem_Alias`) — 인력 = 이름 · 재료 = 이름 + 규격 › 표기 정규화 정확 일치 ·
|
||||
같은 품목 지역 줄은 하나."""
|
||||
|
||||
@@ -305,6 +305,48 @@ def find_all(name: Any, spec: Any = "") -> list[dict[str, Any]]:
|
||||
return out
|
||||
|
||||
|
||||
# ── 같은 파일 안 구성 대조로 가름(816-2 · 요구 773-3) ───────────────────────────
|
||||
MADE = "같은 파일 구성 같음"
|
||||
|
||||
|
||||
def peers(machines: dict[Any, tuple[str, str, dict]]) -> dict[Any, list[list[str]]]:
|
||||
"""같은 파일 기계 {코드: (이름, 규격, 구성 {손료 [[계수, 가격]], 주연료, 조종원})} → {코드: 구성이 같은
|
||||
단독 기계(손료 줄 하나) [이름, 규격] 들} — 낱 기계 = 구성 통째 같음 · 조합 = 손료 줄마다 그 줄 하나뿐인
|
||||
단독 기계. 같은 파일 · 같은 때의 값끼리(우리 표 값과 안 맞댐)."""
|
||||
alone = {c: m for c, m in machines.items() if len(m[2].get("손료") or []) == 1}
|
||||
out: dict[Any, list[list[str]]] = {}
|
||||
for c, (_, _, make) in machines.items():
|
||||
loss = make.get("손료") or []
|
||||
if len(loss) == 1:
|
||||
got = [o for o, m in alone.items() if o != c and m[2] == make]
|
||||
else:
|
||||
got = [o for o, m in alone.items() if o != c and m[2]["손료"][0] in loss]
|
||||
if got:
|
||||
out[c] = [[alone[o][0], alone[o][1]] for o in got]
|
||||
return out
|
||||
|
||||
|
||||
def narrow(parts: list[dict[str, Any]], same: list | None) -> list[dict[str, Any]]:
|
||||
"""「후보 여럿」 부분 → 같은 파일에서 구성이 같은 단독 기계 이름 · 규격으로 다시 찾아(`find`) 그 후보
|
||||
가운데 키 하나로 모이면 그 줄 · 여럿 · 없음 · 대조 없음 = 그대로(우리 값으로 넘겨짚지 않음)."""
|
||||
if not same:
|
||||
return parts
|
||||
found = [(n, s, find(n, s)) for n, s in same]
|
||||
out = []
|
||||
for p in parts:
|
||||
if p["판정"] == MANY:
|
||||
mine = {c["키"] for c in p["후보"]}
|
||||
keys = {
|
||||
g["키"] for _, _, g in found if g["판정"] in (LINKED, ALIAS) and g["키"] in mine
|
||||
}
|
||||
if len(keys) == 1:
|
||||
key = keys.pop()
|
||||
n, s = next((n, s) for n, s, g in found if g["키"] == key)
|
||||
p = {**p, **_ans(LINKED, [table()[2][key]], n, f"{MADE} — {n} {s}".strip())}
|
||||
out.append(p)
|
||||
return out
|
||||
|
||||
|
||||
def mark(got: dict[str, Any], name: Any, spec: Any) -> str | None:
|
||||
"""못 이은 기계 표지 글(「참고 · 」 머리) — 이음 · 별칭 = None."""
|
||||
said = f"「{' '.join(x for x in (str(name or ''), str(spec or '')) if x)}」"
|
||||
|
||||
@@ -56,7 +56,7 @@ def logic_of(code: str) -> dict | None:
|
||||
(「조종원 없는 기종」 목록에 겹친 기계 — 진동롤러(핸드가이드식) · 고소작업차 …)."""
|
||||
fit = [r for r in _family() if code in [str(c) for c in _codes(r)]]
|
||||
if not fit:
|
||||
return None
|
||||
return _own(code)
|
||||
op = _operated(code)
|
||||
|
||||
def order(row: dict) -> tuple:
|
||||
@@ -67,6 +67,25 @@ def logic_of(code: str) -> dict | None:
|
||||
return min(fit, key=order)
|
||||
|
||||
|
||||
def _own(code: str) -> dict | None:
|
||||
"""그 기계만의 시간당 경비 식(816-1 · 4륜구동 1톤 덤프트럭 = GF000145 · 816-3 체인톱 = GF000337) —
|
||||
「기계」 입력 없는 기계경비식이 그 원문번호 기계 줄 값(`EQ….가격` · `EQ….손료계수`)을 읽으면 그 식."""
|
||||
rows = store.loaded()[1].index.get("EQ", {}).values()
|
||||
keys = [f"{r.get('키')}." for r in rows if code and str(r.get("원문번호")) == code]
|
||||
for data in store.loaded()[0].values():
|
||||
for row in data.get("줄") or [] if data.get("그룹") == "로직" else []:
|
||||
if (row.get("부품") or {}).get("갈래") != "기계경비식" or takes_machine(row):
|
||||
continue
|
||||
if any(k in str(row.get("중간")) + str(row.get("호표")) for k in keys):
|
||||
return row
|
||||
return None
|
||||
|
||||
|
||||
def takes_machine(logic: dict) -> bool:
|
||||
"""식이 「기계」 입력(원문번호 고르기)을 받는지 — 안 받는 식(GF000145)엔 기계='…' 를 안 넘김."""
|
||||
return any(i.get("이름") == "기계" for i in logic.get("입력") or [])
|
||||
|
||||
|
||||
def hourly(key: str) -> dict[str, Any]:
|
||||
"""`{ok, 이름, 규격, 로직, 원문번호, 노무비, 재료비, 경비, 계}` · 못 셈 = `{ok: False, reason}`."""
|
||||
if not key.startswith("EQ"):
|
||||
@@ -84,7 +103,8 @@ def hourly(key: str) -> dict[str, Any]:
|
||||
for i in logic.get("입력") or []
|
||||
if i.get("이름") != "기계"
|
||||
)
|
||||
call = f"로직({logic['키']}, 기계='{code}'{args})"
|
||||
mine = f", 기계='{code}'" if takes_machine(logic) else ""
|
||||
call = f"로직({logic['키']}{mine}{args})"
|
||||
line = {"종류": "로직", "요소": call, "이름": "기계", "단위": "시간", "수량": "1"}
|
||||
holder = {
|
||||
"키": "", "원문번호": "", "이름": "기계 시간", "결과단위": "원/식", "출처": "자체",
|
||||
|
||||
@@ -22,7 +22,15 @@ STM_SHEETS = (
|
||||
"경비목록표",
|
||||
"중기목록표",
|
||||
)
|
||||
EST_SHEETS = ("단가산출총괄표", "단가산출", "기계경비총괄표", "자재단가", "노임단가", "견적단가")
|
||||
EST_SHEETS = (
|
||||
"단가산출총괄표",
|
||||
"단가산출",
|
||||
"기계경비총괄표",
|
||||
"기계경비",
|
||||
"자재단가",
|
||||
"노임단가",
|
||||
"견적단가",
|
||||
)
|
||||
_X7, _X5 = "_x0007_", "_x0005_"
|
||||
_HEAD = re.compile(r"BDCOD\|([A-Z]\d+).*?POSS\|(\d+).*?POSE\|(\d+)")
|
||||
|
||||
|
||||
@@ -178,13 +178,14 @@ def reprice(logic: dict, where: dict, price: dict[str, float]) -> int:
|
||||
|
||||
|
||||
# ── 기계 부분(812-9 · 조합 기계 = 줄 둘) ─────────────────────────────────
|
||||
def machine_parts(src: dict) -> list[dict]:
|
||||
"""기계 자원 → 부분 찾기(`M02_Machine_Find.find_all` · 조합 = 둘 이상) · 기계 아님 = []."""
|
||||
def machine_parts(src: dict, same: list | None = None) -> list[dict]:
|
||||
"""기계 자원 → 부분 찾기(`M02_Machine_Find.find_all` · 조합 = 둘 이상) · 기계 아님 = [] ·
|
||||
same = 같은 파일에서 구성이 같은 단독 기계 [[이름, 규격]](816-2 · 있으면 「후보 여럿」을 가름)."""
|
||||
if src.get("갈래") != "기계":
|
||||
return []
|
||||
from M02_MasterTemplete import M02_Machine_Find as mfind
|
||||
|
||||
return mfind.find_all(src.get("이름"), src.get("규격"))
|
||||
return mfind.narrow(mfind.find_all(src.get("이름"), src.get("규격")), same)
|
||||
|
||||
|
||||
def first_spec(one: dict, parts: list[dict]) -> dict:
|
||||
|
||||
@@ -228,9 +228,36 @@ def marks(text: str) -> dict[str, Any]:
|
||||
return out
|
||||
|
||||
|
||||
def makes(t: dict[str, list[dict[str, Any]]]) -> dict[str, dict[str, Any]]:
|
||||
"""기계 구성(816-2 · MachMainDat — TitleCode 머리 다음 줄부터 빈 줄까지) → {코드: {손료 [[계수, 가격]
|
||||
(Index -3 GN · TU)] · 주연료 ℓ(-2 「주연료」 GN) · 조종원(-1 GN > 0)}} — 손료 줄 없는 기계는 뺌."""
|
||||
out: dict[str, dict[str, Any]] = {}
|
||||
cur = None
|
||||
for r in t.get("MachMainDat", []):
|
||||
if r.get("TitleCode"):
|
||||
cur = out.setdefault(r["TitleCode"], {"손료": [], "주연료": None, "조종원": False})
|
||||
elif not r.get("Name"):
|
||||
cur = None
|
||||
elif cur is not None:
|
||||
i, gn = r.get("Index"), r.get("GN")
|
||||
if i == -3 and gn:
|
||||
cur["손료"].append([gn, r.get("TU")])
|
||||
elif i == -2 and cur["주연료"] is None and re.sub(r"\s", "", r["Name"]) == "주연료":
|
||||
cur["주연료"] = gn
|
||||
elif i == -1 and (gn or 0) > 0:
|
||||
cur["조종원"] = True
|
||||
return {k: v for k, v in out.items() if v["손료"]}
|
||||
|
||||
|
||||
def resources(t: dict[str, list[dict[str, Any]]]) -> dict[tuple[int, str], dict[str, Any]]:
|
||||
"""(LinkIndex, 코드) → 자원 머리(표 · 이름 · 규격 · 단위 · 저장 금액)."""
|
||||
"""(LinkIndex, 코드) → 자원 머리(표 · 이름 · 규격 · 단위 · 저장 금액 · 기계 = 같은 파일에서 구성이
|
||||
같은 단독 기계 `같은구성` [[이름, 규격]] · 816-2)."""
|
||||
from M02_MasterTemplete import M02_Machine_Find as mfind
|
||||
|
||||
out = {}
|
||||
heads = {r.get("Code"): r for r in t.get("MachTitleDat", [])}
|
||||
made = {c: (heads[c].get("Name"), heads[c].get("Size"), m) for c, m in makes(t).items() if c in heads} # fmt: skip
|
||||
same = mfind.peers(made)
|
||||
for link, name in SOURCE.items():
|
||||
for r in t.get(name, []):
|
||||
if r.get("Code"):
|
||||
@@ -240,7 +267,11 @@ def resources(t: dict[str, list[dict[str, Any]]]) -> dict[tuple[int, str], dict[
|
||||
"규격": r.get("Size"),
|
||||
"단위": r.get("Unite"),
|
||||
"저장": _amounts(r, AMT),
|
||||
}
|
||||
} | (
|
||||
{"같은구성": same[r["Code"]]}
|
||||
if name == "MachTitleDat" and r["Code"] in same
|
||||
else {}
|
||||
)
|
||||
return out
|
||||
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@ from __future__ import annotations
|
||||
NOTE = "참고 · " # `M02_San_Emit.NOTE` 와 같음(사람 확인 표지 머리)
|
||||
OIL_BASE = ("전국평균", "일반용(갑)Ⅰ 저압") # 유가 · 전력 기본(요구 748 · 우리 기초 기본값)
|
||||
QUOTE = "견적 — 그대로(이름 · 값) · [단가 갱신] 대상 아님" # 815-1 K5 · 요구 762-3
|
||||
APART = "별도계상 · 품목은 프로젝트에서" # 817-1 — 일위대가 길(`M02_CheckApart`)과 같은 글 · 요소 잇기 없음
|
||||
|
||||
|
||||
def m01(kind: str, src: dict) -> tuple[str | None, str | None]:
|
||||
@@ -24,6 +25,10 @@ def m01(kind: str, src: dict) -> tuple[str | None, str | None]:
|
||||
from M02_MasterTemplete import M02_Hopyo_Row as hrow
|
||||
|
||||
line = {k: src.get(k) or "" for k in ("이름", "규격", "단위")}
|
||||
if kind == "재료" and apart(
|
||||
line
|
||||
): # 817-1 — 원가 합 밖(원본 금액 그대로) · 품목은 프로젝트에서 이음
|
||||
return None, f"{NOTE}{APART}"
|
||||
if (
|
||||
kind == "기계"
|
||||
): # 812-9 — 하나 = 이음 · 아니면 표지(후보 여럿 · 못 찾음 · 조용히 넘기지 않음)
|
||||
@@ -68,6 +73,13 @@ def m01(kind: str, src: dict) -> tuple[str | None, str | None]:
|
||||
return None, None
|
||||
|
||||
|
||||
def apart(line: dict) -> bool:
|
||||
"""줄 이름 · 규격에 「별도계상」(`M02_Hopyo_Material.clean`)."""
|
||||
from M02_MasterTemplete import M02_Hopyo_Material as hmat
|
||||
|
||||
return hmat.clean(line)[1]
|
||||
|
||||
|
||||
def quote(src: dict) -> tuple[str | None, str | None]:
|
||||
"""견적 · 운임 · 수수료 꼴 줄(갈래 밖 — EST 견적 · STmate 고정 코드 · 815-1 K5) → 우리 자재에 이어지면
|
||||
(코드 · 별칭 · 정확 일치 · 단가 줄 · 우리 값 있을 때) 그 키 + 갈래 재료(우리 값 · 엑셀 값은 검산) ·
|
||||
@@ -143,11 +155,13 @@ def why(item: dict) -> str:
|
||||
from M02_MasterTemplete import M02_Hopyo_Elem as elem
|
||||
from M02_MasterTemplete import M02_Hopyo_Row as hrow
|
||||
|
||||
if apart({"이름": name, "규격": res.get("규격") or ""}):
|
||||
return APART
|
||||
if elem.oil_rows(name):
|
||||
return "유가 후보 여럿 — 고름"
|
||||
line = {"이름": name, "규격": res.get("규격") or "", "단위": res.get("단위") or ""}
|
||||
if hrow.m01_picks("재료", line):
|
||||
return "자재 후보 여럿 — 고름"
|
||||
if picks := hrow.m01_picks("재료", line): # 817-1 ㉯ 1 순위를 글에(고르기 창은 이름 찾기)
|
||||
return f"자재 후보 여럿 — 고름 · 1 순위 {picks[0]['이름']} {picks[0]['규격']}".strip()
|
||||
return "운임표 없음" if any(w in name for w in FREIGHT) else "자재 없음(우리 자재표)"
|
||||
if kind == "인력":
|
||||
return "노임 없음"
|
||||
|
||||
@@ -145,9 +145,42 @@ def resources(sheets: dict[str, list]) -> dict[str, list[dict[str, Any]]]:
|
||||
vals = {k: float(v or 0) for k, v in vals.items()}
|
||||
got.append({**_drop(one), "표": sheet, "값": vals} | ({"칸": note} if note else {}))
|
||||
out[kind] = got
|
||||
from M02_MasterTemplete import M02_Machine_Find as mfind
|
||||
|
||||
same = mfind.peers(makes(sheets.get("기계경비") or []))
|
||||
for one in out["기계"]: # 816-2 같은 파일에서 구성이 같은 단독 기계
|
||||
if got := same.get((flat(one["이름"]), flat(one["규격"]))):
|
||||
one["같은구성"] = got
|
||||
return out
|
||||
|
||||
|
||||
def makes(rows: list[list[Any]]) -> dict[tuple[str, str], dict[str, Any]]:
|
||||
"""EST 엑셀 「기계경비」 시트(816-2) — 「N. 이름 · 규격」 머리 아래 빈 줄까지: 「손료 가격 ₩× 계수 ×
|
||||
10(-7)」 · 「주연료 종류 ℓ」 · 「직종 1 인」 → {(이름, 규격) 붙임: (이름, 규격, {손료 [[계수, 가격]] ·
|
||||
주연료 ℓ · 조종원})}."""
|
||||
out: dict[tuple[str, str], tuple[str, str, dict[str, Any]]] = {}
|
||||
cur = None
|
||||
for row in rows:
|
||||
v = [c for c in row if c not in (None, "")]
|
||||
head = re.match(r"\d+\.\s*(.+)", str(v[0])) if v else None
|
||||
if head:
|
||||
spec = v[1] if len(v) > 1 and isinstance(v[1], str) else ""
|
||||
name = head.group(1).strip()
|
||||
make = {"손료": [], "주연료": None, "조종원": False}
|
||||
cur = out.setdefault((flat(name), flat(spec)), (name, spec, make))[2]
|
||||
elif not v:
|
||||
cur = None
|
||||
elif cur is not None:
|
||||
word = flat(v[0])
|
||||
if word.startswith("손료") and "₩×" in v and (at := v.index("₩×") + 1) < len(v):
|
||||
cur["손료"].append([number(v[at]), number(v[1])])
|
||||
elif word == "주연료" and len(v) > 2 and cur["주연료"] is None:
|
||||
cur["주연료"] = number(v[2])
|
||||
elif len(v) > 2 and v[2] == "인" and (number(v[1]) or 0) > 0:
|
||||
cur["조종원"] = True
|
||||
return {k: m for k, m in out.items() if m[2]["손료"] and None not in m[2]["손료"][0]}
|
||||
|
||||
|
||||
def _drop(one: dict) -> dict:
|
||||
return {k: v for k, v in one.items() if k != "줄"}
|
||||
|
||||
|
||||
@@ -154,7 +154,11 @@ class Lines:
|
||||
f"단산{one['번']}" if kind == "산근" else f"{one['표']}#{one['번']}"
|
||||
) # 원 코드 없음 = 표 줄 번
|
||||
out = {"코드": code, "종류": kind, "표": one["표"]}
|
||||
return out | {k: one[k] for k in ("이름", "규격", "단위")} | {"저장": dict(one["값"])}
|
||||
out |= {
|
||||
k: one[k]
|
||||
for k in ("이름", "규격", "단위") + (("같은구성",) if "같은구성" in one else ())
|
||||
}
|
||||
return out | {"저장": dict(one["값"])}
|
||||
|
||||
def link(self, ln: dict, kind: str, one: dict | None) -> None:
|
||||
ln["연결"] = LINK[kind] if kind in LINK else 62
|
||||
|
||||
@@ -0,0 +1,101 @@
|
||||
행 가져온 이름 가져온 규격 쓰임 기대 우리 키 우리 이름 우리 규격 확신
|
||||
1 접착제 11 후보 사람이 고름 — 틀린 줄에 자동으로 잇지 않음
|
||||
2 패널(유로폼) 600*1200mm 10 이음 MT000467 유로폼패널 600×1200㎜ 표기만 다름
|
||||
3 각재 외송 8 이음 MT000393 각 재(외송) 30×30×3600mm 눈으로 추정
|
||||
4 요소 8 이음 MT006763 요 소 N:46% 눈으로 추정
|
||||
5 내부코너패널(유로폼) (100x200)x1200mm 7 이음 MT000470 내부코너패널 100×200×1200㎜ 표기만 다름
|
||||
6 결속선(철선) #20 , 0.9M/M 7 후보 사람이 고름 — 틀린 줄에 자동으로 잇지 않음
|
||||
7 각재 (외송) 6 이음 MT000393 각 재(외송) 30×30×3600mm 눈으로 추정
|
||||
8 파형강관 별도계상 6 잇지 않음 별도계상
|
||||
9 금강사 5 잇지 않음 우리 표에 없음
|
||||
10 고무테이프 0.5mm×19mm×10m 5 이음 MT009202 절연 면고무테이프 0.5mm×19mm×10m 눈으로 추정
|
||||
11 낙석방지책 H2500×W2000(표준) 5 후보 사람이 고름 — 틀린 줄에 자동으로 잇지 않음
|
||||
12 낙석방지책 H2500×W2000(단부) 5 후보 사람이 고름 — 틀린 줄에 자동으로 잇지 않음
|
||||
13 종자 초본류 5 후보 1 순위 MT006646 종 자 초본위주형 좁힘(자동 이음 아님)
|
||||
14 종자 목본류 5 후보 1 순위 MT006648 종 자 목본군락형 좁힘(자동 이음 아님)
|
||||
15 어닐링철선(결속선) #20 0.9mm 200.4m/kg 5 이음 MT000209 어닐링철선(결속선),서울 #20, 0.9mm 표기만 다름
|
||||
16 내수합판1급 12mmx1220x2440 4 이음 MT000018 합판(내수), 서울 12t×1220×2440mm 표기만 다름
|
||||
17 어닐링철선(결속선) #20 0.9M/M 4 이음 MT000209 어닐링철선(결속선),서울 #20, 0.9mm 표기만 다름
|
||||
18 유로폼(패널) 600*1200 4 이음 MT000467 유로폼패널 600×1200㎜ 표기만 다름
|
||||
19 내부코너패널 (200+200)*1200 4 이음 MT000472 내부코너패널 (200+200)×1200㎜ 표기만 다름
|
||||
20 합판(내수) 12×1220×2440 4 이음 MT000018 합판(내수), 서울 12t×1220×2440mm 표기만 다름
|
||||
21 조임철물 (직교자재) 4 이음 MT000499 조임철물,서울 직교,자재 표기만 다름
|
||||
22 에폭시수지 YD-128 4 후보 사람이 고름 — 틀린 줄에 자동으로 잇지 않음
|
||||
23 종자 생태복원형,초본류 3 후보 1 순위 MT006646 종 자 초본위주형 좁힘(자동 이음 아님)
|
||||
24 종자 생태복원형,목본류 3 후보 1 순위 MT006648 종 자 목본군락형 좁힘(자동 이음 아님)
|
||||
25 내부코너패널(유로폼) (100+200)*1200mm 3 이음 MT000470 내부코너패널 100×200×1200㎜ 표기만 다름
|
||||
26 유로폼(패널) 600×1200 3 이음 MT000467 유로폼패널 600×1200㎜ 표기만 다름
|
||||
27 콘테이너하우스 창고용(3.0m×6.0m×2.6m) 3 이음 MT007111 콘테이너(창고) 6.0*3.0*2.6m 표기만 다름
|
||||
28 전기뇌관(MS) 3.5m×500개(하이데토) 2 이음 MT005677 전기뇌관 3.5m 눈으로 추정
|
||||
29 BUTTON BIT Ø38×76㎜ 2 후보 사람이 고름 — 틀린 줄에 자동으로 잇지 않음
|
||||
30 유압드릴샹크 T38×CM470 2 후보 사람이 고름 — 틀린 줄에 자동으로 잇지 않음
|
||||
31 유압식드릴스리브 T38×190mm 2 이음 MT005687 유압드릴스리브 T38×190mm 표기만 다름
|
||||
32 미송(판재) 2 후보 사람이 고름 — 틀린 줄에 자동으로 잇지 않음
|
||||
33 산림종자 참싸리(박피) 2 이음 MT006664 산림종자 참싸리(풍화암,강마사토) 눈으로 추정
|
||||
34 유기질퇴비 2 후보 사람이 고름 — 틀린 줄에 자동으로 잇지 않음
|
||||
35 PVC지수판 200x5T KSM3805 2 이음 MT006544 PVC지수판 200×5T 표기만 다름
|
||||
36 PVC용접봉 Ø3.2 (국산) 2 후보 사람이 고름 — 틀린 줄에 자동으로 잇지 않음
|
||||
37 PVC파이프(일반용VG2) 50㎜ × 4m 2 이음 MT002926 PVC파이프(일반관),서울 D=50mm(VG2) 표기만 다름
|
||||
38 혼합골재 영월 2 후보 사람이 고름 — 틀린 줄에 자동으로 잇지 않음
|
||||
39 이형철근 별도계상 2 잇지 않음 별도계상
|
||||
40 안전표지판 삼각,D=900 2 이음 MT003202 교통안전표지판 900,삼각(고휘도) 눈으로 추정
|
||||
41 표지판지주 D=76.3mm, 3.5M 2 후보 사람이 고름 — 틀린 줄에 자동으로 잇지 않음
|
||||
42 블레이드(커터) "d=320 400mm 14""xt3.2" 2 이음 MT005749 블레이드 "14""×3.2mm" 표기만 다름
|
||||
43 떼 2 잇지 않음 우리 표에 없음
|
||||
44 강관비계 φ48.6m/m×2.3mm 2 이음 MT000481 강관비계,서울 Φ48.6*2.3*4.0m 표기만 다름
|
||||
45 철물 2 후보 사람이 고름 — 틀린 줄에 자동으로 잇지 않음
|
||||
46 각재(말뚝) Φ60 2 후보 사람이 고름 — 틀린 줄에 자동으로 잇지 않음
|
||||
47 강관써포트 V4 2600-4000mm 14.3kg 2 이음 MT000487 강관동바리,서울 Φ48.6*2.4,L=2.6~4.0 눈으로 추정
|
||||
48 안고초 2 이음 MT006667 산림종자 안고초 표기만 다름
|
||||
49 비수리 2 이음 MT006665 산림종자 비수리 표기만 다름
|
||||
50 내수합판1급 12m/m 1220×2440 2 이음 MT000018 합판(내수), 서울 12t×1220×2440mm 표기만 다름
|
||||
51 각재(외송) 3.6×3.0×3.0 2 이음 MT000393 각 재(외송) 30×30×3600mm 눈으로 추정
|
||||
52 내수합판1급 12mm 1220×2440 2 이음 MT000018 합판(내수), 서울 12t×1220×2440mm 표기만 다름
|
||||
53 각재(거푸집용,외송) 3.6×3.0×3.0 2 이음 MT000393 각 재(외송) 30×30×3600mm 눈으로 추정
|
||||
54 다이아몬드톱날 "Ø14""×3.2t(콘크리트용)" 2 이음 MT005749 블레이드 "14""×3.2mm" 눈으로 추정
|
||||
55 아세틸렌 용접용 2 이음 MT003163 아세틸렌 98% (용접용) 표기만 다름
|
||||
56 산소 고압가스 2 이음 MT003166 산 소 고압상태 눈으로 추정
|
||||
57 신나 KSM6060 2종(조합페인트류) 2 이음 MT003398 신너(조합페인트용) KSM-6060(2종) 표기만 다름
|
||||
58 초류종자(국내산) 참싸리,비수리,낭아초등 1 후보 사람이 고름 — 틀린 줄에 자동으로 잇지 않음
|
||||
59 종 자 초본형 1 후보 1 순위 MT006646 종 자 초본위주형 좁힘(자동 이음 아님)
|
||||
60 소나무우드칩(멀칭재) 1 후보 사람이 고름 — 틀린 줄에 자동으로 잇지 않음
|
||||
61 3단코어비트 "5""(125mm)" 1 후보 사람이 고름 — 틀린 줄에 자동으로 잇지 않음
|
||||
62 P V C PIPE Φ50M/M,VG1 1 이음 MT002861 PVC파이프(일반관),서울 D=50mm(VG1) 표기만 다름
|
||||
63 콘테이너(창 고) 3×6×2.6 1 이음 MT007111 콘테이너(창고) 6.0*3.0*2.6m 표기만 다름
|
||||
64 강관비계 48.6mm×2.3mm×6m 1 후보 사람이 고름 — 틀린 줄에 자동으로 잇지 않음
|
||||
65 철물 직교자재 1 이음 MT000499 조임철물,서울 직교,자재 눈으로 추정
|
||||
66 문양거푸집(ABS코팅품) 600×1200 1 이음 MT000453 문양거푸집(플라스틱 ABS코팅폼) 600×1200 표기만 다름
|
||||
67 와이어매쉬 #6 100×100 1 이음 MT007130 와이어메쉬(용접철망)관급 #6, 100×100 표기만 다름
|
||||
68 지수판 T5×200 1 이음 MT006544 PVC지수판 200×5T 표기만 다름
|
||||
69 용접봉(연강용) 3.2m/m 1 후보 사람이 고름 — 틀린 줄에 자동으로 잇지 않음
|
||||
70 어닐링철선 (결속선) #6 4.0mm 10.1m/kg 1 후보 사람이 고름 — 틀린 줄에 자동으로 잇지 않음
|
||||
71 구역화물자동차(4.5Ton) 80km이내 1 이음 MT023493 구역화물 4.5톤, 80km 표기만 다름
|
||||
72 구역화물자동차(4.5Ton) 60km이내 1 이음 MT023491 구역화물 4.5톤, 60km 표기만 다름
|
||||
73 산소(6000ℓ) 99%,120kg/㎠ 1 이음 MT003165 산 소 6000ℓ/병 표기만 다름
|
||||
74 아세칠렌 AC 98%,용접용 1 이음 MT003163 아세틸렌 98% (용접용) 표기만 다름
|
||||
75 서비스볼트(고장력볼트) M20×60 1 이음 MT002536 고장력볼트 M20*60mm 표기만 다름
|
||||
76 보조강재(열연강판) T=9-12 1 후보 사람이 고름 — 틀린 줄에 자동으로 잇지 않음
|
||||
77 구역화물자동차(8Ton) 10km이내 1 이음 MT023654 구역화물 8.0톤, 10km 표기만 다름
|
||||
78 구역화물자동차(8Ton) 20km이내 1 이음 MT023655 구역화물 8.0톤, 20km 표기만 다름
|
||||
79 참싸리(박피) 1 이음 MT006664 산림종자 참싸리(풍화암,강마사토) 눈으로 추정
|
||||
80 부산물비료 20kg/포 1 후보 사람이 고름 — 틀린 줄에 자동으로 잇지 않음
|
||||
81 참싸리 박피 1 이음 MT006664 산림종자 참싸리(풍화암,강마사토) 눈으로 추정
|
||||
82 복합비료 21-17-17 20kg/포 1 이음 MT006490 복합비료 21-17-17 표기만 다름
|
||||
83 퇴비(천연부엽토) 20㎏/포 1 이음 MT006788 천연부엽토 20kg/포 눈으로 추정
|
||||
84 문양거푸집(ABS코팅폼) 600×1200×20~30mm 1 이음 MT000453 문양거푸집(플라스틱 ABS코팅폼) 600×1200 표기만 다름
|
||||
85 P.V.C PIPE VG1(50mm) 1 이음 MT002861 PVC파이프(일반관),서울 D=50mm(VG1) 표기만 다름
|
||||
86 다웰바(DOWEL BAR) Ø32×1,000 1 이음 MT006540 다 웰 바 Φ32*1000 표기만 다름
|
||||
87 P.V.C PIPE VG1(35mm) 1 이음 MT002859 PVC파이프(일반관),서울 D=35mm(VG1) 표기만 다름
|
||||
88 휠라 T=15mm,1000×1000mm 1 후보 사람이 고름 — 틀린 줄에 자동으로 잇지 않음
|
||||
89 측구수로관 300×300×2000×120,앵글포함 1 이음 MT015883 측구수로관(앵글부착) 300×300×120 표기만 다름
|
||||
90 중하중그레이팅 395×995×52 1 이음 MT007025 스틸그레이팅(중하중용) 395×995×52 표기만 다름
|
||||
91 측구수로관 500×500×2000×150,앵글포함 1 이음 MT015887 측구수로관(앵글부착) 500×500×150 표기만 다름
|
||||
92 중하중그레이팅 595×995×67 1 이음 MT007032 스틸그레이팅(중하중용) 595×995×67 표기만 다름
|
||||
93 잔디 0.18×0.18×0.01 1 후보 사람이 고름 — 틀린 줄에 자동으로 잇지 않음
|
||||
94 강관비계 4000×48.6mm×2.3mm 1 이음 MT000481 강관비계,서울 Φ48.6*2.3*4.0m 표기만 다름
|
||||
95 삼각표지판 삼각,900mm 1 이음 MT003202 교통안전표지판 900,삼각(고휘도) 눈으로 추정
|
||||
96 표지판지주 D=76.3mm×3.0m 1 이음 MT003241 표지판지주 Φ76.3×3000 표기만 다름
|
||||
97 공사안내표지판 300×1500 1 후보 사람이 고름 — 틀린 줄에 자동으로 잇지 않음
|
||||
98 공사안전PE드럼 500×800 1 이음 MT003311 PE드럼(일체형) Φ500×800 표기만 다름
|
||||
99 윙카호스 10cm 1 후보 사람이 고름 — 틀린 줄에 자동으로 잇지 않음
|
||||
100 조합페인트 KSM6020 2급,백색 1 이음 MT003372 조합페인트 KSM-6020 2급(백색) 표기만 다름
|
||||
|
Binary file not shown.
Binary file not shown.
@@ -160,3 +160,37 @@
|
||||
|
||||
- M2 고침(안): 가름의 잣대 = 금액(노 · 재 · 경 · 계)보다 **구성 줄(손료계수 · 주연료량 · 조종원 — 품셈 값)** — 원파일은 구성 표 · 엑셀은 「기계경비」 시트(산출근거 줄)에 있음 · 가격 · 유가는 값이라 잣대에서 뺌.
|
||||
- M1 = PLAN 816-1 로 진행(요구 770-1).
|
||||
|
||||
## O. 구글 시트 「수동연결(자재)」 · 「수동연결(기계)」 검토(요구 772 · 시트 = AISLO_이슈관리대장 · 읽기 = 드라이브 커넥터로 xlsx 받아 `tmp/q772_sheet.xlsx`)
|
||||
|
||||
기계 30 줄(사용자 메모 대조):
|
||||
|
||||
| 갈래 | 줄 | 내용 |
|
||||
|---|---|---|
|
||||
| 이미 이어짐(목록이 옛 것 — 815-2 별칭 다섯 합치기 전에 뜬 목록) | 4 | 바이브레이트 → EQ000248 · 버킷준설기 → EQ000425 · 아스팔트페이버(피니셔) 3.0m → EQ000193 · 1.7m → EQ000192 |
|
||||
| 816 에서 처리 중 | 8 | 덤프트럭 1.0Ton · 굴착기 1.0 · 0.6㎥ · 굴삭기(1.0m3) 둘 · 크레인(트럭) 10 · 15Ton · 엔진톱 18" |
|
||||
| 메모 글 검색으로 바로 나옴(별칭만 넣으면 됨) | 3 | 덤프자동덮개 15톤 → EQ000078 덤프트럭 자동덮개시설 15톤용 · 24톤 → EQ000080 · 트럭트랙터및트레일러 20톤 → EQ000183 트럭 트랙터 및 평판트레일러 20ton |
|
||||
| 확인 필요 | 2 | 커터(콘크리트 및 아스팔트용) 320-440mm ↔ EQ000234 320~400(규격 범위 다름) · 크로울러드릴 탑승유압식 ↔ EQ000337 110㎾ / EQ000338 150㎾(가져온 쪽 규격 없음) |
|
||||
| 우리 표 · 현행 품셈에 없음 | 13 | 발전기 5.5 · 7 · 10kW · 브레이카 0.18㎥ · 모노펌프 · 에어 호스 · 파워트로웰 · 콘크리트펌프용 파이프 여섯(EST 는 기계 손료로 둠) |
|
||||
|
||||
- 크레인(트럭) 10 · 15Ton: 사용자 메모 = 트럭탑재형 크레인 · EST 구성 줄 = 크레인(타이어)와 같은 값(2,298 · 136,400 천원 · 3.8ℓ — 트럭탑재형 10ton 은 2,598 · 91,593 천원 · 10.3ℓ) → 사용자 판단 대기.
|
||||
|
||||
자재 100 줄(브레인이 한 줄씩 읽음 · 사용자 메모 칸은 비어 있음):
|
||||
|
||||
- 우리 줄 하나로 또렷(표기만 다름 — 별칭만 넣으면 됨) 65 줄: 2 · 18 · 26 → MT000467 / 5 · 25 → MT000470 / 19 → MT000472 / 15 · 17 → MT000209 / 16 · 50 · 52 · 20 → MT000018 / 21 · 65 → MT000499 / 3 · 7 · 51 · 53 → MT000393 / 10 → MT009202 / 27 · 63 → MT007111 / 28 → MT005677 / 31 → MT005687 / 35 · 68 → MT006544 / 37 → MT002926 / 62 · 85 → MT002861 / 87 → MT002859 / 40 · 95 → MT003202 / 96 → MT003241 / 42 · 54 → MT005749 / 44 · 94 → MT000481 / 47 → MT000487 / 55 · 74 → MT003163 / 73 → MT003165 / 56 → MT003166 / 66 · 84 → MT000453 / 67 → MT007130 / 71 → MT023493 / 72 → MT023491 / 77 → MT023654 / 78 → MT023655 / 75 → MT002536 / 82 → MT006490 / 86 → MT006540 / 89 → MT015883 / 91 → MT015887 / 90 → MT007025 / 92 → MT007032 / 98 → MT003311 / 100 → MT003372 / 57 → MT003398 / 4 → MT006763 / 49 → MT006665 / 48 → MT006667 / 33 · 79 · 81 → MT006664 / 83 → MT006788 (번호 = 시트 행 차례 · 머리 뺀 1 ~ 100)
|
||||
- 사람이 골라야 함(우리 줄 여럿 · 규격이 조금 다름) 31 줄: 1 접착제 · 6 결속선(보통철선 대 어닐링철선) · 13 · 14 · 23 · 24 · 58 · 59 종자 · 32 미송(판재) · 45 철물 · 70 어닐링철선 #6 · 36 PVC용접봉 · 69 용접봉(연강용) · 29 BUTTON BIT · 41 표지판지주 3.5M · 64 강관비계 6m · 93 잔디 · 22 에폭시수지 · 46 각재(말뚝) · 76 보조강재 · 97 공사안내표지판 · 99 윙카호스 · 11 · 12 낙석방지책 · 38 혼합골재 · 61 코어비트 · 30 샹크 · 34 유기질퇴비 · 60 우드칩 · 80 부산물비료 · 88 휠라
|
||||
- 잇지 않음 · 없음 4 줄: 8 파형강관 · 39 이형철근(「별도계상」 = 자리만) · 9 금강사 · 43 떼
|
||||
- 못 이은 까닭 = 정확 일치만 잇는 규칙(이름 낱말 차례 「유로폼(패널)」 ↔ 「유로폼패널」 · 「내수합판1급」 ↔ 「합판(내수)」 · 규격 표기 12mm ↔ 12t · 차례 뒤집힘 T5×200 ↔ 200×5T · 덧글 KSM · 국산 · 지역 꼬리) · 가까운 줄 제안이 틀린 것도 있음(신나 → 신나무 · 중하중그레이팅 → 다른 치수).
|
||||
- 구글 시트 직접 쓰기 토큰 만료(invalid_grant) — 사용자가 데스크탑에 있어 랩탑 로그인 창을 못 누름 · 읽기는 드라이브 커넥터로 됨.
|
||||
|
||||
## P. 요구 773 ~ 776 결정
|
||||
|
||||
| # | 결정 | 상태 |
|
||||
|---|---|---|
|
||||
| P1 | 기계 가름은 값 비교가 아니라 로직 — 우리 기계표 값과 맞대지 않고 **같은 파일 안 단독 기계와 대조**해 그 이름으로 찾음(요구 773-3) | 816-2 진행 |
|
||||
| P2 | 기계 별칭 = 브레인이 제안 → 승인 길로 넣음(요구 776): 덤프자동덮개 15톤 → EQ000078 · 24톤 → EQ000080 · 트럭트랙터및트레일러 20톤 → EQ000183 · 커터(콘크리트 및 아스팔트용) 320-440mm → EQ000234 · 크로울러드릴 탑승유압식 → EQ000337(110㎾) · 크레인(트럭) 10Ton → EQ000128 · 15Ton → EQ000129 | 넣음 · 찾기 확인 |
|
||||
| P3 | 엔진톱 18" → 체인톱 EQ000620 별칭 = 인치 따옴표 풀이(816-3) 합친 뒤 넣음 | 대기(816 합친 뒤) |
|
||||
| P4 | 재료 찾기 엔진 개선 — 표기만 다름 = 자동 이음 · 종자류 = 후보 좁힘 1 순위 · 별도계상 = 단가산출 뒤 프로젝트에서 연결(요구 773-5) | 817-1 진행 |
|
||||
| P5 | 값이 빈 우리 자재 줄(치즐 0.7 · 0.4 등)에 이어진 줄 = 이음 완료(로직 문제 아님) · 0 원 줄 흠에서 뺌 · 자재표 값 보강은 27 년 초 프로그램 막바지(요구 776) — J 장 닫음 | 완료 처리 |
|
||||
| P6 | 콘크리트펌프용 파이프 = EST 「레미콘타설(펌프차)(배관타설)」 의 압송관 손료(직관 1 · 2 · 3m · 곡관 45 · 60 · 90° · 손료계수 4,500) · 현행 품셈 기계 목록에 없음 | 조사 끝 |
|
||||
| P7 | STmate 재료 DB 있나(요구 778) | 있음 — 설치 폴더 `ST_BSC\자재단가\시설공통자재\` 넷(2025-상 · 2025-하 · 2026-상 · 2026-상(06) 시설공통자재.STBSC · 각 약 180 KB) · 꼴 = 암호 걸린 ZIP(속 `COST.ST` 등) → 내용은 못 읽음 · 파일 자체는 우리 저장소에 없음(목록만 `STmate/old/30_원자료/system_inventory.csv`) · 공사 파일(.STC) 안 `COSTN` 표의 M 코드 자재(값 여섯 칸)는 지금도 읽음 | 조사 끝 |
|
||||
|
||||
@@ -0,0 +1,100 @@
|
||||
# 817-1 불러오기 재료 찾기 — 전후 전수 보고(요구 773-5 · 774 · 방향 장부 O 장)
|
||||
|
||||
- 대상 = 815 와 같은 자 — 원파일 EST 6 · STmate 7(2,220 블록) + 실무 엑셀 11(1,021 블록) · 재는 법 = 815-1 과 같음(블록마다 금액 · 요소 이음 · [우리 단가로])
|
||||
- 전 = 817-1 앞 판 · 후 = 커밋 bb243af
|
||||
- 자세한 표 = `_목록_817-1_재료찾기.xlsx`(과녁 100 줄 · 새로 이어진 줄 · 남은 못 이음 · 전후)
|
||||
- 별칭 값 · M01 마스터 자료 · storage 층 사본 안 건드림 · 인력 · 기계 · 유가 길 그대로
|
||||
|
||||
## 요약
|
||||
|
||||
| 잣대 | 전 | 후 |
|
||||
|---|---|---|
|
||||
| 재료 이음(전체) | 88.0 %(1,792 / 2,036) | **93.5 %(1,904 / 2,036)** |
|
||||
| 못 이은 재료 | 244 줄 · 100 종 | **132 줄 · 49 종** |
|
||||
| 새로 이어진 줄 | — | 112 줄 · 51 종(목록 시트 「새로 이어진 줄」) |
|
||||
| 끊긴 이음 | — | 0 |
|
||||
| 이은 키가 바뀐 줄 | — | 28 줄 — 치즐 0.7㎥ 이 엉뚱한 「치즐(0.2㎥)」 에서 「치즐(0.7㎥)」 로(바로잡음) |
|
||||
| 금액(엑셀 단가 그대로 셈) | 3,241 블록 100 % | 같음 100 %(셈 바뀐 블록 0) |
|
||||
| 검산 틀린 줄 | 929 | 929(그대로) |
|
||||
|
||||
### 원천 셋
|
||||
|
||||
| 원천 | 전후 | 인력 | 기계 | 재료 | 유가 | 형제 | 모두 우리 값 | 우리 값 + 견적 | 이어짐 · 우리 값 빔 | 엑셀 단가 남음 | 0 원 줄 |
|
||||
|---|---|---|---|---|---|---|---|---|---|---|---|
|
||||
| EST 원파일(1,849) | 전 | 100 | 97.2 | 99.4 | 100 | 100 | 1,622 | 6 | 100 | 75 | 46 |
|
||||
| | 후 | 100 | 97.2 | **99.7** | 100 | 100 | 1,622 | 6 | 100 | 75 | 46 |
|
||||
| STmate 원파일(371) | 전 | 100 | 95.2 | 40.2 | — | 100 | 278 | 13 | 1 | 79 | 0 |
|
||||
| | 후 | 100 | 95.2 | **59.8** | — | 100 | **285** | 13 | 11 | **62** | 0 |
|
||||
| 실무 엑셀(1,021) | 전 | 100 | 93.2 | 54.3 | 100 | 100 | 823 | 12 | 9 | 177 | 0 |
|
||||
| | 후 | 100 | 93.2 | **78.2** | 100 | 100 | **854** | 12 | 27 | **128** | 0 |
|
||||
|
||||
- 「이어짐 · 우리 값 빔」 = 우리 자재 줄에 이었는데 그 줄 값 칸이 빈 블록(요구 776 — 흠 아님 · 자재표 보강 몫) · 늘어난 28 블록 = 모두 치즐 0.7㎥(값 칸 빔)
|
||||
- 블록 옮김: 엑셀 단가 남음 → 모두 우리 값 43(STmate 7 · 엑셀 36) · 엑셀 단가 남음 → 이어짐 · 우리 값 빔 23 · 모두 우리 값 → 이어짐 · 우리 값 빔 5(치즐 0.2 → 0.7 바로잡음)
|
||||
|
||||
## 엔진 — 새 단 셋(`M02_Hopyo_MatSame` · 재료 길 = 별칭 › 정확 일치 › 바탕 이름 유일 짝 › ㉮ › ㉯)
|
||||
|
||||
- ㉮ 이음(자동) = 이름이 같은 품목 + 규격 수 묶음 같음 + 규격 낱말 안 부딪힘 + 그런 우리 품목 하나(지역 · 관급/사급 줄은 한 품목)
|
||||
- 이름 같음: 빈칸 · 괄호 · 지역 · 관급/사급 꼬리 · 등급(1급) 뗌 · 점 · 빈칸 영문(P.V.C) · 소리 표기 넷(칠렌 → 틸렌 · 매쉬 → 메쉬 · 신나 → 신너 · pipe → 파이프) · 괄호 차례(유로폼(패널) = 유로폼패널) · 괄호 이름(서비스볼트(고장력볼트) → 고장력볼트 — 가져온 바탕 이름이 우리 표에 없을 때만) · 규격에 든 이름(안고초 → 산림종자 안고초)
|
||||
- 약한 같음(수 묶음 같음 · 단위 배일 때만): 우리 이름 끝 = 가져온 이름(지수판 → PVC지수판 · 고무테이프 → 절연 면고무테이프 · 철물 직교자재 → 조임철물) · 우리 이름 = 가져온 이름 앞(구역화물자동차 → 구역화물 · 뒤 덧말이 우리 낱말이 아닐 때만)
|
||||
- 수 묶음: 차례 무관 · KS 번호 · 제곱 꼬리 뗌 · 천 단위 쉼표 · 같음 › 낱마다 단위 배(3.6×3.0×3.0 = 30×30×3600) › 한쪽이 다른 쪽에 듦(측구수로관 300×300×2000×120 → 300×300×120 · 이름이 굳세게 같을 때만)
|
||||
- 낱말 부딪힘 = 두 쪽 다 괄호 · 규격 한글 낱말이 있고 두 글자 묶음이 안 겹침(연강용 ↔ 주철용 · 표준 ↔ 단부구간 · 스티로폼 ↔ ABS코팅폼)
|
||||
- ㉯ 후보 좁힘 = 이름 같은 묶음에서 규격 어근(초본류 → 초본) 품은 줄 › 수 묶음 닮음 › 바탕 같음 › 덧말 적은 줄 차례 + 옛 후보 가운데 규격 수 묶음(둘 이상) 같은 줄 먼저 · 자동 이음 아님
|
||||
- 화면: 못 이은 재료 까닭 글에 1 순위(「자재 후보 여럿 — 고름 · 1 순위 종 자 초본위주형」) — [이음 고르기] 창은 이름 찾기 그대로
|
||||
- 사전 = 소리 · 영문 넷 · 덧글 낱말 다섯(이내 · 이하 · 이상 · 국산 · 포함) 한 곳 · 자재마다 박은 예외 없음
|
||||
|
||||
## 과녁 100 줄(브레인 과녁을 우리 자재표로 다시 확인)
|
||||
|
||||
| 과녁 | 줄 | 같음 | 다름 |
|
||||
|---|---|---|---|
|
||||
| 이음 | 65 | 49 | 16 — 1 순위는 맞음 10 · 그 밖 6 |
|
||||
| 후보 1 순위(종자) | 5 | 5 | 0 |
|
||||
| 후보(사람이 고름) | 26 | 25 | 1 |
|
||||
| 잇지 않음 | 4 | 4 | 0 |
|
||||
|
||||
과녁과 다른 줄(맞추지 않음 · 까닭):
|
||||
|
||||
| 행 | 가져온 줄 | 과녁 | 결과 | 까닭 |
|
||||
|---|---|---|---|---|
|
||||
| 3 · 7 | 각재 외송 · 각재 (외송) | 이음 | ㉯ 1 순위 각재(외송) | 규격 수 없음 · 빔 → 수 묶음 다름 |
|
||||
| 4 | 요소 | 이음 | ㉯ 1 순위 요소 N:46% | 가져온 규격 빔 |
|
||||
| 31 | 유압식드릴스리브 T38×190mm | 이음 | 후보 1 순위 유압드릴스리브 | 「유압식」 ↔ 「유압」 이름 다름 |
|
||||
| 40 | 안전표지판 삼각,D=900 | 이음 | ㉯ 1 순위 교통안전표지판 900,삼각 | 같은 수 · 낱말 줄 둘(LED 교통안전표지판(태양광) 900,삼각) |
|
||||
| 47 | 강관써포트 V4 2600-4000mm | 이음 | 후보 1 순위 강관동바리 | 이름 다름 |
|
||||
| 55 | 아세틸렌 용접용 | 이음 | ㉯ 1 순위 아세틸렌 98%(용접용) | 규격 수 없음 ↔ 98% |
|
||||
| 90 · 92 | 중하중그레이팅 395×995×52 · 595×995×67 | 이음 | 후보 1 순위 스틸그레이팅(중하중용) 같은 치수 | 이름 다름 — 같은 수 묶음 후보 먼저(전에는 다른 치수 OK그레이팅이 1 순위) |
|
||||
| 98 | 공사안전PE드럼 500×800 | 이음 | 후보 1 순위 PE드럼(일체형) Φ500×800 | 가져온 이름 앞 덧말은 안 뗌(PVC용접봉 ↔ 용접봉 틀린 이음 막음) |
|
||||
| 67 | 와이어매쉬 #6 100×100 | 이음 MT007130(관급) | 이음 MT007136(서울) | 같은 품목 변형 — 별칭 · 정확 일치 길과 같은 차례(서울 먼저) |
|
||||
| 54 | 다이아몬드톱날 Ø14"×3.2t | 이음 블레이드 | 후보(1 순위 다름) | 이름 다름 — 별칭 몫 |
|
||||
| 79 · 81 | 참싸리(박피) · 참싸리 박피 | 이음 산림종자 참싸리 | 못 찾음 | 박피 ↔ 풍화암,강마사토 덧말 부딪힘 — 별칭 몫 |
|
||||
| 83 | 퇴비(천연부엽토) 20㎏/포 | 이음 천연부엽토 | 후보(1 순위 퇴비 1등급) | 「퇴비」가 우리 표에 따로 있어 괄호 이름 길 안 씀(보조강재(열연강판) → 열연강판 같은 이음 막음) |
|
||||
| 95 | 삼각표지판 삼각,900mm | 이음 | 후보(1 순위 다름) | 이름 다름 — 별칭 몫 |
|
||||
| 93 | 잔디 0.18×0.18×0.01 | 후보 | **이음 천연잔디(한국형) 0.18*0.18*0.01** | 우리 표에 수 묶음 같은 줄 하나 — 과녁은 잔디 0.03 줄만 봄 · 브레인 확인 필요 |
|
||||
|
||||
- 「가까운 줄 제안」 바로잡음: 신나 → 신너(조합페인트용) KSM-6060(2종) ㉮(전 = 신나무 1 순위) · 중하중그레이팅 → 같은 치수 1 순위
|
||||
- 틀린 이음 막는 견본(시험): 용접봉(연강용) ↔ 주철용 · PVC용접봉 ↔ 용접봉 · 어닐링철선 #6 ↔ #8 · 잔디 0.01 ↔ 0.03 · 표지판지주 3.5M ↔ 3000 · 강관비계 6m ↔ 4.0m · 신나 ↔ 신나무 — 모두 안 이음
|
||||
|
||||
## 새로 이어진 줄(51 종 · 112 줄) — 틀린 이음 0
|
||||
|
||||
- 전부 목록 시트(가져온 이름 · 규격 → 우리 키 · 이름 · 규격 · 맞은 까닭) · 한 줄씩 읽음 — 과녁과 다른 이음은 93 잔디(위 표) · 67 와이어매쉬 변형뿐
|
||||
- 과녁 밖에서 새로 이어진 것 = 0(새 이음 51 종 모두 과녁 100 줄 안)
|
||||
|
||||
## 별도계상
|
||||
|
||||
- 단가산출 불러오기 길(엑셀 산근 · 원파일): 전에는 표지 없이 「자재 후보 여럿」 못 이음 → 이제 요소 잇기 없음 + 표지 「참고 · 별도계상 · 품목은 프로젝트에서」(일위대가 호표 길 `M02_CheckApart` 와 같은 글) · 못 이음 띠 「파형강관(별도계상) (별도계상 · 품목은 프로젝트에서) · 이음 고르기」
|
||||
- 원가 합 밖 = 그대로(줄 값 0 · 원본 금액 그대로) · 0 원 줄 수에 안 듦(엑셀 값 0 줄) · 대상 = 동부청영월 파형강관 · 이형철근 8 줄(엑셀 · STmate)
|
||||
- 일위대가 호표 길 · 프로젝트로 복사(`M02_Doc_Copy` 합밖) 은 전부터 되어 있음
|
||||
|
||||
## 화면(8001 · 5174 · 일반 계정 개인 층 · 「sub1임시_동부청영월.xlsx」 · 「sub1임시_울진소광.xlsx」)
|
||||
|
||||
- 단산58 유로폼 설치 · 해체 — 「유로폼(패널) 600*1200」 줄 → MT000467 유로폼패널(저장된 SK 이음 키로 확인) · 이음 없는 자원 0
|
||||
- 단산71 파형강관부설 — 「파형강관(별도계상) (별도계상 · 품목은 프로젝트에서)」 · 금액 15,898 그대로
|
||||
- 단산1492 씨뿌리기(울진소광) — 「종자(초본류) … 1 순위 종 자 초본위주형」 · 「종자(목본류) … 1 순위 종 자 목본군락형」(ORCA 탭 사진 `tmp/q17/ui/orca_1492.png`)
|
||||
- 밝음 · 어두움 · 1366×768 가로 넘침 0 · 콘솔 오류 0(로그인 전 세션 401 하나 뺌) · 만든 SK 셋 · 올린 HP 둘 지움
|
||||
|
||||
## 남은 것
|
||||
|
||||
| # | 내용 | 갈래 |
|
||||
|---|---|---|
|
||||
| 1 | 일위대가 호표 화면(일위대가표 시트) 줄 판정은 다른 길(`M02_Hopyo_Units`) — 안고초 · 참싸리 등 아직 분홍(못 이음) | 이번 고침 밖 |
|
||||
| 2 | 남은 못 이은 재료 132 줄 · 49 종 = 이름 다름 · 규격 다름 · 우리 표에 없음 → 별칭 몫(목록 시트) | 사용자 별칭 |
|
||||
| 3 | 93 잔디 0.01 → 천연잔디(한국형) 0.01 이음 맞는지 | 브레인 확인 |
|
||||
@@ -902,3 +902,50 @@ b3 품셈 원문에 있는데 누락된 것만 추가하면 된다는거지?
|
||||
|
||||
1. 다음으로 너가 공유해준 엑셀파일을 구글 스프레드 시트로 가져와 수동연결(자재)탭과 수동연결(기계)탭으로 넣었고 기계와 관련해서는 해당 행의 메모에 의견을 작성해 뒀음. 이때 벌써 별칭으로 연결된 정보도 있었고 간단한 텍스트 검색으로도 분류가 되는 경우가 있어. 확인해봐.
|
||||
2. 재료에 대한 메모 의견은 다시 한번 너도 직접 일어봐봐. 읽어보고 대화하자. 대부분 연결되는 것글이 많은 것같은데?
|
||||
|
||||
## 요구 773 (11-11 · 사용자 · 1 크롤러드릴 기본 110kW · 2 커터 320~440 인정 → 우리 320~400 · 3 크레인(트럭) = 크레인(타이어) · 값 비교로 잇지 말고 로직으로 판단 · 4 콘크리트펌프용 파이프 = 손료 — 어떤 장비 손료인지 확인 · 5 자재: 표기 다름은 분석 엔진 개선 · 종자는 로직 개선으로 찾을 수 있을 듯(종자 & 초본류 조회) · 별도계상은 단가산출 뒤 연결)
|
||||
|
||||
1. 크롤러는 기본 110kw로 지정
|
||||
2. 커터는 320~440 인정, 우리 자료의 320~400으로 지정
|
||||
3. 그럼 크레인 타이어가 맞지. (대신 값을 비교해서 연결하면 문제가 됨. 값은 업데이트 되기때문 과거 자료일수록 더 값이 안맞음. 그러니 로직으로 판단하게 되어야함. 굴삭기가 그렇게 되어 있을것으로 판단됨. )
|
||||
4. 콘크리트펌프용 파이프는 손료가 맞는것같음. 어떤 장비의 손료인지. 확인
|
||||
5. 자재에서
|
||||
- 표기 다름은 잘 찾도록 분석엔진 개선
|
||||
- 사람이 골라야하는 정보들 중 종자 같은건 로직 개선치 찾을 수 있을것 같음. 종자 & 초본류로 조회해봐.
|
||||
- 별도계상은 단가산출에서는 원가를 합산하지는 않지만 나중에 프로젝트 내부에서는 연결이 되어야함. 그러니 별도계상은 항목도 연결이 불가능에 가까움. 사용자가 별도계상하더라도 옵션은 단가산출을 만든 후 연결하게 해야함. 벌써 반영되어 있을것으로 예상
|
||||
|
||||
## 요구 774 (11-11 · 사용자 · 별칭은 사용자가 지정 · 브레인은 분석 엔진 업데이트에 집중)
|
||||
|
||||
그리고 별칭은 내가 지정할거야. 너는 분석 엔진 업데이트에 집중해줘.
|
||||
|
||||
## 요구 775 (11-11 · 사용자 · 이렇게 반영되면 엑셀 · 경쟁사 두 확장자 파일을 각각 넣었을 때 우리 단가산출 형태로 만드는 진행율이 어느 정도인지)
|
||||
|
||||
이렇게 반영되면 엑셀, 경쟁사 2개 확장자 파일을 각각 넣었을때 우리 프로그램의 단가산출 형태로 만드는 진행율이 어느정도 됨?
|
||||
|
||||
## 요구 776 (11-11 · 사용자 · 기계 별칭 입력은 브레인이 · 승인까지 · 자재표 값 빈 줄은 로직 문제 아님 = 완료 처리 · 자재표 보강은 27 년 초 프로그램 막바지)
|
||||
|
||||
기계 별칭 입력은 너가 해줘. 승인까지 진행
|
||||
자재표의 값이 없어 빈값을 출력하는 정보는 연결은 되었지만 못찾아서가 아니라 값이 없어서인지라 로직에는 문제가 없음. 그러니 완료 처리 자재표 보강은 27년 초에 진행예정. 프로그램 막바지에 진행예정. 너는 완료처리
|
||||
|
||||
## 요구 777 (11-11 · 사용자 · STmate 굴삭기(1.0m3) = 무한궤도)
|
||||
|
||||
STmate 의 「굴삭기(1.0m3)은 무한궤도
|
||||
|
||||
## 요구 778 (11-11 · 사용자 · STmate 에도 재료 DB 가 있을 것 같음 — 있는지만 확인)
|
||||
|
||||
추가로 하나만 확인해봐.stmate에도 재료 db가 있을것같은데 있는지만 확인해줘
|
||||
|
||||
## 요구 779 (11-11 · 사용자 · 암호 압축을 풀 수도 있지 않나)
|
||||
|
||||
암호 압축을 풀수도 있지않아?
|
||||
|
||||
## 요구 780 (11-11 · 사용자 · 자재표 값 보강 · 별칭 · 미룬 자료를 다 반영했다고 가정하면 몇 %인가)
|
||||
|
||||
자재표 값 보강을 했다고 별칭과 미룬 자료를 반영했다고 가정하면 몇 프로인거야?
|
||||
|
||||
## 요구 781 (11-11 · 사용자 · 1 기계에서 없는 것들은 경쟁사가 어떻게 계산했는지 확인 · 2 별도계상 완료 처리 · 3 구글 시트 기계 · 재료 탭 내용 지우고 별칭 안 들어간 기계 · 해결 안 된 자재 넣기 · 4 자재표 값 보강 대상 확인해 시트에 넣기)
|
||||
|
||||
1. 일단 기계에서 없는 것들은 경쟁사들은 어떻게 계산되었는지 확인
|
||||
2. 별도 계상은 완료처리하는게 맞는걸로 보임.
|
||||
3. 구글 스프레드시트의 내용 중 기계와 재료의 내용을 삭제하고 기계 별칭안들어간 항목과 해결되지 않은 자재들을 넣어줘.
|
||||
4. 자재표 값 보강이 어떤게 있는지 확인해서 스프레드시트에 넣어줘.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"다음": {"LB": 409, "EQ": 655, "RT": 20, "MN": 7000, "MT": 30719, "MO": 74, "MP": 364, "FX": 6, "CF": 159, "QF": 477, "GF": 337, "CC": 528, "GC": 1195, "QC": 2060, "GX": 5, "UA": 38},
|
||||
"다음": {"LB": 409, "EQ": 656, "RT": 20, "MN": 7000, "MT": 30719, "MO": 74, "MP": 364, "FX": 6, "CF": 159, "QF": 477, "GF": 338, "CC": 528, "GC": 1195, "QC": 2060, "GX": 5, "UA": 38},
|
||||
"폐기": {"MT000001": "자재품목 휘발유 — 값이 비고 글뿐 · 유가 테이블로 감", "MT000002": "자재품목 경유 — 값이 비고 글뿐 · 유가 테이블로 감", "MT000003": "자재품목 선박용경유 — 값이 비고 글뿐 · 유가 테이블로 감", "MT000004": "자재품목 중유 — 값이 비고 글뿐 · 유가 테이블로 감", "MT000005": "자재품목 등유 — 값이 비고 글뿐 · 유가 테이블로 감", "LB000399": "이름 「〃」 — 직종이 아니라 되풀이 기호 · 소요량 13-6-9 에서 윗줄 직종으로 바로잡음", "MN": "재료_나라장터자재 — old 로 옮김 · 로직은 자재품목·품셈재료로 갈아 끼움", "LB000372": "미확보 고급기능사 — 측량노임 항공사진고급기능사 옛이름으로 옮김", "LB000393": "미확보 리베팅공 — 건설노임 철골공 옛이름으로 옮김(2010 통합표 연번10)", "LB000405": "미확보 Belt Conveyor 설치공 — 품셈 13-10-1 주② 배분으로 다섯 직종에 나눔", "MT000006": "자재품목 전력 — 재료_유가전력 전력 줄로 감", "MT000007": "자재품목 전력 — 재료_유가전력 전력 줄로 감", "LB000368": "미확보 기술사 — 기술사(건설) 옛이름으로 옮김", "LB000369": "미확보 특급기술자 — 특급기술자(건설) 옛이름으로 옮김", "LB000370": "미확보 고급기술자 — 고급기술자(건설) 옛이름으로 옮김", "LB000361": "미확보 중급기술자 — 중급기술자(건설) 옛이름으로 옮김", "LB000364": "미확보 초급기술자 — 초급기술자(건설) 옛이름으로 옮김", "LB000365": "미확보 고급숙련기술자 — 고급숙련기술자(건설) 옛이름으로 옮김", "LB000366": "미확보 중급숙련기술자 — 중급숙련기술자(건설) 옛이름으로 옮김", "LB000367": "미확보 초급숙련기술자 — 초급숙련기술자(건설) 옛이름으로 옮김", "LB000389": "미확보 기계기사 — 초급기술자(기계·설비) 옛이름으로 옮김", "LB000388": "미확보 기계산업기사 — 초급숙련기술자(기계·설비) 옛이름으로 옮김", "MO000055": {"원문번호": "주택용저압:기본_200이하", "파일": "재료_유가전력.json"}, "MO000056": {"원문번호": "주택용저압:기본_201~400", "파일": "재료_유가전력.json"}, "MO000057": {"원문번호": "주택용저압:기본_400초과", "파일": "재료_유가전력.json"}, "MO000059": {"원문번호": "주택용저압:전력량_다음200", "파일": "재료_유가전력.json"}, "MO000060": {"원문번호": "주택용저압:전력량_400초과", "파일": "재료_유가전력.json"}, "MO000061": {"원문번호": "주택용저압:월간최저요금", "파일": "재료_유가전력.json"}, "MO000062": {"원문번호": "일반용갑I저압:기본", "파일": "재료_유가전력.json"}, "MO000063": {"원문번호": "일반용갑I저압:전력량_여름철", "파일": "재료_유가전력.json"}, "MO000064": {"원문번호": "일반용갑I저압:전력량_봄가을철", "파일": "재료_유가전력.json"}, "MO000065": {"원문번호": "일반용갑I저압:전력량_겨울철", "파일": "재료_유가전력.json"}, "MO000066": {"원문번호": "산업용갑I저압:기본", "파일": "재료_유가전력.json"}, "MO000067": {"원문번호": "산업용갑I저압:전력량_여름철", "파일": "재료_유가전력.json"}, "MO000068": {"원문번호": "산업용갑I저압:전력량_봄가을철", "파일": "재료_유가전력.json"}, "MO000069": {"원문번호": "산업용갑I저압:전력량_겨울철", "파일": "재료_유가전력.json"}, "MO000070": {"원문번호": "임시전력갑:기본요금적용", "파일": "재료_유가전력.json"}, "MO000071": {"원문번호": "임시전력갑:전력량요금적용", "파일": "재료_유가전력.json"}, "MO000072": {"원문번호": "임시전력갑:월간최저요금", "파일": "재료_유가전력.json"}, "GC001145": "작업량당 기계경비 — 부르는 곳 0 · 필요하면 단가산출에 기계 하나 넣어 만듦(요구 734-4 · 735-4 · 813-2)", "GC001148": "작업량당 기계경비 — 부르는 곳 0 · 필요하면 단가산출에 기계 하나 넣어 만듦(요구 734-4 · 735-4 · 813-2)", "GC001151": "작업량당 기계경비 — 부르는 곳 0 · 필요하면 단가산출에 기계 하나 넣어 만듦(요구 734-4 · 735-4 · 813-2)", "GC001156": "작업량당 기계경비 — 부르는 곳 0 · 필요하면 단가산출에 기계 하나 넣어 만듦(요구 734-4 · 735-4 · 813-2)", "GC001159": "작업량당 기계경비 — 부르는 곳 0 · 필요하면 단가산출에 기계 하나 넣어 만듦(요구 734-4 · 735-4 · 813-2)", "GC001166": "작업량당 기계경비 — 부르는 곳 0 · 필요하면 단가산출에 기계 하나 넣어 만듦(요구 734-4 · 735-4 · 813-2)"},
|
||||
"키": {
|
||||
"CC000001": {"원문번호": "공통 1-3-1 콘크리트 및 포장용 재료", "파일": "계수_건설품셈_01장_적용기준.json"},
|
||||
@@ -1341,6 +1341,7 @@
|
||||
"EQ000652": {"원문번호": "2-2-11 부착용집게(0.2~0.8㎥)", "파일": "기계.json"},
|
||||
"EQ000653": {"원문번호": "2-2-11 트럭탑재형크레인", "파일": "기계.json"},
|
||||
"EQ000654": {"원문번호": "2-2-11 타이어형크레인", "파일": "기계.json"},
|
||||
"EQ000655": {"원문번호": "10-9 덤프트럭(4륜구동) 1ton", "파일": "기계.json"},
|
||||
"FX000001": {"원문번호": "0000001", "파일": "환율_한국은행환율.json"},
|
||||
"FX000002": {"원문번호": "0000002", "파일": "환율_한국은행환율.json"},
|
||||
"FX000003": {"원문번호": "0000003", "파일": "환율_한국은행환율.json"},
|
||||
@@ -2870,6 +2871,7 @@
|
||||
"GF000334": {"원문번호": "8-3 원목 박피", "파일": "로직_산림품셈_08장_방제.json"},
|
||||
"GF000335": {"원문번호": "10-1", "파일": "로직_산림품셈_10장_자재․장비_운반.json"},
|
||||
"GF000336": {"원문번호": "10-2", "파일": "로직_산림품셈_10장_자재․장비_운반.json"},
|
||||
"GF000337": {"원문번호": "2-2-1 체인톱 시간당 기계경비", "파일": "로직_산림품셈_02장_소요재료_및_기계손료.json"},
|
||||
"GX000001": {"원문번호": "13-4-1 돌쌓기 메쌓기(인력)", "파일": "로직_자체.json"},
|
||||
"GX000002": {"원문번호": "12-34-4 암거이음받침 채움재(T=20㎜)", "파일": "로직_자체.json"},
|
||||
"GX000003": {"원문번호": "13-4-1 돌쌓기 메쌓기(인력) (2)", "파일": "로직_자체.json"},
|
||||
|
||||
@@ -2197,7 +2197,8 @@
|
||||
"조종원": "건설품셈 공통 8-4-1",
|
||||
"조종원직종": "건설품셈 공통 8-1-3 5 · 건설노임 1048"
|
||||
},
|
||||
"비고": "조종원직종 원문 이름: 건설기계운전사 · 주연료종류: 원문 글을 가름"
|
||||
"비고": "조종원직종 원문 이름: 건설기계운전사 · 주연료종류: 원문 글을 가름",
|
||||
"별칭": "굴삭기(1.0m3), 굴삭기(1.0m3) 할증율:1.20"
|
||||
},
|
||||
{
|
||||
"키": "EQ000023",
|
||||
@@ -5613,7 +5614,8 @@
|
||||
"조종원": "건설품셈 공통 8-4",
|
||||
"조종원직종": "건설품셈 공통 8-1-3 5"
|
||||
},
|
||||
"비고": "규격 단위: (ton)"
|
||||
"비고": "규격 단위: (ton)",
|
||||
"별칭": "덤프자동덮개 15톤"
|
||||
},
|
||||
{
|
||||
"키": "EQ000079",
|
||||
@@ -5735,7 +5737,8 @@
|
||||
"조종원": "건설품셈 공통 8-4",
|
||||
"조종원직종": "건설품셈 공통 8-1-3 5"
|
||||
},
|
||||
"비고": "규격 단위: (ton)"
|
||||
"비고": "규격 단위: (ton)",
|
||||
"별칭": "덤프자동덮개 24톤"
|
||||
},
|
||||
{
|
||||
"키": "EQ000081",
|
||||
@@ -8672,7 +8675,8 @@
|
||||
"조종원": "건설품셈 공통 8-4-3",
|
||||
"조종원직종": "건설품셈 공통 8-1-3 5 · 건설노임 1048"
|
||||
},
|
||||
"비고": "조종원직종 원문 이름: 건설기계운전사 · 해석: 크레인(타이어) = 원문 「기중기(차륜)」 · 주연료종류: 원문 글을 가름"
|
||||
"비고": "조종원직종 원문 이름: 건설기계운전사 · 해석: 크레인(타이어) = 원문 「기중기(차륜)」 · 주연료종류: 원문 글을 가름",
|
||||
"별칭": "크레인(트럭) 10Ton"
|
||||
},
|
||||
{
|
||||
"키": "EQ000129",
|
||||
@@ -8733,7 +8737,8 @@
|
||||
"조종원": "건설품셈 공통 8-4-3",
|
||||
"조종원직종": "건설품셈 공통 8-1-3 5 · 건설노임 1048"
|
||||
},
|
||||
"비고": "조종원직종 원문 이름: 건설기계운전사 · 해석: 크레인(타이어) = 원문 「기중기(차륜)」 · 주연료종류: 원문 글을 가름"
|
||||
"비고": "조종원직종 원문 이름: 건설기계운전사 · 해석: 크레인(타이어) = 원문 「기중기(차륜)」 · 주연료종류: 원문 글을 가름",
|
||||
"별칭": "크레인(트럭) 15Ton"
|
||||
},
|
||||
{
|
||||
"키": "EQ000130",
|
||||
@@ -12018,7 +12023,8 @@
|
||||
"조종원": "건설품셈 공통 8-4-3",
|
||||
"조종원직종": "건설품셈 공통 8-1-3 5 · 건설노임 1049"
|
||||
},
|
||||
"비고": "조종원직종 원문 이름: 화물차운전사 · 주연료종류: 원문 글을 가름"
|
||||
"비고": "조종원직종 원문 이름: 화물차운전사 · 주연료종류: 원문 글을 가름",
|
||||
"별칭": "트럭트랙터및트레일러 20톤"
|
||||
},
|
||||
{
|
||||
"키": "EQ000184",
|
||||
@@ -15176,9 +15182,8 @@
|
||||
"조종원직종": "건설품셈 공통 8-1-3 5"
|
||||
},
|
||||
"비고": "규격 단위: (㎜) · 주연료종류: 원문 글을 가름",
|
||||
"값원문": {
|
||||
"8-4-5 주연료": "휘발유5.6"
|
||||
}
|
||||
"값원문": { "8-4-5 주연료": "휘발유5.6" },
|
||||
"별칭": "커터(콘크리트 및 아스팔트용) 320-440mm"
|
||||
},
|
||||
{
|
||||
"키": "EQ000235",
|
||||
@@ -21400,7 +21405,8 @@
|
||||
"조종원": "건설품셈 공통 8-4-6",
|
||||
"조종원직종": "건설품셈 공통 8-1-3 5 · 건설노임 1048"
|
||||
},
|
||||
"비고": "조종원직종 원문 이름: 건설기계운전사 · 주연료종류: 원문 글을 가름"
|
||||
"비고": "조종원직종 원문 이름: 건설기계운전사 · 주연료종류: 원문 글을 가름",
|
||||
"별칭": "크로울러드릴 탑승유압식"
|
||||
},
|
||||
{
|
||||
"키": "EQ000338",
|
||||
@@ -39000,16 +39006,11 @@
|
||||
"상세구분": "체인톱",
|
||||
"이름": "체인톱",
|
||||
"규격": "45cc(배기량기준)",
|
||||
"단위": {
|
||||
"손료계수": "1일 1대당"
|
||||
},
|
||||
"값": {
|
||||
"손료계수": 0.0084
|
||||
},
|
||||
"출처": {
|
||||
"손료계수": "산림품셈 2-2-1"
|
||||
},
|
||||
"비고": "체인톱 수량(대) = 산출된 벌목부(체인톱 사용) 인원(인)×100% · 기계손료 = 체인톱수량×손료계수×체인톱가격"
|
||||
"단위": { "손료계수": "1일 1대당" },
|
||||
"값": { "손료계수": 0.0084 },
|
||||
"출처": { "손료계수": "산림품셈 2-2-1" },
|
||||
"비고": "체인톱 수량(대) = 산출된 벌목부(체인톱 사용) 인원(인)×100% · 기계손료 = 체인톱수량×손료계수×체인톱가격",
|
||||
"별칭": "엔진톱 18\""
|
||||
},
|
||||
{
|
||||
"키": "EQ000621",
|
||||
@@ -39697,6 +39698,25 @@
|
||||
"값": {},
|
||||
"출처": {},
|
||||
"비고": "「건설공사 표준품셈 공통부문, 8-3-3 [20]운반 및 하역기계, (2104)크레인(타이어)(2025)」 적용"
|
||||
},
|
||||
{
|
||||
"키": "EQ000655",
|
||||
"원문번호": "10-9",
|
||||
"원문": "산림품셈",
|
||||
"구분": "임업기계",
|
||||
"상세구분": "덤프트럭",
|
||||
"이름": "덤프트럭(4륜구동)",
|
||||
"규격": "1ton",
|
||||
"단위": {
|
||||
"가격": "천원"
|
||||
},
|
||||
"값": {
|
||||
"가격": 28000
|
||||
},
|
||||
"출처": {
|
||||
"가격": "산림품셈 10-9"
|
||||
},
|
||||
"비고": "원문 이름: 4륜구동 1톤 덤프트럭 · 중기가격 = 조달청 등재가격(기본값 28,000 천원 = EST 2026-07 「덤프트럭 1.0Ton」 · 바꿀 수 있음) · 작업량은 건설품셈 · 손료 및 운전경비 = 2.5톤 덤프트럭(0602-0025) · 주 연료 = 2.5톤의 80% · 시간당 경비 = GF000145"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -0,0 +1,114 @@
|
||||
{
|
||||
"그룹": "로직",
|
||||
"원문": "산림품셈",
|
||||
"차례": 0,
|
||||
"판": "2026-01-01",
|
||||
"줄": [
|
||||
{
|
||||
"키": "GF000337",
|
||||
"원문번호": "2-2-1",
|
||||
"구분": "산림품셈",
|
||||
"상세구분": "02장 소요재료 및 기계손료",
|
||||
"이름": "체인톱 시간당 기계경비",
|
||||
"결과단위": "원/시간",
|
||||
"출처": "산림품셈 2-1-1 · 2-2-1",
|
||||
"부품": {
|
||||
"갈래": "기계경비식"
|
||||
},
|
||||
"개정": [
|
||||
2026
|
||||
],
|
||||
"소유": "공용",
|
||||
"비고": "1일 1대당(2-1-1 주연료 · 체인오일 · 잡품 · 2-2-1 손료계수) × 시간환산(1일 8시간) · 체인톱 가격 = 재료 「동력체인톱」 · 체인오일 = 재료 「작동유」(GF000076 체인톱 줄과 같음) · 가져온 시간당 「엔진톱」 줄을 잇는 식",
|
||||
"입력": [
|
||||
{
|
||||
"이름": "지역",
|
||||
"고르기": [
|
||||
"전국평균",
|
||||
"전국",
|
||||
"서울",
|
||||
"경기",
|
||||
"강원",
|
||||
"충북",
|
||||
"충남",
|
||||
"전북",
|
||||
"경북",
|
||||
"경남",
|
||||
"부산",
|
||||
"제주",
|
||||
"대구",
|
||||
"인천",
|
||||
"대전",
|
||||
"울산",
|
||||
"세종",
|
||||
"전남광주"
|
||||
],
|
||||
"출처": "건설품셈 공통 8-1-7 5"
|
||||
}
|
||||
],
|
||||
"중간": [
|
||||
{
|
||||
"이름": "시간환산",
|
||||
"식": "찾기(CC000336, 구분='시간환산').분자 / 찾기(CC000336, 구분='시간환산').분모",
|
||||
"출처": "건설품셈 공통 1-1-3 2(일일 작업시간 8시간)"
|
||||
}
|
||||
],
|
||||
"호표": [
|
||||
{
|
||||
"종류": "재료",
|
||||
"요소": "MO:휘발유:{지역}",
|
||||
"이름": "휘발유(체인톱)",
|
||||
"단위": "ℓ",
|
||||
"수량": "찾기(QF000001, 기계명='체인톱', 재료명='보통휘발유(주연료)').주연료 * 시간환산",
|
||||
"비목": "재료비",
|
||||
"출처": "산림품셈 2-1-1 주③"
|
||||
},
|
||||
{
|
||||
"종류": "재료",
|
||||
"요소": {
|
||||
"구분": "기계·설비",
|
||||
"상세구분": "기계류",
|
||||
"검색어": "작동유"
|
||||
},
|
||||
"이름": "체인오일",
|
||||
"단위": "ℓ",
|
||||
"수량": "찾기(QF000001, 기계명='체인톱', 재료명='체인오일(일반오일)').주연료 * 시간환산",
|
||||
"비목": "재료비",
|
||||
"출처": "산림품셈 2-1-1 주④"
|
||||
},
|
||||
{
|
||||
"종류": "재료",
|
||||
"요소": {
|
||||
"구분": "기계·설비",
|
||||
"상세구분": "기계류",
|
||||
"검색어": "동력체인톱"
|
||||
},
|
||||
"이름": "체인톱 손료",
|
||||
"단위": "대",
|
||||
"수량": "EQ000620.손료계수 * 시간환산",
|
||||
"비목": "경비",
|
||||
"출처": "산림품셈 2-2-1"
|
||||
}
|
||||
],
|
||||
"덧줄": [
|
||||
{
|
||||
"이름": "잡품",
|
||||
"식": "줄.'휘발유(체인톱)' * 찾기(QF000001, 기계명='체인톱', 재료명='보통휘발유(주연료)').잡품 / 100",
|
||||
"비목": "재료비",
|
||||
"출처": "산림품셈 2-1-1 주⑤"
|
||||
}
|
||||
],
|
||||
"끝수": null,
|
||||
"변수표": {
|
||||
"지역": {
|
||||
"기호": "S",
|
||||
"아래": "지역"
|
||||
},
|
||||
"시간환산": {
|
||||
"기호": "Q",
|
||||
"아래": "시간환산"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -522,7 +522,7 @@
|
||||
"부품": { "갈래": "기계경비식" },
|
||||
"개정": [2026],
|
||||
"소유": "공용",
|
||||
"비고": "손료·운전경비는 2.5톤(0602-0025) · 주연료 2.5톤의 80% · 중기가격 조달청 등재가격은 요소 없음 · 작업량(건설품셈 덤프트럭 식)·10-9-1 운반량 쓰는 식 요소 없음 · 주② 제잡비 비율 원문에 없음",
|
||||
"비고": "손료·운전경비는 2.5톤(0602-0025) · 주연료 2.5톤의 80% · 중기가격 조달청 등재가격 = 기계표 EQ000655 덤프트럭(4륜구동) 1ton 가격 · 작업량(건설품셈 덤프트럭 식)·10-9-1 운반량 쓰는 식 요소 없음 · 주② 제잡비 비율 원문에 없음",
|
||||
"입력": [
|
||||
{
|
||||
"이름": "지역",
|
||||
@@ -531,7 +531,7 @@
|
||||
}
|
||||
],
|
||||
"중간": [
|
||||
{ "이름": "손료", "식": "버림(EQ000069.가격 * 1000 * (EQ000069.상각비계수 + EQ000069.정비비계수 + EQ000069.관리비계수) / 10000000, 0)", "출처": "산림품셈 10-9 · 건설품셈 공통 8-1-5 12" },
|
||||
{ "이름": "손료", "식": "버림(EQ000655.가격 * 1000 * (EQ000069.상각비계수 + EQ000069.정비비계수 + EQ000069.관리비계수) / 10000000, 0)", "출처": "산림품셈 10-9 · 건설품셈 공통 8-1-5 12" },
|
||||
{ "이름": "비율", "식": "찾기(QF000330, 구분='2.5톤 덤프 주 연료 대비').비율", "출처": "산림품셈 10-9" },
|
||||
{ "이름": "직종", "식": "EQ000069.조종원직종" },
|
||||
{ "이름": "시간환산", "식": "찾기(CC000336, 구분='시간환산').분자 / 찾기(CC000336, 구분='시간환산').분모", "출처": "건설품셈 공통 1-1-3 2(일일 작업시간 8시간)" }
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
{
|
||||
"그룹": "수식문서",
|
||||
"로직파일": "로직_산림품셈_02장_소요재료_및_기계손료.json",
|
||||
"줄": [
|
||||
{
|
||||
"꼴": "수식문서",
|
||||
"판": 1,
|
||||
"키": "GF000337",
|
||||
"이름": "체인톱 시간당 기계경비",
|
||||
"결과단위": "원/시간",
|
||||
"출처": "산림품셈 2-1-1 · 2-2-1",
|
||||
"부품": {
|
||||
"갈래": "기계경비식"
|
||||
},
|
||||
"변수": [
|
||||
{"열쇠": "지역", "기호": "S", "아래": "지역", "위": "", "이름": "지역", "단위": "", "출처": "건설품셈 공통 8-1-7 5", "연결": {"갈래": "선택", "목록": ["전국평균", "전국", "서울", "경기", "강원", "충북", "충남", "전북", "경북", "경남", "부산", "제주", "대구", "인천", "대전", "울산", "세종", "전남광주"], "값": "전국평균"}},
|
||||
{"열쇠": "시간환산", "기호": "Q", "아래": "시간환산", "위": "", "이름": "시간환산", "단위": "", "출처": "건설품셈 공통 1-1-3 2(일일 작업시간 8시간)", "연결": {"갈래": "식"}},
|
||||
{"열쇠": "Q_분자^표", "기호": "Q", "아래": "분자", "위": "표", "이름": "건설기계 조종원 노임 시간 환산 계수.분자", "단위": "", "연결": {"갈래": "표", "표": "CC000336", "조건": {"구분": "'시간환산'"}, "칸": "분자"}},
|
||||
{"열쇠": "Q_분모^표", "기호": "Q", "아래": "분모", "위": "표", "이름": "건설기계 조종원 노임 시간 환산 계수.분모", "단위": "", "연결": {"갈래": "표", "표": "CC000336", "조건": {"구분": "'시간환산'"}, "칸": "분모"}},
|
||||
{"열쇠": "Q_주연료^표", "기호": "Q", "아래": "주연료", "위": "표", "이름": "체인톱·예취기·아키아윈치·2드럼 케이블윈치·천공기 연료.주연료", "단위": "수(단위는 줄의 단위 칸 따름)", "연결": {"갈래": "표", "표": "QF000001", "조건": {"기계명": "'체인톱'", "재료명": "'보통휘발유(주연료)'"}, "칸": "주연료"}},
|
||||
{"열쇠": "P_휘발유", "기호": "P", "아래": "휘발유", "위": "", "이름": "휘발유단가", "단위": "", "연결": {"갈래": "요소", "키": "MO:휘발유:{S_지역}"}},
|
||||
{"열쇠": "Q_주연료2^표", "기호": "Q", "아래": "주연료2", "위": "표", "이름": "체인톱·예취기·아키아윈치·2드럼 케이블윈치·천공기 연료.주연료", "단위": "수(단위는 줄의 단위 칸 따름)", "연결": {"갈래": "표", "표": "QF000001", "조건": {"기계명": "'체인톱'", "재료명": "'체인오일(일반오일)'"}, "칸": "주연료"}},
|
||||
{"열쇠": "P_체인오일", "기호": "P", "아래": "체인오일", "위": "", "이름": "체인오일단가", "단위": "", "연결": {"갈래": "요소", "재료": {"구분": "기계·설비", "상세구분": "기계류", "검색어": "작동유"}, "단위": "ℓ"}},
|
||||
{"열쇠": "k_체인톱", "기호": "k", "아래": "체인톱", "위": "", "이름": "EQ000620.손료계수", "단위": "", "연결": {"갈래": "요소", "키": "EQ000620", "칸": "손료계수"}},
|
||||
{"열쇠": "P_체인톱손료", "기호": "P", "아래": "체인톱손료", "위": "", "이름": "체인톱 손료단가", "단위": "", "연결": {"갈래": "요소", "재료": {"구분": "기계·설비", "상세구분": "기계류", "검색어": "동력체인톱"}, "단위": "대"}},
|
||||
{"열쇠": "Q_잡^표", "기호": "Q", "아래": "잡", "위": "표", "이름": "체인톱·예취기·아키아윈치·2드럼 케이블윈치·천공기 연료.잡품", "단위": "%(주연료비의)", "연결": {"갈래": "표", "표": "QF000001", "조건": {"기계명": "'체인톱'", "재료명": "'보통휘발유(주연료)'"}, "칸": "잡품"}}
|
||||
],
|
||||
"식": [
|
||||
{"기호": "Q_시간환산", "식": "Q_분자^표 ÷ Q_분모^표"}
|
||||
],
|
||||
"금액": [
|
||||
{"이름": "휘발유(체인톱)", "단위": "ℓ", "비목": "재료비", "수량": "Q_주연료^표 × Q_시간환산", "단가": "P_휘발유", "출처": "산림품셈 2-1-1 주③", "기호": "C_휘발유"},
|
||||
{"이름": "체인오일", "단위": "ℓ", "비목": "재료비", "수량": "Q_주연료2^표 × Q_시간환산", "단가": "P_체인오일", "출처": "산림품셈 2-1-1 주④"},
|
||||
{"이름": "체인톱 손료", "단위": "대", "비목": "경비", "수량": "k_체인톱 × Q_시간환산", "단가": "P_체인톱손료", "출처": "산림품셈 2-2-1"},
|
||||
{"이름": "잡품", "단위": "식", "비목": "재료비", "단가": "C_휘발유 × Q_잡^표 ÷ 100", "출처": "산림품셈 2-1-1 주⑤"}
|
||||
],
|
||||
"끝수": {
|
||||
"줄": {"방법": "버림", "자리": 1},
|
||||
"비목": {"방법": "버림", "자리": 0},
|
||||
"끝전": "비목버림"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -482,7 +482,7 @@
|
||||
{"열쇠": "비율", "기호": "r", "아래": "비율", "위": "", "이름": "비율", "단위": "", "출처": "산림품셈 10-9", "연결": {"갈래": "표", "표": "QF000330", "조건": {"구분": "'2.5톤 덤프 주 연료 대비'"}, "칸": "비율"}},
|
||||
{"열쇠": "직종", "기호": "S", "아래": "직종", "위": "", "이름": "직종", "단위": "", "연결": {"갈래": "요소", "키": "EQ000069", "칸": "조종원직종"}},
|
||||
{"열쇠": "시간환산", "기호": "Q", "아래": "시간환산", "위": "", "이름": "시간환산", "단위": "", "출처": "건설품셈 공통 1-1-3 2(일일 작업시간 8시간)", "연결": {"갈래": "식"}},
|
||||
{"열쇠": "P_덤프트럭", "기호": "P", "아래": "덤프트럭", "위": "", "이름": "EQ000069.가격", "단위": "", "연결": {"갈래": "요소", "키": "EQ000069", "칸": "가격"}},
|
||||
{"열쇠": "P_덤프트럭", "기호": "P", "아래": "덤프트럭", "위": "", "이름": "EQ000655.가격", "단위": "", "연결": {"갈래": "요소", "키": "EQ000655", "칸": "가격"}},
|
||||
{"열쇠": "k_덤프트럭", "기호": "k", "아래": "덤프트럭", "위": "", "이름": "EQ000069.상각비계수", "단위": "", "연결": {"갈래": "요소", "키": "EQ000069", "칸": "상각비계수"}},
|
||||
{"열쇠": "k_덤프트럭2", "기호": "k", "아래": "덤프트럭2", "위": "", "이름": "EQ000069.정비비계수", "단위": "", "연결": {"갈래": "요소", "키": "EQ000069", "칸": "정비비계수"}},
|
||||
{"열쇠": "k_덤프트럭3", "기호": "k", "아래": "덤프트럭3", "위": "", "이름": "EQ000069.관리비계수", "단위": "", "연결": {"갈래": "요소", "키": "EQ000069", "칸": "관리비계수"}},
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
"""812-9 지킴 — 가져온 기계 잇기(`M02_Machine_Find` · 요구 718 · 719).
|
||||
|
||||
(a) 기계표 전체에서 찾음 — 낱말표 밖 기종(로우더 · 공기 압축기 · 램 머 · 라인마커 …) · 후보 여럿이던 것
|
||||
(물탱크 · 크레인(무한궤도) · 머캐덤 · 콘크리트 펌프차 …) · 지어 맞추기 없음(1.0Ton = 우리 표에 없음)
|
||||
(물탱크 · 크레인(무한궤도) · 머캐덤 · 콘크리트 펌프차 …) · 지어 맞추기 없음(3.0Ton = 우리 표에 없음)
|
||||
(b) 표기 정규화 · 괄호 낱말(무한궤도 · 타이어 · 일반용 · 표에 없는 낱말 발파 = 안 봄) · 범위 · 복수 규격
|
||||
(c) 조합 기계 = 둘(대형브레이카 +굴착기 · 0.7+브레이카 · +부착용집게 · +진동콤팩터 · 덤프트럭(자동덮개))
|
||||
(d) 별칭 먼저 — M01 기계 줄 「별칭」 칸(815-1 · 시험 안 임시 별칭) · 이름 + 규격 통째 = 그 EQ · 이름만 =
|
||||
@@ -24,7 +24,14 @@ for p in (ROOT, HERE):
|
||||
|
||||
from M02_MasterTemplete import M02_Elem_Alias as ea # noqa: E402
|
||||
from M02_MasterTemplete import M02_Machine_Find as mf # noqa: E402
|
||||
from test_815_1_import_link import aliases # noqa: E402 — M01 별칭 칸 임시
|
||||
from test_815_1_import_link import aliases, no_aliases # noqa: E402 — M01 별칭 칸 임시
|
||||
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def _blank_aliases():
|
||||
"""진짜 마스터 기계 별칭 내용 · 수와 무관 — 별칭이 필요한 시험은 스스로 넣음."""
|
||||
with no_aliases("EQ"):
|
||||
yield
|
||||
|
||||
|
||||
def one(name: str, spec: str = "") -> tuple[str, str, str]:
|
||||
@@ -104,7 +111,7 @@ def test_master_alias(name: str, spec: str, eq: str) -> None:
|
||||
|
||||
def test_no_guess() -> None:
|
||||
"""우리 표에 없는 규격 = 못 찾음(가까운 규격으로 안 이음) · 기종 모름 = 별칭 몫 · 여럿 = 여럿."""
|
||||
got = mf.find("덤프트럭", "1.0Ton")
|
||||
got = mf.find("덤프트럭", "3.0Ton")
|
||||
assert got["판정"] == mf.NONE and got["까닭"] == "우리 표에 없음(규격)" and got["키"] is None
|
||||
assert got["후보"][0]["규격"] == "2.5ton" # 보여주기만 — 가까운 규격 먼저
|
||||
for name, spec in (("에어 호스", "1.91cm"), ("모노펌프", "5-20㎥/hr")): # 승인 전 = 별칭 몫
|
||||
@@ -113,7 +120,7 @@ def test_no_guess() -> None:
|
||||
got = mf.find("크레인(트럭)", "10Ton") # 「트럭」 = 기계표 괄호에 없는 낱말 → 안 봄
|
||||
assert got["판정"] == mf.MANY and got["수"] == 2
|
||||
assert mf.mark(got, "크레인(트럭)", "10Ton").startswith("참고 · 기계 후보 2 — 고르기")
|
||||
assert "기계 못 찾음" in mf.mark(mf.find("덤프트럭", "1.0Ton"), "덤프트럭", "1.0Ton")
|
||||
assert "기계 못 찾음" in mf.mark(mf.find("덤프트럭", "3.0Ton"), "덤프트럭", "3.0Ton")
|
||||
assert mf.mark(mf.find("물탱크", "5500ℓ"), "물탱크", "5500ℓ") is None
|
||||
|
||||
|
||||
@@ -160,10 +167,11 @@ def test_combo_many_part() -> None:
|
||||
def test_alias_first_master_column() -> None:
|
||||
"""마스터 기계 줄 「별칭」 칸(815-2 옮김) — 이름만 별칭 = 그 기종 → 규격으로 하나 ·
|
||||
통째 별칭 · 할증 글 · 후보 여럿 = `test_815_1_import_link.test_alias_machine`."""
|
||||
got = mf.find("아스팔트페이버(피니셔)", "3.0m")
|
||||
assert (got["판정"], got["후보"][0]["이름"], got["후보"][0]["규격"]) == (
|
||||
mf.ALIAS, "아스팔트 피니셔", "3m") # fmt: skip
|
||||
assert mf.find("크레인(트럭)", "10Ton")["판정"] == mf.MANY
|
||||
with aliases("EQ", FIVE): # 다섯만 넣음(더 있어도 비워져 있음)
|
||||
got = mf.find("아스팔트페이버(피니셔)", "3.0m")
|
||||
assert (got["판정"], got["후보"][0]["이름"], got["후보"][0]["규격"]) == (
|
||||
mf.ALIAS, "아스팔트 피니셔", "3m") # fmt: skip
|
||||
assert mf.find("크레인(트럭)", "10Ton")["판정"] == mf.MANY # 크레인 별칭은 비움 · 여럿
|
||||
|
||||
|
||||
def test_combo_money_same() -> None:
|
||||
|
||||
@@ -60,6 +60,11 @@ def aliases(table: str, pairs: dict[str, str]):
|
||||
clear()
|
||||
|
||||
|
||||
def no_aliases(table: str):
|
||||
"""그 표 별칭 칸을 시험 동안 모두 비움 — 진짜 마스터에 별칭이 더 들어가도 시험이 안 흔들림."""
|
||||
return aliases(table, {k: "" for k in ea.book()["줄"][table]})
|
||||
|
||||
|
||||
# ── 별칭 풀이 ─────────────────────────────────────────────────────────────
|
||||
def test_alias_names_rule() -> None:
|
||||
assert am.names("") == [] and am.names(None) == []
|
||||
@@ -99,16 +104,20 @@ def test_est_code_first() -> None:
|
||||
|
||||
# ── K2 별칭 → 요소 ───────────────────────────────────────────────────────────
|
||||
def test_alias_machine() -> None:
|
||||
assert mf.find("에어 호스", "1.91cm")["판정"] == mf.NONE
|
||||
with aliases("EQ", {"EQ000129": '"크레인(트럭) 15Ton"', "EQ000193": "아스팔트페이버(피니셔)"}):
|
||||
with no_aliases("EQ"):
|
||||
assert mf.find("에어 호스", "1.91cm")["판정"] == mf.NONE
|
||||
assert mf.find("크레인(트럭)", "15Ton")["판정"] != mf.ALIAS # 별칭 전
|
||||
with (
|
||||
no_aliases("EQ"),
|
||||
aliases("EQ", {"EQ000129": '"크레인(트럭) 15Ton"', "EQ000193": "아스팔트페이버(피니셔)"}),
|
||||
):
|
||||
got = mf.find("크레인(트럭)", "15Ton") # 이름 + 규격 통째 별칭 = 그 EQ
|
||||
assert (got["판정"], got["키"]) == (mf.ALIAS, "EQ000129")
|
||||
assert mf.find("크레인(트럭)", "15Ton(할증125%)")["키"] == "EQ000129" # 할증 글 안 봄
|
||||
got = mf.find("아스팔트페이버(피니셔)", "3.0m") # 이름 별칭 = 그 기종 → 규격으로 하나
|
||||
assert (got["판정"], got["후보"][0]["이름"], got["후보"][0]["규격"]) == (mf.ALIAS, "아스팔트 피니셔", "3m") # fmt: skip
|
||||
with aliases("EQ", {"EQ000129": "크레인 갑", "EQ000130": "크레인 갑"}):
|
||||
with no_aliases("EQ"), aliases("EQ", {"EQ000129": "크레인 갑", "EQ000130": "크레인 갑"}):
|
||||
assert mf.find("크레인 갑")["판정"] == mf.MANY # 같은 별칭 둘 = 후보 여럿
|
||||
assert mf.find("크레인(트럭)", "15Ton")["판정"] != mf.ALIAS # 되돌림
|
||||
|
||||
|
||||
def test_alias_material() -> None:
|
||||
|
||||
@@ -104,7 +104,8 @@ def test_M01_길은_시스템_관리자만() -> None:
|
||||
|
||||
def test_마스터_기계_별칭_다섯_옮김_장부_걷음() -> None:
|
||||
rows = {r["키"]: r for r in json.loads((REAL / MACHINE).read_text(encoding="utf-8"))["줄"]}
|
||||
assert {k: r.get("별칭") for k, r in rows.items() if r.get("별칭")} == FIVE
|
||||
have = {k: r.get("별칭") for k, r in rows.items() if r.get("별칭")}
|
||||
assert FIVE.items() <= have.items() # 다섯이 들어 있음 · 사용자가 더 넣어도 통과
|
||||
assert not (ROOT / "resources" / "master_template" / "실무자료" / "맞춤.json").exists()
|
||||
|
||||
|
||||
@@ -137,8 +138,9 @@ def _alias_app(role: str = "SYSTEM_ADMIN") -> TestClient:
|
||||
def test_모아_보기_다섯_수기_넣은_것_바로(copy: Path, suggest_file: Path) -> None:
|
||||
client = _alias_app()
|
||||
rows = client.get("/api/m01/alias").json()["줄"]
|
||||
assert {r["키"]: r["별칭"] for r in rows if r["갈래"] == "기계"} == FIVE
|
||||
assert all(r["들어옴"] == "수기" for r in rows)
|
||||
machine = {r["키"]: r["별칭"] for r in rows if r["갈래"] == "기계"}
|
||||
assert FIVE.items() <= machine.items() # 다섯이 들어 있음 · 더 있어도 통과
|
||||
assert all(r["들어옴"] == "수기" for r in rows if r["키"] in FIVE)
|
||||
one = next(r for r in rows if r["키"] == "EQ000248")
|
||||
assert [p["글"] for p in one["조각"]] == ["바이브레이트 봉상후렉시블"]
|
||||
res = client.post("/api/m01/alias/save", json={"키": "LB000001", "별칭": "시험A,시험B"})
|
||||
|
||||
@@ -0,0 +1,91 @@
|
||||
"""816-1 지킴 — 기계표 「덤프트럭(4륜구동) 1ton」(산림품셈 10-9 · 요구 769-1 · 770-1).
|
||||
|
||||
(a) 기계표 줄 · 값 — 원문 산림품셈 10-9 · 가격 28,000 천원(조달청 등재가격 기본값) · 키대장
|
||||
(b) GF000145(4륜구동 1톤 덤프트럭 시간당 기계경비)가 그 줄 가격을 읽음 · 계수 · 연료 · 조종원은 2.5 ton
|
||||
(c) 잇기 — 가져온 「덤프트럭 1.0Ton · 1톤 · 1ton」 → 새 줄 · 「덤프트럭 2.5ton」 → EQ000069 그대로
|
||||
(d) 시간당 경비 한 길 — `M02_Machine_Hourly.hourly` · `M02_Doc_Import.hourly_link` = GF000145
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import importlib
|
||||
import sys
|
||||
from decimal import Decimal
|
||||
from pathlib import Path
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[2]
|
||||
for p in (ROOT, ROOT / "resources/master_data/scripts"):
|
||||
if str(p) not in sys.path:
|
||||
sys.path.insert(0, str(p))
|
||||
|
||||
from M01_MasterData import M01_MasterData_Store as m01 # noqa: E402
|
||||
from M02_MasterTemplete import M02_Doc_Import as di # noqa: E402
|
||||
from M02_MasterTemplete import M02_Machine_Find as mf # noqa: E402
|
||||
from M02_MasterTemplete import M02_Machine_Hourly as mh # noqa: E402
|
||||
from test_815_1_import_link import no_aliases # noqa: E402 — M01 별칭 칸 임시
|
||||
|
||||
ml = importlib.import_module("master_doc_link")
|
||||
mc = importlib.import_module("master_doc_calc")
|
||||
NEW, OLD = "EQ000655", "EQ000069"
|
||||
|
||||
|
||||
def _whole():
|
||||
return m01.loaded()[1]
|
||||
|
||||
|
||||
def _docs() -> dict:
|
||||
return ml.load_all()
|
||||
|
||||
|
||||
def test_row() -> None:
|
||||
row = _whole().get(NEW)
|
||||
assert (row["원문"], row["원문번호"], row["상세구분"]) == ("산림품셈", "10-9", "덤프트럭")
|
||||
assert (row["이름"], row["규격"]) == ("덤프트럭(4륜구동)", "1ton")
|
||||
assert row["값"] == {"가격": 28000} and row["출처"]["가격"] == "산림품셈 10-9"
|
||||
assert "4륜구동 1톤 덤프트럭" in row["비고"]
|
||||
|
||||
|
||||
def test_gf145_reads_price() -> None:
|
||||
doc = _docs()["GF000145"]
|
||||
reads = {(v["연결"].get("키"), v["연결"].get("칸")) for v in doc["변수"]}
|
||||
assert (NEW, "가격") in reads and (OLD, "가격") not in reads
|
||||
assert {
|
||||
(OLD, c) for c in ("상각비계수", "정비비계수", "관리비계수", "주연료", "조종원")
|
||||
} <= reads
|
||||
got = mc.calc(doc, ml.Links(_whole(), _docs()))
|
||||
old = _whole().get(OLD)["값"]
|
||||
loss = int(Decimal(28000 * 1000) * sum(Decimal(str(old[k])) for k in ("상각비계수", "정비비계수", "관리비계수")) / Decimal(10**7)) # fmt: skip
|
||||
assert got["셈됨"] and got["경비"] == loss == 8307
|
||||
|
||||
|
||||
def test_find() -> None:
|
||||
with no_aliases("EQ"): # 진짜 마스터 별칭 있든 없든 같은 결과
|
||||
_find()
|
||||
|
||||
|
||||
def _find() -> None:
|
||||
for spec in ("1.0Ton", "1톤", "1ton"):
|
||||
got = mf.find("덤프트럭", spec)
|
||||
assert got["키"] == NEW and got["판정"] == mf.LINKED, spec
|
||||
assert mf.find("덤프트럭", "2.5ton")["키"] == OLD
|
||||
assert mf.find("덤프트럭", "15Ton")["키"] == "EQ000074"
|
||||
|
||||
|
||||
def test_hourly_one_way() -> None:
|
||||
assert mh.logic_of("10-9")["키"] == "GF000145"
|
||||
assert mh.logic_of("0602-0025")["키"] != "GF000145" # 2.5 ton = 일반 식 그대로
|
||||
got = mh.hourly(NEW)
|
||||
doc = mc.calc(_docs()["GF000145"], ml.Links(_whole(), _docs()))
|
||||
assert got["ok"] and got["로직"] == "GF000145"
|
||||
assert [got[k] for k in ("노무비", "재료비", "경비", "계")] == [float(doc[k]) for k in ("노무비", "재료비", "경비", "계")] # fmt: skip
|
||||
link = di.hourly_link(NEW, "경비")
|
||||
assert link["키"] == "GF000145" and "기계" not in link["입력"] and link["칸"] == "경비"
|
||||
|
||||
|
||||
def test_gf283_option() -> None:
|
||||
"""10-9-1 덤프트럭 운반 「1톤 4륜구동」 옵션 = GF000145 값으로 셈."""
|
||||
links = ml.Links(_whole(), _docs())
|
||||
one = mc.calc(_docs()["GF000283"], links, {"덤프트럭": "1톤 4륜구동"})
|
||||
big = mc.calc(_docs()["GF000283"], links, {"덤프트럭": "0602-0025"})
|
||||
assert one["셈됨"] and big["셈됨"] and one["노무비"] == big["노무비"] # 조종원 = 2.5 ton
|
||||
assert one["경비"] > big["경비"] # 손료 = 1 톤 가격 28,000 > 2.5 ton 21,982
|
||||
@@ -0,0 +1,147 @@
|
||||
"""816-2 지킴 — 「후보 여럿」 기계를 같은 파일 안 구성 대조로 가름(요구 770-2 · 773-3 · 방향 장부 N1).
|
||||
|
||||
잣대 = 같은 파일 · 같은 때의 값끼리(손료 계수 · 가격 · 주연료 · 조종원) — 우리 기계표 값과 맞대지 않음.
|
||||
(a) EST 원파일 MachMainDat 묶기 — main 기계 1,049 대 모두 구성
|
||||
(b) 견본 셋 — 대형브레이카 +굴착기(1.0㎥) → 같은 파일 굴착기(무한궤도) 1.0㎥ → EQ000022 · 크레인(트럭) 10Ton →
|
||||
크레인(타이어) 10Ton → EQ000128 · 굴삭기(유압식백호) 0.6㎥ → EQ000019
|
||||
(c) 파일 값이 우리 표와 달라도 같은 파일 대조로 가름 · 우리 표 값과 같다는 것만으로는 안 이음 · 같은 구성
|
||||
없음 · 여럿 = 그대로
|
||||
(d) EST 엑셀 「기계경비」 시트 길 — 산출근거 줄 → 같은 대조 · 2016 사방 엑셀 기계 12 대 구성 읽음
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import sys
|
||||
from functools import lru_cache
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[2]
|
||||
HERE = Path(__file__).resolve().parent
|
||||
for p in (ROOT, HERE):
|
||||
if str(p) not in sys.path:
|
||||
sys.path.insert(0, str(p))
|
||||
|
||||
from M02_MasterTemplete import M02_Machine_Find as mf # noqa: E402
|
||||
from M02_MasterTemplete import M02_San_Book as book # noqa: E402
|
||||
from M02_MasterTemplete import M02_San_Emit as em # noqa: E402
|
||||
from M02_MasterTemplete import M02_San_Estx as estx # noqa: E402
|
||||
from M02_MasterTemplete import M02_San_Xlsx as sx # noqa: E402
|
||||
|
||||
from test_815_1_import_link import aliases, no_aliases # noqa: E402 — M01 별칭 칸 임시
|
||||
|
||||
MAIN = ROOT / "resources/knowledge/original/경쟁사 프로그램/EST Plus 2/다산소프트/2026-07-main.estx"
|
||||
COMBO = ("대형브레이카", "+굴착기(1.0㎥)")
|
||||
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def _blank_aliases():
|
||||
"""같은 파일 대조는 별칭이 없는 상태에서 봄 — 진짜 마스터 별칭 내용 · 수와 무관."""
|
||||
with no_aliases("EQ"):
|
||||
yield
|
||||
|
||||
|
||||
@lru_cache(maxsize=1)
|
||||
def _tables() -> dict:
|
||||
return estx.tables(MAIN.read_bytes())
|
||||
|
||||
|
||||
@lru_cache(maxsize=1)
|
||||
def _main() -> dict:
|
||||
res = estx.resources(_tables()).values()
|
||||
return {(r["이름"], r["규격"]): r for r in res if r["표"] == "MachTitleDat"}
|
||||
|
||||
|
||||
def _keys(name: str, spec: str, same: list | None) -> list:
|
||||
return [(p["판정"], p["키"]) for p in em.machine_parts({"갈래": "기계", "이름": name, "규격": spec}, same)] # fmt: skip
|
||||
|
||||
|
||||
def test_main_makes() -> None:
|
||||
made = estx.makes(_tables())
|
||||
assert len(made) == 1049 and all(m["손료"] for m in made.values())
|
||||
code = next(r["Code"] for r in _tables()["MachTitleDat"] if (r["Name"], r["Size"]) == COMBO)
|
||||
assert made[code] == {"손료": [[2085.0, 142069000.0], [6601.0, 28692000.0]], "주연료": 19.5, "조종원": True} # fmt: skip
|
||||
assert ["굴착기(무한궤도)", "1.0㎥"] in _main()[COMBO]["같은구성"]
|
||||
|
||||
|
||||
def test_samples() -> None:
|
||||
for (name, spec), want in (
|
||||
(COMBO, "EQ000022"),
|
||||
(("크레인(트럭)", "10Ton"), "EQ000128"),
|
||||
(("굴삭기(유압식백호)", "0.6㎥"), "EQ000019"),
|
||||
):
|
||||
same = _main()[(name, spec)]["같은구성"]
|
||||
assert any(v == mf.MANY for v, _ in _keys(name, spec, None)), name
|
||||
after = _keys(name, spec, same)
|
||||
assert (mf.LINKED, want) in after and all(v != mf.MANY for v, _ in after), name
|
||||
got = em.machine_parts({"갈래": "기계", "이름": "크레인(트럭)", "규격": "10Ton"}, _main()[("크레인(트럭)", "10Ton")]["같은구성"]) # fmt: skip
|
||||
assert got[0]["까닭"].startswith(mf.MADE) and "크레인(타이어)" in got[0]["까닭"]
|
||||
|
||||
|
||||
def test_alias_first() -> None:
|
||||
"""별칭이 있으면 같은 파일 대조보다 먼저(크레인(트럭) 10Ton 별칭 = EQ000128) · 별칭 없으면 여럿."""
|
||||
assert _keys("크레인(트럭)", "10Ton", None) == [(mf.MANY, None)]
|
||||
with aliases("EQ", {"EQ000128": '"크레인(트럭) 10Ton"'}):
|
||||
assert _keys("크레인(트럭)", "10Ton", None) == [(mf.ALIAS, "EQ000128")]
|
||||
|
||||
|
||||
def test_file_values_only() -> None:
|
||||
"""파일 값이 우리 표와 달라도 같은 파일 대조로 가름 · 우리 표 값과 같아도 같은 파일에 짝 없으면 그대로."""
|
||||
odd = {"손료": [[9999.0, 1.0]], "주연료": 1.0, "조종원": True} # 우리 표에 없는 값
|
||||
files = {
|
||||
"a": ("굴착기", "1.0㎥", odd),
|
||||
"b": ("굴착기(무한궤도)", "1.0㎥", odd),
|
||||
"c": ("굴착기(타이어)", "1.0㎥", {**odd, "주연료": 2.0}),
|
||||
}
|
||||
same = mf.peers(files)
|
||||
assert same["a"] == [["굴착기(무한궤도)", "1.0㎥"]]
|
||||
assert _keys("굴착기", "1.0㎥", same["a"]) == [(mf.LINKED, "EQ000022")]
|
||||
ours = {
|
||||
"손료": [[2085.0, 142069000.0]],
|
||||
"주연료": 19.5,
|
||||
"조종원": True,
|
||||
} # 우리 표 무한궤도 값 그대로
|
||||
alone = mf.peers({"a": ("굴착기", "1.0㎥", ours), "c": ("굴착기(타이어)", "1.0㎥", odd)})
|
||||
assert "a" not in alone and _keys("굴착기", "1.0㎥", alone.get("a")) == [(mf.MANY, None)]
|
||||
both = mf.peers(
|
||||
{**files, "c": ("굴착기(타이어)", "1.0㎥", odd)}
|
||||
) # 같은 구성 둘(형식 다름) = 여럿
|
||||
assert _keys("굴착기", "1.0㎥", both["a"])[0][0] == mf.MANY
|
||||
|
||||
|
||||
def test_keep_when_unsure() -> None:
|
||||
same = _main()[("굴삭기(유압식백호)", "1.0㎥;암보정")].get(
|
||||
"같은구성"
|
||||
) # 같은 파일 무한궤도(발파) 2,405
|
||||
assert ["굴착기(무한궤도)", "1.0㎥(발파)"] in same
|
||||
assert _keys("굴삭기(유압식백호)", "1.0㎥;암보정", same) == [(mf.LINKED, "EQ000022")]
|
||||
assert _keys("크레인(트럭)", "10Ton", None) == [(mf.MANY, None)]
|
||||
|
||||
|
||||
def test_excel_sheet() -> None:
|
||||
rows = [
|
||||
["기계경비"],
|
||||
["명 칭", "규 격", "산 출 근 거"],
|
||||
["1. 굴삭기", "1.0㎥", 0.0, 28259.0],
|
||||
["손 료", 100000000.0, "₩×", 2038.0, "×", "10(-7)", 20380.0],
|
||||
["주연료", "경유", 19.5, "ℓ", "×", 993.0, 19363.5],
|
||||
["건설기계운전사", 1.0, "인", "×", 135644.0],
|
||||
[],
|
||||
["2. 굴삭기(무한궤도)", "1.0㎥", 0.0, 28259.0],
|
||||
["손 료", 100000000.0, "₩×", 2038.0, "×", "10(-7)", 20380.0],
|
||||
["주연료", "경유", 19.5, "ℓ", "×", 993.0, 19363.5],
|
||||
["건설기계운전사", 1.0, "인", "×", 135644.0],
|
||||
[],
|
||||
]
|
||||
made = sx.makes(rows)
|
||||
assert made[("굴삭기", "1.0㎥")][2] == {"손료": [[2038.0, 100000000.0]], "주연료": 19.5, "조종원": True} # fmt: skip
|
||||
same = mf.peers(made)[("굴삭기", "1.0㎥")]
|
||||
assert same == [["굴삭기(무한궤도)", "1.0㎥"]]
|
||||
assert _keys("굴삭기", "1.0㎥", same) == [(mf.LINKED, "EQ000022")]
|
||||
import test_797_1a_import_flags as practice
|
||||
|
||||
sad = practice.FILES.get("2016사방")
|
||||
if sad:
|
||||
sheets = book.sheets(sad.name, sad.read_bytes())
|
||||
assert len(sx.makes(sheets["기계경비"])) == 12
|
||||
@@ -0,0 +1,71 @@
|
||||
"""816-3 지킴 — 가져온 「엔진톱」 = 우리 체인톱 EQ000620(요구 771 · 방향 장부 N2).
|
||||
|
||||
(a) 별칭 길 — 사용자가 EQ000620 「별칭」 칸에 「엔진톱 18", 엔진톱」 을 넣으면(시험 = 임시 별칭 · 진짜 마스터
|
||||
별칭은 시험 안에서 비움) 인치 따옴표가 깨지지 않고 「엔진톱 18"」 · 「엔진톱」(규격 빔) → EQ000620 · 다른 톱 · 다른 규격은 안 이음
|
||||
(b) 시간당 = GF000337 「체인톱 시간당 기계경비」 = 산림품셈 1 일 1 대당(2-1-1 휘발유 · 체인오일 · 잡품 ·
|
||||
2-2-1 손료) ÷ 8 — GF000076 체인톱 줄(1 일 1 대당) 단가 그대로 · 수량 × 시간환산
|
||||
(c) 이음 한 길 — `M02_Machine_Hourly.hourly` · `M02_Doc_Import.hourly_link` = GF000337(기계 입력 없음)
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import importlib
|
||||
import sys
|
||||
from decimal import Decimal
|
||||
from pathlib import Path
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[2]
|
||||
for p in (ROOT, ROOT / "resources/master_data/scripts"):
|
||||
if str(p) not in sys.path:
|
||||
sys.path.insert(0, str(p))
|
||||
|
||||
from M01_MasterData import M01_MasterData_Store as m01 # noqa: E402
|
||||
from M02_MasterTemplete import M02_Doc_Import as di # noqa: E402
|
||||
from M02_MasterTemplete import M02_Machine_Find as mf # noqa: E402
|
||||
from M02_MasterTemplete import M02_Machine_Hourly as mh # noqa: E402
|
||||
from test_815_1_import_link import aliases, no_aliases # noqa: E402 — M01 별칭 칸 임시
|
||||
|
||||
ml = importlib.import_module("master_doc_link")
|
||||
mc = importlib.import_module("master_doc_calc")
|
||||
SAW = "EQ000620"
|
||||
|
||||
|
||||
def _calc(key: str) -> dict:
|
||||
docs = ml.load_all()
|
||||
return mc.calc(docs[key], ml.Links(m01.loaded()[1], docs))
|
||||
|
||||
|
||||
def test_alias() -> None:
|
||||
am = importlib.import_module("master_alias")
|
||||
with no_aliases("EQ"): # 진짜 마스터 별칭 있든 없든 — 별칭 전 = 못 이음
|
||||
assert mf.find("엔진톱", '18"')["키"] is None
|
||||
for text in ('엔진톱 18", 엔진톱', '"엔진톱 18"", 엔진톱'):
|
||||
assert am.names(text) == ['엔진톱 18"', "엔진톱"], text
|
||||
with no_aliases("EQ"), aliases("EQ", {SAW: '엔진톱 18", 엔진톱'}):
|
||||
for name, spec in (("엔진톱", '18"'), ("엔진톱", ""), ("엔진톱", '18 "')):
|
||||
got = mf.find(name, spec)
|
||||
assert (got["판정"], got["키"]) == (mf.ALIAS, SAW), (name, spec)
|
||||
for name, spec in (("엔진톱", '24"'), ("체인톱", '18"')):
|
||||
assert mf.find(name, spec)["키"] is None, (name, spec)
|
||||
|
||||
|
||||
def test_hourly_is_day_over_8() -> None:
|
||||
hour = _calc("GF000337")
|
||||
day = {x["이름"]: x for x in _calc("GF000076")["줄"]} # 같은 체인톱 줄(1 일 1 대당 × 벌목부수)
|
||||
lines = {x["이름"]: x for x in hour["줄"]}
|
||||
assert set(lines) == {"휘발유(체인톱)", "체인오일", "체인톱 손료", "잡품"}
|
||||
for name in ("휘발유(체인톱)", "체인오일", "체인톱 손료"):
|
||||
assert lines[name]["단가"] == day[name]["단가"], name
|
||||
assert lines["휘발유(체인톱)"]["수량"] == Decimal("5.6") / 8
|
||||
assert lines["체인톱 손료"]["수량"] == Decimal("0.0084") / 8
|
||||
assert hour["셈됨"] and hour["노무비"] == 0 and hour["계"] > 0
|
||||
|
||||
|
||||
def test_one_way() -> None:
|
||||
got = mh.hourly(SAW)
|
||||
doc = _calc("GF000337")
|
||||
assert got["ok"] and got["로직"] == "GF000337"
|
||||
assert [got[k] for k in ("재료비", "경비", "계")] == [float(doc[k]) for k in ("재료비", "경비", "계")] # fmt: skip
|
||||
link = di.hourly_link(SAW, "재료비")
|
||||
assert link["키"] == "GF000337" and "기계" not in link["입력"]
|
||||
assert mh.hourly("EQ000621")["ok"] is False # 예취기 = 시간당 식 없음 그대로
|
||||
@@ -0,0 +1,192 @@
|
||||
"""817-1 불러오기 재료 찾기 — 표기만 다름 ㉮ 이음 · ㉯ 후보 좁힘 · 틀린 이음 막기 · 별도계상 표지(요구 773-5 · 774).
|
||||
|
||||
과녁 = 사용자 시트 「수동연결(자재)」 100 줄(브레인 과녁 `_과녁_773_자재표기.tsv` 를 우리 자재표로 다시 확인한 결과).
|
||||
「후보」 줄 키 = 1 순위를 굳힌 줄만(종자 · 그레이팅 · 표기만 다름인데 수 묶음 · 이름이 안 맞아 ㉯ 로 둔 줄) · None = 이음만 안 됨.
|
||||
"""
|
||||
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[2]
|
||||
sys.path[:0] = [str(ROOT), str(ROOT / "resources" / "master_data" / "scripts")]
|
||||
|
||||
from M02_MasterTemplete import M02_Hopyo_MatSame as ms # noqa: E402
|
||||
from M02_MasterTemplete import M02_Hopyo_Row as hrow # noqa: E402
|
||||
from M02_MasterTemplete import M02_San_Link as sl # noqa: E402
|
||||
|
||||
TABLE = [
|
||||
("접착제", "", "후보", None), # 1
|
||||
("패널(유로폼)", "600*1200mm", "이음", "MT000467"), # 2
|
||||
("각재", "외송", "후보", "MT000393"), # 3
|
||||
("요소", "", "후보", "MT006763"), # 4
|
||||
("내부코너패널(유로폼)", "(100x200)x1200mm", "이음", "MT000470"), # 5
|
||||
("결속선(철선)", "#20 , 0.9M/M", "후보", None), # 6
|
||||
("각재 (외송)", "", "후보", "MT000393"), # 7
|
||||
("파형강관", "별도계상", "후보", None), # 8
|
||||
("금강사", "", "없음", None), # 9
|
||||
("고무테이프", "0.5mm×19mm×10m", "이음", "MT009202"), # 10
|
||||
("낙석방지책", "H2500×W2000(표준)", "후보", None), # 11
|
||||
("낙석방지책", "H2500×W2000(단부)", "후보", None), # 12
|
||||
("종자", "초본류", "후보", "MT006646"), # 13
|
||||
("종자", "목본류", "후보", "MT006648"), # 14
|
||||
("어닐링철선(결속선)", "#20 0.9mm 200.4m/kg", "이음", "MT000209"), # 15
|
||||
("내수합판1급", "12mmx1220x2440", "이음", "MT000018"), # 16
|
||||
("어닐링철선(결속선)", "#20 0.9M/M", "이음", "MT000209"), # 17
|
||||
("유로폼(패널)", "600*1200", "이음", "MT000467"), # 18
|
||||
("내부코너패널", "(200+200)*1200", "이음", "MT000472"), # 19
|
||||
("합판(내수)", "12×1220×2440", "이음", "MT000018"), # 20
|
||||
("조임철물", "(직교자재)", "이음", "MT000499"), # 21
|
||||
("에폭시수지", "YD-128", "후보", None), # 22
|
||||
("종자", "생태복원형,초본류", "후보", "MT006646"), # 23
|
||||
("종자", "생태복원형,목본류", "후보", "MT006648"), # 24
|
||||
("내부코너패널(유로폼)", "(100+200)*1200mm", "이음", "MT000470"), # 25
|
||||
("유로폼(패널)", "600×1200", "이음", "MT000467"), # 26
|
||||
("콘테이너하우스", "창고용(3.0m×6.0m×2.6m)", "이음", "MT007111"), # 27
|
||||
("전기뇌관(MS)", "3.5m×500개(하이데토)", "이음", "MT005677"), # 28
|
||||
("BUTTON BIT", "Ø38×76㎜", "후보", None), # 29
|
||||
("유압드릴샹크", "T38×CM470", "후보", None), # 30
|
||||
("유압식드릴스리브", "T38×190mm", "후보", "MT005687"), # 31
|
||||
("미송(판재)", "", "후보", None), # 32
|
||||
("산림종자", "참싸리(박피)", "이음", "MT006664"), # 33
|
||||
("유기질퇴비", "", "후보", None), # 34
|
||||
("PVC지수판", "200x5T KSM3805", "이음", "MT006544"), # 35
|
||||
("PVC용접봉", "Ø3.2 (국산)", "후보", None), # 36
|
||||
("PVC파이프(일반용VG2)", "50㎜ × 4m", "이음", "MT002926"), # 37
|
||||
("혼합골재", "영월", "후보", None), # 38
|
||||
("이형철근", "별도계상", "후보", None), # 39
|
||||
("안전표지판", "삼각,D=900", "후보", "MT003202"), # 40
|
||||
("표지판지주", "D=76.3mm, 3.5M", "후보", None), # 41
|
||||
("블레이드(커터)", 'd=320 400mm 14"xt3.2', "이음", "MT005749"), # 42
|
||||
("떼", "", "없음", None), # 43
|
||||
("강관비계", "φ48.6m/m×2.3mm", "이음", "MT000481"), # 44
|
||||
("철물", "", "후보", None), # 45
|
||||
("각재(말뚝)", "Φ60", "후보", None), # 46
|
||||
("강관써포트", "V4 2600-4000mm 14.3kg", "후보", None), # 47
|
||||
("안고초", "", "이음", "MT006667"), # 48
|
||||
("비수리", "", "이음", "MT006665"), # 49
|
||||
("내수합판1급", "12m/m 1220×2440", "이음", "MT000018"), # 50
|
||||
("각재(외송)", "3.6×3.0×3.0", "이음", "MT000393"), # 51
|
||||
("내수합판1급", "12mm 1220×2440", "이음", "MT000018"), # 52
|
||||
("각재(거푸집용,외송)", "3.6×3.0×3.0", "이음", "MT000393"), # 53
|
||||
("다이아몬드톱날", 'Ø14"×3.2t(콘크리트용)', "후보", None), # 54
|
||||
("아세틸렌", "용접용", "후보", "MT003163"), # 55
|
||||
("산소", "고압가스", "이음", "MT003166"), # 56
|
||||
("신나", "KSM6060 2종(조합페인트류)", "이음", "MT003398"), # 57
|
||||
("초류종자(국내산)", "참싸리,비수리,낭아초등", "후보", None), # 58
|
||||
("종 자", "초본형", "후보", "MT006646"), # 59
|
||||
("소나무우드칩(멀칭재)", "", "후보", None), # 60
|
||||
("3단코어비트", '5"(125mm)', "후보", None), # 61
|
||||
("P V C PIPE", "Φ50M/M,VG1", "이음", "MT002861"), # 62
|
||||
("콘테이너(창 고)", "3×6×2.6", "이음", "MT007111"), # 63
|
||||
("강관비계", "48.6mm×2.3mm×6m", "후보", None), # 64
|
||||
("철물", "직교자재", "이음", "MT000499"), # 65
|
||||
("문양거푸집(ABS코팅품)", "600×1200", "이음", "MT000453"), # 66
|
||||
("와이어매쉬", "#6 100×100", "이음", "MT007136"), # 67
|
||||
("지수판", "T5×200", "이음", "MT006544"), # 68
|
||||
("용접봉(연강용)", "3.2m/m", "후보", None), # 69
|
||||
("어닐링철선 (결속선)", "#6 4.0mm 10.1m/kg", "후보", None), # 70
|
||||
("구역화물자동차(4.5Ton)", "80km이내", "이음", "MT023493"), # 71
|
||||
("구역화물자동차(4.5Ton)", "60km이내", "이음", "MT023491"), # 72
|
||||
("산소(6000ℓ)", "99%,120kg/㎠", "이음", "MT003165"), # 73
|
||||
("아세칠렌", "AC 98%,용접용", "이음", "MT003163"), # 74
|
||||
("서비스볼트(고장력볼트)", "M20×60", "이음", "MT002536"), # 75
|
||||
("보조강재(열연강판)", "T=9-12", "후보", None), # 76
|
||||
("구역화물자동차(8Ton)", "10km이내", "이음", "MT023654"), # 77
|
||||
("구역화물자동차(8Ton)", "20km이내", "이음", "MT023655"), # 78
|
||||
("참싸리(박피)", "", "없음", None), # 79
|
||||
("부산물비료", "20kg/포", "후보", None), # 80
|
||||
("참싸리", "박피", "없음", None), # 81
|
||||
("복합비료", "21-17-17 20kg/포", "이음", "MT006490"), # 82
|
||||
("퇴비(천연부엽토)", "20㎏/포", "후보", None), # 83
|
||||
("문양거푸집(ABS코팅폼)", "600×1200×20~30mm", "이음", "MT000453"), # 84
|
||||
("P.V.C PIPE", "VG1(50mm)", "이음", "MT002861"), # 85
|
||||
("다웰바(DOWEL BAR)", "Ø32×1,000", "이음", "MT006540"), # 86
|
||||
("P.V.C PIPE", "VG1(35mm)", "이음", "MT002859"), # 87
|
||||
("휠라", "T=15mm,1000×1000mm", "없음", None), # 88
|
||||
("측구수로관", "300×300×2000×120,앵글포함", "이음", "MT015883"), # 89
|
||||
("중하중그레이팅", "395×995×52", "후보", "MT007025"), # 90
|
||||
("측구수로관", "500×500×2000×150,앵글포함", "이음", "MT015887"), # 91
|
||||
("중하중그레이팅", "595×995×67", "후보", "MT007032"), # 92
|
||||
("잔디", "0.18×0.18×0.01", "이음", "MT006796"), # 93
|
||||
("강관비계", "4000×48.6mm×2.3mm", "이음", "MT000481"), # 94
|
||||
("삼각표지판", "삼각,900mm", "후보", None), # 95
|
||||
("표지판지주", "D=76.3mm×3.0m", "이음", "MT003241"), # 96
|
||||
("공사안내표지판", "300×1500", "후보", None), # 97
|
||||
("공사안전PE드럼", "500×800", "후보", "MT003311"), # 98
|
||||
("윙카호스", "10cm", "후보", None), # 99
|
||||
("조합페인트", "KSM6020 2급,백색", "이음", "MT003372"), # 100
|
||||
]
|
||||
|
||||
|
||||
def _link(name, spec):
|
||||
key, mark = sl.m01("재료", {"이름": name, "규격": spec, "단위": "", "갈래": "재료"})
|
||||
return key, mark, hrow.m01_picks("재료", {"이름": name, "규격": spec, "단위": ""})
|
||||
|
||||
|
||||
@pytest.mark.parametrize("name,spec,want,key", TABLE)
|
||||
def test_target_rows(name, spec, want, key):
|
||||
got, _mark, picks = _link(name, spec)
|
||||
if want == "이음":
|
||||
assert got == key
|
||||
else:
|
||||
assert got is None
|
||||
assert bool(picks) == (want == "후보")
|
||||
if key:
|
||||
assert picks[0]["키"] == key
|
||||
|
||||
|
||||
def test_counts():
|
||||
linked = sum(1 for *_x, w, _k in TABLE if w == "이음")
|
||||
assert linked == 51
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"name,spec,bad",
|
||||
[
|
||||
("용접봉(연강용)", "3.2m/m", "MT003195"), # 연강용 ↔ 주철용
|
||||
("PVC용접봉", "Ø3.2 (국산)", "MT003195"), # PVC 용접봉 ↔ 용접봉
|
||||
("어닐링철선 (결속선)", "#6 4.0mm 10.1m/kg", "MT000199"), # #6 ↔ #8
|
||||
("잔디", "0.18×0.18×0.01", "MT019017"), # 0.01 ↔ 0.03
|
||||
("표지판지주", "D=76.3mm, 3.5M", "MT003241"), # 3.5M ↔ 3000
|
||||
("신나", "KSM6060 2종(조합페인트류)", "MT018835"), # 신나 ↔ 신나무
|
||||
("강관비계", "48.6mm×2.3mm×6m", "MT000481"), # 6m ↔ 4.0m
|
||||
],
|
||||
)
|
||||
def test_no_wrong_link(name, spec, bad):
|
||||
got, _mark, picks = _link(name, spec)
|
||||
assert got != bad
|
||||
if name == "신나":
|
||||
assert got == "MT003398" # 신너(조합페인트용)
|
||||
|
||||
|
||||
def test_seed_narrow():
|
||||
assert [x["자재"] for x in ms.narrow({"이름": "종자", "규격": "초본류"})][:3] == [
|
||||
"MT006646",
|
||||
"MT006647",
|
||||
"MT006649",
|
||||
]
|
||||
|
||||
|
||||
def test_spec_forms():
|
||||
assert ms.num_fit(ms._bag("3.6×3.0×3.0"), ms._bag("30×30×3600mm"))[0] == 2
|
||||
assert ms.num_fit(ms._bag("T5×200"), ms._bag("200×5T"))[0] == 3
|
||||
assert ms.num_fit(ms._bag("Ø32×1,000"), ms._bag("Φ32*1000"))[0] == 3
|
||||
assert ms._item("PVC지수판", "200x5T KSM3805")["bag"] == ms._item("PVC지수판", "200×5T")["bag"]
|
||||
assert ms._item("유로폼(패널)", "")["chars"] == ms._item("유로폼패널", "")["chars"]
|
||||
assert ms._item("P.V.C PIPE", "")["core"] == ms._item("PVC파이프(일반관),서울", "")["core"]
|
||||
assert ms._item("아세칠렌", "")["core"] == "아세틸렌"
|
||||
|
||||
|
||||
def test_apart_mark():
|
||||
key, mark, _ = _link("파형강관", "별도계상")
|
||||
assert key is None and mark == f"{sl.NOTE}{sl.APART}"
|
||||
assert (
|
||||
sl.why({"자원": {"갈래": "재료", "이름": "철근(별도계상)", "규격": "별도계상"}}) == sl.APART
|
||||
)
|
||||
|
||||
|
||||
def test_why_shows_first():
|
||||
got = sl.why({"자원": {"갈래": "재료", "이름": "종자", "규격": "초본류"}})
|
||||
assert got == "자재 후보 여럿 — 고름 · 1 순위 종 자 초본위주형"
|
||||
Reference in New Issue
Block a user