knowledge(경쟁사): STmate 자재 채택 규칙 확정 — 0 아닌 후보 중 최저가
07번이 C등급으로 두고 「최저가·최신가·특정 슬롯 우선 중 어느 하나라고 단정할 근거가 없다」 한 항목을 실증으로 확정. 출력 XLSX 의 자재단가대비표에 후보 다섯 칸(조달가격·물가자료·물가정보· 거래가격·견적단가)과 적용 단가·출처가 나란히 있어 직접 검정함. - 실무 6건 전수 186건 중 186건이 후보 최저가와 일치 - 후보 값이 2종 이상인 76건 전부 최저가 - 후보 값이 3종 이상인 36건 전부 최저가 - 최저가가 아닌 후보를 택한 건 0, 후보 밖 값을 쓴 건 0 곁들여 - COSTN 의 여섯 칸 중 여섯 번째가 적용 단가 칸으로 보임 - 후보 없이 적용만 있는 25건은 유가 자료, 적용 0 인 49건은 별도계상 - 남은 것은 사용자 수동 고정 시의 우선순위 (T10) 09번이 꼽은 여섯 호환성 차단점 중 자재 자동채택이 풀림. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Prk9BCHG1EMAywk9k8wegA
This commit is contained in:
@@ -58,6 +58,7 @@
|
||||
| 차수 인계 | 「가정하지 말 것」 경고만 | **A 실증** — `BOQ12`가 현재안, 다음 파일 차수1로 인계 | 16번 §4.5·§4.6, 08번 |
|
||||
| 중기 시간당 사용료 | 손료 계수 없음 | **A** — 손료율·취득가·연료·잡품률 25개 장비 실측 | 17번 §3·§4 |
|
||||
| 호표 성분 소계 반올림 | 미확정 | **A** — 절사 (중기 398건 100%) | 17번 §2 |
|
||||
| 자재 6후보 자동채택 | C | **A 실증** — 0 아닌 후보 중 **최저가**. 복수후보 76/76 | 17번 §5.5, 07번 §5 |
|
||||
|
||||
분석일: 2026-09-13
|
||||
대상: `C:\경영정보(주)`
|
||||
|
||||
@@ -89,7 +89,13 @@ price_candidate(material_id, revision_id, slot_no, source_name, page, observed_p
|
||||
price_adoption(material_id, revision_id, chosen_slot_no?, adopted_price, method, user_override)
|
||||
```
|
||||
|
||||
자동선정은 현재 C등급이다. 최저가·최신가·특정 슬롯 우선 중 어느 하나라고 단정할 근거가 없다. 동일 구현 전에는 후보 조합을 통제한 STmate 시험이 필수다: 6개 슬롯에 서로 다른 값, 동률, 0, 공란을 배치하고 채택값과 `PG_1` 변화를 기록한다.
|
||||
**2026-09-13 — 자동선정은 「0이 아닌 후보 중 최저가」로 확정됐다. A등급 실증.**
|
||||
|
||||
출력 XLSX의 `자재단가대비표`에 후보 다섯 칸과 적용 단가·출처가 나란히 있어 직접 검정했다. 실무 6건 전수에서 **후보 값이 2종 이상인 76건 전부, 3종 이상인 36건 전부 최저가**였고, 최저가가 아닌 후보를 택한 건은 0이다. 상세는 17번 §5.5.
|
||||
|
||||
`COSTN`의 여섯 칸 중 **여섯 번째가 적용 단가 칸**으로 보이며, 이는 「노무 L은 후보6과 채택가가 거의 일치」라는 위 관찰과 맞는다.
|
||||
|
||||
남은 것은 **사용자 수동 고정이 걸렸을 때의 우선순위**다. 표본에는 수동 고정 흔적이 없었다. 통제시험(13번 T10)에서 6개 슬롯에 서로 다른 값·동률·0·공란을 배치하고 수동 고정을 섞어 채택값과 `PG_1` 변화를 기록한다.
|
||||
|
||||
## 6. 반올림 경계
|
||||
|
||||
|
||||
@@ -106,6 +106,38 @@
|
||||
|
||||
`깬잡석찰쌓기 58,889`가 제 1 호표의 합계와 같다. **호표 사이에 참조가 걸리므로 위상순서로 계산해야 한다** — 09번 §3이 말한 의존 그래프가 실제로 필요하다.
|
||||
|
||||
## 5.5 자재 채택 규칙 — **최저가**, A등급 실증
|
||||
|
||||
출력 XLSX의 `자재단가대비표` 시트에 **후보 다섯 칸과 적용 단가·출처가 나란히** 있다.
|
||||
|
||||
```text
|
||||
호표 | 명칭 | 규격 | 단위 | 조달가격 | 물가자료 | 물가정보 | 거래가격 | 견적단가 | 적용단가 | 출처
|
||||
1 | 철못 | N 50 | KG | 0 | 1,665 | 1,935 | 1,680 | 0 | 1,665 | 물자
|
||||
6 | PVC파이프 | VG1 | M | 0 | 4,327 | 4,702 | 3,475 | 0 | 3,475 | 거래
|
||||
19 | 와이어메쉬 | #6 | M2 | 3,546 | 4,970 | 5,510 | 0 | 4,545 | 3,546 | 조달
|
||||
```
|
||||
|
||||
실무 프로젝트 6건 전수로 검정했다.
|
||||
|
||||
| 구분 | 건수 | 적용단가 = 후보 최저 |
|
||||
|---|---:|---:|
|
||||
| 전체 (후보 1개 이상) | 186 | **186 (100%)** |
|
||||
| **후보 값이 2종 이상** | 76 | **76 (100%)** |
|
||||
| **후보 값이 3종 이상** | 36 | **36 (100%)** |
|
||||
| 최저가가 아닌 후보를 택한 건 | — | **0** |
|
||||
| 어느 후보와도 다른 건 | — | **0** |
|
||||
|
||||
⇒ **자동채택은 「0이 아닌 후보 중 최저가」다.** 07번 §5가 「최저가·최신가·특정 슬롯 우선 중 어느 하나라고 단정할 근거가 없다」며 C등급으로 둔 항목이 **실증으로 확정**됐다.
|
||||
|
||||
곁들여 확인된 것.
|
||||
|
||||
- 후보 슬롯은 화면상 **다섯**(조달가격·물가자료·물가정보·거래가격·견적단가)이고, `COSTN`의 `AMT_11..AMT_61` 여섯 칸 중 **여섯 번째가 적용 단가 칸**으로 보인다. 07번이 「노무 L은 후보6과 채택가가 거의 일치」라 관찰한 것과 맞는다.
|
||||
- 후보가 하나도 없는데 적용 단가만 있는 행이 25건 있다(경유·휘발유 등 **유가 자료**, 별도 배포 단가).
|
||||
- 적용 단가가 0인 행 49건은 `별도계상`(이형철근·레미콘)·`조달수수료(재료비×0.54%)`처럼 **다른 자리에서 계산되는** 것이다.
|
||||
- **출처 표시는 슬롯 이름의 축약**이다(물자=물가자료, 물정=물가정보, 거래, 조달, 견적). 슬롯 번호에 기관명을 코드로 박으면 안 된다는 07번 경고는 그대로 유효하다 — `Start8x.Ini`가 표시명을 바꾼다.
|
||||
|
||||
⚠ **사용자 수동 고정이 있으면 이 규칙을 벗어난다.** 표본 6건에는 수동 고정 흔적이 없었을 뿐이며, 수동 우선순위는 여전히 통제시험(T10) 대상이다.
|
||||
|
||||
## 6. 이로써 바뀌는 판정
|
||||
|
||||
| 항목 | 15번의 판정 | 지금 |
|
||||
@@ -113,12 +145,13 @@
|
||||
| 중기 시간당 단가 재현 | ❌ 손료 계수가 어디에도 없음 | ✅ **호표에 손료율·취득가·연료·잡품률이 전부 있음** |
|
||||
| 단가 조립 규칙 | ❌ `BDQTY` 암호화로 불가 | ✅ **출력 시트로 조립 규칙·반올림 확인됨** |
|
||||
| 성분 소계 반올림 | 미확정 | ✅ **절사** (중기 398건 100%) |
|
||||
| 자재 6후보 채택 | ❌ C등급, 규칙 없음 | ✅ **최저가** — 복수후보 76/76 |
|
||||
|
||||
여전히 막힌 것은 **입력 쪽**이다.
|
||||
|
||||
- `BDQTY`의 수량 **할증** 필드(`X_QTY`·`X_HAL`) 실값 — 출력에는 할증 후 수량만 남는다
|
||||
- 품셈에서 구성 수량이 **어떻게 뽑혔는가**
|
||||
- 자재 6후보 중 **채택 규칙**
|
||||
- 사용자가 **수동 고정**했을 때의 우선순위 (T10)
|
||||
|
||||
## 7. 다음
|
||||
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""자재 채택 규칙 검정 — 적용단가가 후보 중 무엇인가."""
|
||||
import sys, io, pathlib, warnings, collections
|
||||
from decimal import Decimal as D
|
||||
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding="utf-8", errors="replace")
|
||||
warnings.filterwarnings("ignore"); import openpyxl
|
||||
def dec(v):
|
||||
if v in (None,""): return None
|
||||
try: return D(str(v))
|
||||
except Exception: return None
|
||||
ROOT = pathlib.Path(r"D:\aislo-wt\sub\resources\knowledge\original\실무문서")
|
||||
SLOTS = [("조달가격",5),("물가자료",7),("물가정보",9),("거래가격",11),("견적단가",13)]
|
||||
APPLY = 15
|
||||
stat = collections.Counter(); examples=[]
|
||||
files=0
|
||||
for p in sorted(ROOT.rglob("*.xlsx")):
|
||||
if p.name.startswith("~$"): continue
|
||||
try: wb = openpyxl.load_workbook(p, data_only=True, read_only=True)
|
||||
except Exception: continue
|
||||
if "자재단가대비표" not in wb.sheetnames: continue
|
||||
files += 1
|
||||
ws = wb["자재단가대비표"]
|
||||
for row in ws.iter_rows(min_row=5, max_row=ws.max_row, max_col=17, values_only=True):
|
||||
name = row[1]
|
||||
if not name or not str(name).strip(): continue
|
||||
cands = {nm: dec(row[i-1]) for nm,i in SLOTS}
|
||||
nz = {k:v for k,v in cands.items() if v not in (None, D(0))}
|
||||
app = dec(row[APPLY-1]); src = (str(row[APPLY]) if len(row)>APPLY and row[APPLY] else "").strip()
|
||||
if app in (None, D(0)):
|
||||
stat["적용단가 없음(별도계상 등)"] += 1; continue
|
||||
if not nz:
|
||||
stat["후보 없이 적용단가만(유가·별도자료)"] += 1; continue
|
||||
mn = min(nz.values()); mx = max(nz.values())
|
||||
if len(set(nz.values())) >= 2:
|
||||
stat["복수후보 총"] += 1
|
||||
if app == mn: stat["복수후보 최저"] += 1
|
||||
elif app in nz.values(): stat["복수후보 최저아닌 후보"] += 1
|
||||
else: stat["복수후보 후보밖"] += 1
|
||||
if len(set(nz.values())) >= 3:
|
||||
stat["3개이상 총"] += 1
|
||||
if app == mn: stat["3개이상 최저"] += 1
|
||||
if app == mn: stat["최저가와 일치"] += 1
|
||||
elif app == mx: stat["최고가와 일치"] += 1
|
||||
elif app in nz.values(): stat["후보 중 하나지만 최저 아님"] += 1
|
||||
else:
|
||||
stat["어느 후보와도 다름"] += 1
|
||||
if len(examples)<6: examples.append((p.stem[:26], str(name)[:18], nz, app, src))
|
||||
stat["총"] += 1
|
||||
|
||||
print("### 자재 채택 규칙 검정 — 파일 %d개" % files)
|
||||
tot = stat["총"]
|
||||
for k in ("최저가와 일치","후보 중 하나지만 최저 아님","최고가와 일치","어느 후보와도 다름"):
|
||||
print(" %-24s %4d건 %5.1f%%" % (k, stat[k], 100*stat[k]/tot if tot else 0))
|
||||
print(" %-24s %4d건" % ("(후보 없이 적용만)", stat["후보 없이 적용단가만(유가·별도자료)"]))
|
||||
print(" %-24s %4d건" % ("(적용단가 0/별도계상)", stat["적용단가 없음(별도계상 등)"]))
|
||||
print(" ── 검정 대상 합계 %d건" % tot)
|
||||
print()
|
||||
for k in ("복수후보 총","복수후보 최저","복수후보 최저아닌 후보","복수후보 후보밖","3개이상 총","3개이상 최저"):
|
||||
print(" %-22s %4d건" % (k, stat[k]))
|
||||
if examples:
|
||||
print("\n### 어느 후보와도 다른 예")
|
||||
for f,n,nz,app,src in examples:
|
||||
print(" %-26s %-18s 적용 %-10s 출처 %-6s 후보 %s" % (f,n,app,src,
|
||||
", ".join("%s=%s"%(k,v) for k,v in nz.items())))
|
||||
Reference in New Issue
Block a user