Merge remote-tracking branch 'origin/main_laptop_1' into sub_laptop_3
This commit is contained in:
@@ -137,6 +137,9 @@ COLS = {
|
||||
}
|
||||
COLS["한국은행환율"] = COLS["유가전력"]
|
||||
ALIAS = "별칭" # 요소 줄 덧칸(815-2) — 열 한 벌 밖 · 있으면 빈 글 아님
|
||||
OFF = (
|
||||
"비활성" # 기계 줄 맨 위 칸(842-2) — 있으면 true 만 · 없음 = 켜짐 · 찾기 · 고르기 · 잇기에서 뺌
|
||||
)
|
||||
|
||||
|
||||
def _cols_of(row: dict) -> tuple:
|
||||
@@ -300,6 +303,8 @@ def check_form(name: str, data: dict) -> list[str]:
|
||||
out += _check_labor(where, row)
|
||||
if ALIAS in row and not (isinstance(row[ALIAS], str) and row[ALIAS].strip()):
|
||||
out.append(f"{where} · 「{ALIAS}」 이 빈 글 · 글 아님(빈 칸이면 칸을 두지 않음)")
|
||||
if OFF in row and (row[OFF] is not True or data.get("그룹") != "기계"):
|
||||
out.append(f"{where} · 「{OFF}」 는 기계 줄에만 true(켜짐이면 칸을 두지 않음)")
|
||||
if cols and _cols_of(row) != cols:
|
||||
out.append(f"{where} · 열이 한 벌이 아님 — 「{' · '.join(cols)}」")
|
||||
if book == "자재품목" and row.get(mm.LOAD) not in mm.LOADS:
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
{
|
||||
"키": "EQ000541",
|
||||
"비활성": true,
|
||||
"원문번호": "9010-0003",
|
||||
"원문": "건설품셈",
|
||||
"구분": "[90]해상장비",
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
{
|
||||
"키": "EQ000542",
|
||||
"비활성": true,
|
||||
"원문번호": "9010-0006",
|
||||
"원문": "건설품셈",
|
||||
"구분": "[90]해상장비",
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
{
|
||||
"키": "EQ000543",
|
||||
"비활성": true,
|
||||
"원문번호": "9010-0010",
|
||||
"원문": "건설품셈",
|
||||
"구분": "[90]해상장비",
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
{
|
||||
"키": "EQ000544",
|
||||
"비활성": true,
|
||||
"원문번호": "9010-0012",
|
||||
"원문": "건설품셈",
|
||||
"구분": "[90]해상장비",
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
{
|
||||
"키": "EQ000545",
|
||||
"비활성": true,
|
||||
"원문번호": "9010-0020",
|
||||
"원문": "건설품셈",
|
||||
"구분": "[90]해상장비",
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
{
|
||||
"키": "EQ000546",
|
||||
"비활성": true,
|
||||
"원문번호": "9010-0022",
|
||||
"원문": "건설품셈",
|
||||
"구분": "[90]해상장비",
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
{
|
||||
"키": "EQ000547",
|
||||
"비활성": true,
|
||||
"원문번호": "9010-0033",
|
||||
"원문": "건설품셈",
|
||||
"구분": "[90]해상장비",
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
{
|
||||
"키": "EQ000548",
|
||||
"비활성": true,
|
||||
"원문번호": "9010-0040",
|
||||
"원문": "건설품셈",
|
||||
"구분": "[90]해상장비",
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
{
|
||||
"키": "EQ000549",
|
||||
"비활성": true,
|
||||
"원문번호": "9010-0044",
|
||||
"원문": "건설품셈",
|
||||
"구분": "[90]해상장비",
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
{
|
||||
"키": "EQ000550",
|
||||
"비활성": true,
|
||||
"원문번호": "9010-0060",
|
||||
"원문": "건설품셈",
|
||||
"구분": "[90]해상장비",
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
{
|
||||
"키": "EQ000551",
|
||||
"비활성": true,
|
||||
"원문번호": "9010-0080",
|
||||
"원문": "건설품셈",
|
||||
"구분": "[90]해상장비",
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
{
|
||||
"키": "EQ000552",
|
||||
"비활성": true,
|
||||
"원문번호": "9010-0120",
|
||||
"원문": "건설품셈",
|
||||
"구분": "[90]해상장비",
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
{
|
||||
"키": "EQ000553",
|
||||
"비활성": true,
|
||||
"원문번호": "9010-0200",
|
||||
"원문": "건설품셈",
|
||||
"구분": "[90]해상장비",
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
{
|
||||
"키": "EQ000554",
|
||||
"비활성": true,
|
||||
"원문번호": "9020-0010",
|
||||
"원문": "건설품셈",
|
||||
"구분": "[90]해상장비",
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
{
|
||||
"키": "EQ000555",
|
||||
"비활성": true,
|
||||
"원문번호": "9020-0015",
|
||||
"원문": "건설품셈",
|
||||
"구분": "[90]해상장비",
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
{
|
||||
"키": "EQ000556",
|
||||
"비활성": true,
|
||||
"원문번호": "9020-0016",
|
||||
"원문": "건설품셈",
|
||||
"구분": "[90]해상장비",
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
{
|
||||
"키": "EQ000557",
|
||||
"비활성": true,
|
||||
"원문번호": "9020-0022",
|
||||
"원문": "건설품셈",
|
||||
"구분": "[90]해상장비",
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
{
|
||||
"키": "EQ000558",
|
||||
"비활성": true,
|
||||
"원문번호": "9020-0035",
|
||||
"원문": "건설품셈",
|
||||
"구분": "[90]해상장비",
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
{
|
||||
"키": "EQ000559",
|
||||
"비활성": true,
|
||||
"원문번호": "9020-0050",
|
||||
"원문": "건설품셈",
|
||||
"구분": "[90]해상장비",
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
{
|
||||
"키": "EQ000560",
|
||||
"비활성": true,
|
||||
"원문번호": "9020-0072",
|
||||
"원문": "건설품셈",
|
||||
"구분": "[90]해상장비",
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
{
|
||||
"키": "EQ000561",
|
||||
"비활성": true,
|
||||
"원문번호": "9020-0160",
|
||||
"원문": "건설품셈",
|
||||
"구분": "[90]해상장비",
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
{
|
||||
"키": "EQ000562",
|
||||
"비활성": true,
|
||||
"원문번호": "9020-0180",
|
||||
"원문": "건설품셈",
|
||||
"구분": "[90]해상장비",
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
{
|
||||
"키": "EQ000563",
|
||||
"비활성": true,
|
||||
"원문번호": "9020-0200",
|
||||
"원문": "건설품셈",
|
||||
"구분": "[90]해상장비",
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
{
|
||||
"키": "EQ000564",
|
||||
"비활성": true,
|
||||
"원문번호": "9030-0016",
|
||||
"원문": "건설품셈",
|
||||
"구분": "[90]해상장비",
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
{
|
||||
"키": "EQ000565",
|
||||
"비활성": true,
|
||||
"원문번호": "9030-0018",
|
||||
"원문": "건설품셈",
|
||||
"구분": "[90]해상장비",
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
{
|
||||
"키": "EQ000566",
|
||||
"비활성": true,
|
||||
"원문번호": "9030-0025",
|
||||
"원문": "건설품셈",
|
||||
"구분": "[90]해상장비",
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
{
|
||||
"키": "EQ000567",
|
||||
"비활성": true,
|
||||
"원문번호": "9030-0035",
|
||||
"원문": "건설품셈",
|
||||
"구분": "[90]해상장비",
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
{
|
||||
"키": "EQ000568",
|
||||
"비활성": true,
|
||||
"원문번호": "9030-0045",
|
||||
"원문": "건설품셈",
|
||||
"구분": "[90]해상장비",
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
{
|
||||
"키": "EQ000569",
|
||||
"비활성": true,
|
||||
"원문번호": "9030-0050",
|
||||
"원문": "건설품셈",
|
||||
"구분": "[90]해상장비",
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
{
|
||||
"키": "EQ000570",
|
||||
"비활성": true,
|
||||
"원문번호": "9030-0080",
|
||||
"원문": "건설품셈",
|
||||
"구분": "[90]해상장비",
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
{
|
||||
"키": "EQ000571",
|
||||
"비활성": true,
|
||||
"원문번호": "9030-0100",
|
||||
"원문": "건설품셈",
|
||||
"구분": "[90]해상장비",
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
{
|
||||
"키": "EQ000572",
|
||||
"비활성": true,
|
||||
"원문번호": "9030-0240",
|
||||
"원문": "건설품셈",
|
||||
"구분": "[90]해상장비",
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
{
|
||||
"키": "EQ000573",
|
||||
"비활성": true,
|
||||
"원문번호": "9040-0010",
|
||||
"원문": "건설품셈",
|
||||
"구분": "[90]해상장비",
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
{
|
||||
"키": "EQ000574",
|
||||
"비활성": true,
|
||||
"원문번호": "9040-0030",
|
||||
"원문": "건설품셈",
|
||||
"구분": "[90]해상장비",
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
{
|
||||
"키": "EQ000575",
|
||||
"비활성": true,
|
||||
"원문번호": "9040-0050",
|
||||
"원문": "건설품셈",
|
||||
"구분": "[90]해상장비",
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
{
|
||||
"키": "EQ000576",
|
||||
"비활성": true,
|
||||
"원문번호": "9040-0060",
|
||||
"원문": "건설품셈",
|
||||
"구분": "[90]해상장비",
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
{
|
||||
"키": "EQ000577",
|
||||
"비활성": true,
|
||||
"원문번호": "9040-0100",
|
||||
"원문": "건설품셈",
|
||||
"구분": "[90]해상장비",
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
{
|
||||
"키": "EQ000578",
|
||||
"비활성": true,
|
||||
"원문번호": "9040-0120",
|
||||
"원문": "건설품셈",
|
||||
"구분": "[90]해상장비",
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
{
|
||||
"키": "EQ000579",
|
||||
"비활성": true,
|
||||
"원문번호": "9040-0200",
|
||||
"원문": "건설품셈",
|
||||
"구분": "[90]해상장비",
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
{
|
||||
"키": "EQ000580",
|
||||
"비활성": true,
|
||||
"원문번호": "9040-0250",
|
||||
"원문": "건설품셈",
|
||||
"구분": "[90]해상장비",
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
{
|
||||
"키": "EQ000581",
|
||||
"비활성": true,
|
||||
"원문번호": "9040-0300",
|
||||
"원문": "건설품셈",
|
||||
"구분": "[90]해상장비",
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
{
|
||||
"키": "EQ000582",
|
||||
"비활성": true,
|
||||
"원문번호": "9040-0380",
|
||||
"원문": "건설품셈",
|
||||
"구분": "[90]해상장비",
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
{
|
||||
"키": "EQ000583",
|
||||
"비활성": true,
|
||||
"원문번호": "9040-0680",
|
||||
"원문": "건설품셈",
|
||||
"구분": "[90]해상장비",
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
{
|
||||
"키": "EQ000584",
|
||||
"비활성": true,
|
||||
"원문번호": "9050-0075",
|
||||
"원문": "건설품셈",
|
||||
"구분": "[90]해상장비",
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
{
|
||||
"키": "EQ000585",
|
||||
"비활성": true,
|
||||
"원문번호": "9050-0150",
|
||||
"원문": "건설품셈",
|
||||
"구분": "[90]해상장비",
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
{
|
||||
"키": "EQ000586",
|
||||
"비활성": true,
|
||||
"원문번호": "9050-0450",
|
||||
"원문": "건설품셈",
|
||||
"구분": "[90]해상장비",
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
{
|
||||
"키": "EQ000587",
|
||||
"비활성": true,
|
||||
"원문번호": "9050-0750",
|
||||
"원문": "건설품셈",
|
||||
"구분": "[90]해상장비",
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
{
|
||||
"키": "EQ000588",
|
||||
"비활성": true,
|
||||
"원문번호": "9050-0850",
|
||||
"원문": "건설품셈",
|
||||
"구분": "[90]해상장비",
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
{
|
||||
"키": "EQ000589",
|
||||
"비활성": true,
|
||||
"원문번호": "9060-0060",
|
||||
"원문": "건설품셈",
|
||||
"구분": "[90]해상장비",
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
{
|
||||
"키": "EQ000590",
|
||||
"비활성": true,
|
||||
"원문번호": "9060-0100",
|
||||
"원문": "건설품셈",
|
||||
"구분": "[90]해상장비",
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
{
|
||||
"키": "EQ000591",
|
||||
"비활성": true,
|
||||
"원문번호": "9060-0200",
|
||||
"원문": "건설품셈",
|
||||
"구분": "[90]해상장비",
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
{
|
||||
"키": "EQ000592",
|
||||
"비활성": true,
|
||||
"원문번호": "9060-0300",
|
||||
"원문": "건설품셈",
|
||||
"구분": "[90]해상장비",
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
{
|
||||
"키": "EQ000593",
|
||||
"비활성": true,
|
||||
"원문번호": "9060-0500",
|
||||
"원문": "건설품셈",
|
||||
"구분": "[90]해상장비",
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
{
|
||||
"키": "EQ000594",
|
||||
"비활성": true,
|
||||
"원문번호": "9060-0600",
|
||||
"원문": "건설품셈",
|
||||
"구분": "[90]해상장비",
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
{
|
||||
"키": "EQ000595",
|
||||
"비활성": true,
|
||||
"원문번호": "9070-0015",
|
||||
"원문": "건설품셈",
|
||||
"구분": "[90]해상장비",
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
{
|
||||
"키": "EQ000596",
|
||||
"비활성": true,
|
||||
"원문번호": "9070-0020",
|
||||
"원문": "건설품셈",
|
||||
"구분": "[90]해상장비",
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
{
|
||||
"키": "EQ000597",
|
||||
"비활성": true,
|
||||
"원문번호": "9080-0050",
|
||||
"원문": "건설품셈",
|
||||
"구분": "[90]해상장비",
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
{
|
||||
"키": "EQ000598",
|
||||
"비활성": true,
|
||||
"원문번호": "9080-0080",
|
||||
"원문": "건설품셈",
|
||||
"구분": "[90]해상장비",
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
{
|
||||
"키": "EQ000599",
|
||||
"비활성": true,
|
||||
"원문번호": "9080-0100",
|
||||
"원문": "건설품셈",
|
||||
"구분": "[90]해상장비",
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
{
|
||||
"키": "EQ000600",
|
||||
"비활성": true,
|
||||
"원문번호": "9080-0120",
|
||||
"원문": "건설품셈",
|
||||
"구분": "[90]해상장비",
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
{
|
||||
"키": "EQ000601",
|
||||
"비활성": true,
|
||||
"원문번호": "9080-0150",
|
||||
"원문": "건설품셈",
|
||||
"구분": "[90]해상장비",
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
{
|
||||
"키": "EQ000602",
|
||||
"비활성": true,
|
||||
"원문번호": "9080-0200",
|
||||
"원문": "건설품셈",
|
||||
"구분": "[90]해상장비",
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
{
|
||||
"키": "EQ000603",
|
||||
"비활성": true,
|
||||
"원문번호": "9080-0300",
|
||||
"원문": "건설품셈",
|
||||
"구분": "[90]해상장비",
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
{
|
||||
"키": "EQ000604",
|
||||
"비활성": true,
|
||||
"원문번호": "9080-0500",
|
||||
"원문": "건설품셈",
|
||||
"구분": "[90]해상장비",
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
{
|
||||
"키": "EQ000605",
|
||||
"비활성": true,
|
||||
"원문번호": "9080-0700",
|
||||
"원문": "건설품셈",
|
||||
"구분": "[90]해상장비",
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
{
|
||||
"키": "EQ000606",
|
||||
"비활성": true,
|
||||
"원문번호": "9080-1000",
|
||||
"원문": "건설품셈",
|
||||
"구분": "[90]해상장비",
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
{
|
||||
"키": "EQ000607",
|
||||
"비활성": true,
|
||||
"원문번호": "9080-1100",
|
||||
"원문": "건설품셈",
|
||||
"구분": "[90]해상장비",
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
{
|
||||
"키": "EQ000608",
|
||||
"비활성": true,
|
||||
"원문번호": "9080-1400",
|
||||
"원문": "건설품셈",
|
||||
"구분": "[90]해상장비",
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
{
|
||||
"키": "EQ000609",
|
||||
"비활성": true,
|
||||
"원문번호": "9080-1500",
|
||||
"원문": "건설품셈",
|
||||
"구분": "[90]해상장비",
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
{
|
||||
"키": "EQ000610",
|
||||
"비활성": true,
|
||||
"원문번호": "9080-1750",
|
||||
"원문": "건설품셈",
|
||||
"구분": "[90]해상장비",
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
{
|
||||
"키": "EQ000611",
|
||||
"비활성": true,
|
||||
"원문번호": "9080-2000",
|
||||
"원문": "건설품셈",
|
||||
"구분": "[90]해상장비",
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
{
|
||||
"키": "EQ000612",
|
||||
"비활성": true,
|
||||
"원문번호": "9080-3000",
|
||||
"원문": "건설품셈",
|
||||
"구분": "[90]해상장비",
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
{
|
||||
"키": "EQ000613",
|
||||
"비활성": true,
|
||||
"원문번호": "9090-0800",
|
||||
"원문": "건설품셈",
|
||||
"구분": "[90]해상장비",
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
{
|
||||
"키": "EQ000614",
|
||||
"비활성": true,
|
||||
"원문번호": "9090-1000",
|
||||
"원문": "건설품셈",
|
||||
"구분": "[90]해상장비",
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
{
|
||||
"키": "EQ000615",
|
||||
"비활성": true,
|
||||
"원문번호": "9090-1200",
|
||||
"원문": "건설품셈",
|
||||
"구분": "[90]해상장비",
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
{
|
||||
"키": "EQ000616",
|
||||
"비활성": true,
|
||||
"원문번호": "9090-1300",
|
||||
"원문": "건설품셈",
|
||||
"구분": "[90]해상장비",
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
{
|
||||
"키": "EQ000617",
|
||||
"비활성": true,
|
||||
"원문번호": "9090-1400",
|
||||
"원문": "건설품셈",
|
||||
"구분": "[90]해상장비",
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
{
|
||||
"키": "EQ000618",
|
||||
"비활성": true,
|
||||
"원문번호": "9090-1500",
|
||||
"원문": "건설품셈",
|
||||
"구분": "[90]해상장비",
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
{
|
||||
"키": "EQ000619",
|
||||
"비활성": true,
|
||||
"원문번호": "9090-1600",
|
||||
"원문": "건설품셈",
|
||||
"구분": "[90]해상장비",
|
||||
|
||||
@@ -35,8 +35,8 @@ FILES = {
|
||||
#: 영월 = 내역 줄이 코드로만 가리킴(번 채움) · STmate 하위 호표 반올림 끝수 28 줄
|
||||
#: (줄당 1 원/단위 · 브레인 정함 = 그래도 이음)
|
||||
WANT = {
|
||||
# 841-1 이은 안내판 줄 = M02 사본 셈(엑셀보다 50,000 적음 · 노임 · 기계 덮개 걷음)
|
||||
"거창": (14, 68, 56, 12, 11, 8, 8, 384916227, 0, 50000),
|
||||
# 842-1 이은 안내판 줄 표지판지주 = 별도 → 대가 안 0 · 자재대 50,000 (직접비 합 = 엑셀)
|
||||
"거창": (14, 68, 56, 12, 11, 8, 8, 384916227, 0, 0),
|
||||
"대흥": (34, 171, 143, 28, 0, 26, 45, 710359136, 0, 0),
|
||||
"소광": (25, 117, 98, 19, 0, 0, 0, 173311386, 0, 0),
|
||||
"영월": (23, 138, 135, 3, 0, 21, 34, 767148700, 28, 2951),
|
||||
@@ -117,6 +117,28 @@ def test_실무_엑셀_통째_빠짐_0_합계_같음_층_쓰기_0(world, name):
|
||||
assert not [p for p in world["storage"].rglob("*") if p.is_file()] # 층 쓰기 0
|
||||
|
||||
|
||||
@pytest.mark.parametrize("name", ["거창", "대흥", "소광", "영월"])
|
||||
def test_대가_안_0_원_재료_자재대에_한_번(world, name):
|
||||
"""842-1 전수 — 별도 · 관급 이은 자재(재료 줄 · 고정 줄)마다 집계 `aside` 줄 · 4 자재대 · 관급
|
||||
자재대 수량 = 그 모음(사라짐 0) · 재료 줄 아닌 같은 자재 이중 집계 0."""
|
||||
from B09_Estimation.B09_Estimation_Summary import block_lines
|
||||
|
||||
got = world["run"](name)["payload"]
|
||||
sheets, aside = got["sheets"], got["inputs"].get("aside_qty") or {}
|
||||
apart = {r["key"] for r in sheets["compare"] if r["supply"] in ("별도", "관급")}
|
||||
items = {r["ref"]["key"]: r["qty"] for r in sheets["bill"]["rows"]
|
||||
if r.get("kind") == "item" and str(r["key"]).startswith("mat:")} # fmt: skip
|
||||
for tab in ("sg", "daega_blocks"):
|
||||
for block in sheets[tab]:
|
||||
lines = block_lines([block])[str(block["key"])]["lines"]
|
||||
for ln in block["lines"]:
|
||||
key = (ln.get("ref") or {}).get("key") or (ln.get("link") or {}).get("material")
|
||||
if key in apart and ln["kind"] in ("material", "fixed") and ln.get("qty"):
|
||||
assert any(x["kind"] == "aside" and x["key"] == key for x in lines), ln["name"]
|
||||
assert all(x["kind"] != "material" or x["key"] != key for x in lines)
|
||||
assert items == {k: v for k, v in aside.items() if k in apart}
|
||||
|
||||
|
||||
def test_대흥_총공사비_엑셀과_같음_하위는_참조사본(world):
|
||||
got = world["run"]("대흥")
|
||||
assert got["합계"]["우리총"] == got["합계"]["엑셀총"] == 1201879000
|
||||
@@ -145,7 +167,7 @@ def test_다시_부르기_같은_사본_두_번_셈_0(world):
|
||||
first = world["run"]("거창")
|
||||
again = world["run"]("거창", fresh=False)
|
||||
assert _libs(again["root"]) == _libs(first["root"])
|
||||
assert again["합계"]["우리직접"] == first["합계"]["우리직접"] == 384866227
|
||||
assert again["합계"]["우리직접"] == first["합계"]["우리직접"] == 384916227
|
||||
rows = again["user"]["bill"]["rows"]
|
||||
assert len({r["key"] for r in rows}) == len(rows)
|
||||
|
||||
|
||||
@@ -35,8 +35,11 @@ def test_기계_이름_표_문서_목록_전수() -> None:
|
||||
|
||||
def test_옵션_부품_글만_바꿈() -> None:
|
||||
src = (ROOT / "A00_Common/formula_doc_options.ts").read_text(encoding="utf-8")
|
||||
assert "known[x] ? `${known[x]} (${x})` : x" in src
|
||||
assert "list.map((x) => ({ value: x, text: optionText(x) }))" in src
|
||||
assert "known[x] ? `${known[x]} (${x})${off.has(x)" in src # 842-2 끈 기계 꼬리
|
||||
assert (
|
||||
'liveOptions(list, String(now ?? "")).map((x) => ({ value: x, text: optionText(x) }))'
|
||||
in src
|
||||
)
|
||||
assert "nameMachines(list, () => f.setOptions(items(), f.select.value));" in src
|
||||
api = (ROOT / "M02_MasterTemplete/DataFlow/M02_DF_Api.ts").read_text(encoding="utf-8")
|
||||
assert "machineNames ??= fetch(" in api # 한 번만 받음
|
||||
|
||||
@@ -0,0 +1,82 @@
|
||||
"""842-1 B09 — 대가 안 0 원이 된 재료는 자재대(`aside`)에 꼭 한 번 · 겹쳐 세지 않음.
|
||||
|
||||
고정 줄이 이은 자재(`link.material`)가 별도 · 관급이면 대가 안 0 원 + 그 몫 = 줄 `aside` →
|
||||
집계표 `aside` 줄(자재대 수량) · 재료 줄 · 붙인 줄 · 관급도 같은 길 · 직접계산 = `aside` 없음.
|
||||
거울: `B09_Estimation_Calc_Labor.py` ↔ `.ts` (`fixedApart`).
|
||||
"""
|
||||
|
||||
import json
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[2]
|
||||
if str(ROOT) not in sys.path:
|
||||
sys.path.insert(0, str(ROOT))
|
||||
|
||||
from B09_Estimation.B09_Estimation_Calc_Labor import compute_labor # noqa: E402
|
||||
from B09_Estimation.B09_Estimation_Summary import block_lines, totals # noqa: E402
|
||||
from resources.tester.test_145_2_b09_labor_calc import _b, _l, _same, _ts # noqa: E402
|
||||
|
||||
SHEETS = {
|
||||
"labor": [],
|
||||
"material": [
|
||||
{"key": "MA", "price": 50000, "supply": "별도"},
|
||||
{"key": "MB", "price": 800, "supply": "관급"},
|
||||
{"key": "MC", "price": 300, "supply": None},
|
||||
],
|
||||
"machine": [],
|
||||
"gaps": [],
|
||||
}
|
||||
|
||||
|
||||
def _fix(name, qty, mat, price, **link):
|
||||
own = {"labor": 0.0, "material": price, "expense": 0.0}
|
||||
return {
|
||||
"kind": "fixed",
|
||||
"name": name,
|
||||
"qty": qty,
|
||||
"own": own,
|
||||
"link": {"material": mat, **link},
|
||||
}
|
||||
|
||||
|
||||
DAEGA = {
|
||||
"blocks": [
|
||||
_b(
|
||||
"DG1",
|
||||
"품셈",
|
||||
"안내판",
|
||||
[
|
||||
_fix("지주", 1, "MA", 50000), # 별도 → 대가 안 0 · 자재대 50000
|
||||
_fix("관급 줄", 2, "MB", 800), # 관급 → 자재대(관급)
|
||||
_fix("직접 줄", 3, "MC", 300), # 직접계산 → 대가 안
|
||||
_fix(
|
||||
"기계 겸", 1, "MA", 50000, machine="X1"
|
||||
), # 기계 이음이 먼저여도 자재대 몫은 감
|
||||
{**_l("material", "MA", 4), "own": {"material": 50000}}, # 재료 줄도 같은 자재
|
||||
],
|
||||
),
|
||||
],
|
||||
"owners": {"x": "DG1"},
|
||||
}
|
||||
|
||||
|
||||
def test_고정_줄_자재_대가_안_0_자재대에_한_번():
|
||||
got = compute_labor(DAEGA, SHEETS)["daega_blocks"][0]
|
||||
by = {ln["name"]: ln for ln in got["lines"]}
|
||||
assert by["지주"]["amounts"]["material"] == 0 and by["지주"]["aside"] == 50000
|
||||
assert by["관급 줄"]["amounts"]["material"] == 0 and by["관급 줄"]["aside"] == 1600
|
||||
assert by["직접 줄"]["amounts"]["material"] == 900 and "aside" not in by["직접 줄"]
|
||||
assert got["material"] == 900 # 대가 안 = 직접계산 줄만
|
||||
lines = block_lines([got])[got["key"]]["lines"]
|
||||
assert [x for x in lines if x["kind"] == "material"] == [ # 직접 줄만 일반 재료 줄
|
||||
{"kind": "material", "key": "MC", "qty": 3}
|
||||
]
|
||||
aside = totals({"bill": {"rows": [{"kind": "item", "qty": 1, "ref": {"key": got["key"]}}]}},
|
||||
{got["key"]: {"lines": lines}})[0]["aside"] # fmt: skip
|
||||
assert aside == {"MA": 1 + 1 + 4, "MB": 2} # 재료 줄 4 + 고정 줄 둘 = 한 번씩 모음
|
||||
|
||||
|
||||
def test_거울_파이썬_TS():
|
||||
py = json.loads(json.dumps(compute_labor(DAEGA, SHEETS), ensure_ascii=False))
|
||||
assert _same(py, _ts(DAEGA, SHEETS, {})["out"])
|
||||
@@ -0,0 +1,118 @@
|
||||
"""842-2 기계 비활성 — 지킴 시험(쓰기는 임시 사본만).
|
||||
|
||||
① 틀 검사 — 기계 줄 맨 위 칸 `비활성` = true 만 · 다른 그룹 줄 · false 는 오류
|
||||
② 저장 왕복 — M01 저장 길이 표지를 받고(그 대 파일 하나) 지우면 칸이 사라짐 · 셈 값 그대로
|
||||
③ 끈 기계는 찾기 · 고르기 · 잇기에서 안 나옴 — 요소 찾기 창 · 비슷한 기계 · 불러오기 기계 잇기 ·
|
||||
목록 거름 「켜짐」 · 끈 원문번호 목록 · 셈(시간당)은 그대로
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import copy
|
||||
import shutil
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
|
||||
from M01_MasterData import M01_MasterData_Store as store
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[2]
|
||||
sys.path.insert(0, str(ROOT / "resources" / "master_data" / "scripts"))
|
||||
import master_machine_files as mmf # noqa: E402
|
||||
|
||||
from M01_MasterData import M01_MasterData_Machine as m01m # noqa: E402
|
||||
from M02_MasterTemplete import M02_Machine_Find as mfind # noqa: E402
|
||||
from M02_MasterTemplete import M02_Machine_Hourly as mh # noqa: E402
|
||||
from M02_MasterTemplete.DataFlow import M02_DF_Router as dfr # noqa: E402
|
||||
|
||||
REAL = store.FOLDER
|
||||
cm, mk, M = store.cm, store.mk, mmf.NAME
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def master(tmp_path: Path, monkeypatch) -> Path:
|
||||
for path in REAL.glob("*.json"):
|
||||
if not path.name.startswith("_") or path.name == mk.BOOK.name:
|
||||
shutil.copy(path, tmp_path / path.name)
|
||||
shutil.copytree(REAL / mmf.DIR, tmp_path / mmf.DIR)
|
||||
monkeypatch.setattr(store, "FOLDER", tmp_path)
|
||||
return tmp_path
|
||||
|
||||
|
||||
def _rows() -> list[dict]:
|
||||
return store.read(M)[0]["줄"]
|
||||
|
||||
|
||||
def _part(*changes: dict) -> dict:
|
||||
return {"file": M, "version": store.read(M)[1], "changes": list(changes)}
|
||||
|
||||
|
||||
# ① 틀 검사 ──────────────────────────────────────────────────────────
|
||||
def test_form_check() -> None:
|
||||
data = copy.deepcopy(store.read(M)[0])
|
||||
data["줄"] = data["줄"][:3]
|
||||
assert cm.check_form(M, data) == []
|
||||
data["줄"][0][cm.OFF] = True
|
||||
assert cm.check_form(M, data) == []
|
||||
data["줄"][0][cm.OFF] = False
|
||||
assert any(cm.OFF in e for e in cm.check_form(M, data))
|
||||
labor = copy.deepcopy(store.read("인력.json")[0])
|
||||
labor["줄"] = labor["줄"][:1]
|
||||
labor["줄"][0][cm.OFF] = True
|
||||
assert any(cm.OFF in e for e in cm.check_form("인력.json", labor))
|
||||
|
||||
|
||||
# ② 저장 왕복 ───────────────────────────────────────────────────────
|
||||
def test_save_round_trip(master: Path) -> None:
|
||||
key = next(r["키"] for r in _rows() if not r.get(cm.OFF) and r["이름"].startswith("불도저"))
|
||||
row = copy.deepcopy(next(r for r in _rows() if r["키"] == key))
|
||||
before = mh.hourly(key)
|
||||
snap = {p.name: p.read_bytes() for p in (master / mmf.DIR).glob("*.json")}
|
||||
store.save([_part({"op": "edit", "key": key, "row": {**row, cm.OFF: True}})])
|
||||
after = {p.name: p.read_bytes() for p in (master / mmf.DIR).glob("*.json")}
|
||||
assert {n for n in after if after[n] != snap[n]} == {f"{key}.json"}
|
||||
got = next(r for r in _rows() if r["키"] == key)
|
||||
assert got[cm.OFF] is True and got["값"] == row["값"]
|
||||
assert mh.hourly(key) == before # 셈은 그대로(쓰던 단가산출 · 사본이 깨지지 않음)
|
||||
assert key not in {x["ref"] for x in store.elements("기계", "불도저", 200)["items"]}
|
||||
store.save([_part({"op": "edit", "key": key, "row": row})])
|
||||
assert (master / mmf.DIR / f"{key}.json").read_bytes() == snap[f"{key}.json"]
|
||||
|
||||
|
||||
# ③ 찾기 · 고르기 · 잇기에서 뺌 ──────────────────────────────────────
|
||||
def _off() -> list[dict]:
|
||||
got = [r for r in _rows() if r.get(cm.OFF)]
|
||||
if not got:
|
||||
pytest.skip("끈 기계 없음")
|
||||
return got
|
||||
|
||||
|
||||
def test_hidden_everywhere() -> None:
|
||||
off = _off()
|
||||
keys = {r["키"] for r in off}
|
||||
for r in off[:: max(1, len(off) // 6)]:
|
||||
name = str(r["상세구분"] or r["이름"]).replace(" ", "")
|
||||
assert not keys & {x["ref"] for x in store.elements("기계", name, 200)["items"]}
|
||||
assert not keys & {x["키"] for x in m01m.find(name, 200)}
|
||||
with mfind.m01_only():
|
||||
got = mfind.find(r["이름"], r.get("규격") or "")
|
||||
assert got["키"] not in keys and not keys & {x["키"] for x in got["후보"]}
|
||||
listed = store.rows(M, 1, 500, "", state="켜짐")
|
||||
assert not keys & {r["키"] for r in listed["rows"]}
|
||||
assert listed["total"] == len(_rows()) - len(keys)
|
||||
assert store.rows(M, 1, 1, "")["total"] == len(_rows()) # 같이 보기 = 전부
|
||||
codes = {str(r["원문번호"]) for r in off} - {
|
||||
str(r["원문번호"]) for r in _rows() if not r.get(cm.OFF)
|
||||
}
|
||||
assert set(dfr.get_machine_off()) == codes
|
||||
assert all(c in dfr.get_machine_names() for c in codes) # 고른 값 글은 그대로
|
||||
|
||||
|
||||
def test_sea_machines_off() -> None:
|
||||
"""842-2 ㉣ 해상 기계([90]해상장비 = 선원류 칸 있는 대)는 끔 — 사용자가 다시 켤 수 있으므로 켠 대는 알림만."""
|
||||
sea = [r for r in _rows() if "해상" in str(r.get("구분"))]
|
||||
assert len(sea) == 79
|
||||
on = [r["키"] for r in sea if not r.get(cm.OFF)]
|
||||
if on:
|
||||
pytest.skip(f"다시 켠 해상 기계 {len(on)} 대 — 사용자 판단")
|
||||
Reference in New Issue
Block a user