2026-09-15 세션 — 부자재 발주서 신규(/field/sub-orderpaper) — 기록: [코디]
논의·대안
[페페] 지시 = 「Pfs에 부자재 발주서 기능 추가해줘. 기존 엑셀 발주서 및 자재관리 페이지 확인해서 추천안으로 구성해줘. 기본규칙은 입력시에는 해당 현장 업체를 사용자가 섞어서 입력 하고, 추후 정렬로 정리할수 있다. 발주서는 업체별로 분리 출력 가능하게. 개별 출력 가능하게. 운영배포」.
먼저 PFS 에 이미 있는 두 발주 자리를 확인했다.
① /field/orderpaper(자재관리·발주서 정리) = 하류다. NAS 발주서 엑셀을 파싱해 발주 추적·입고 관리만 한다.
자재분류 파생에 S = 부자재가 이미 있다.
② /field/al-orderpaper(AL발주서) = 상류다. 도어 스펙 → 계산 → AL업체·유리업체 발주서 엑셀.
→ 부자재는 상류가 통째로 비어 있었다. 지금까지는 NAS 원부자재발주서.xlsx 의 「발주서」 탭(기타자재 범용폼)을
탭 복제 → 업체명으로 rename → 수기로 채워 왔다. 이번 것이 그 수기를 대체한다.
하류 테이블(orderpaper)에 얹지 않고 새 테이블로 갔다. 그쪽은 item_key = sha1(파일,sheet,section,material_type)
로 NAS 파일과 묶여 있어서, 웹에서 만든 행을 끼워 넣으면 다음 동기화가 전 행을 새 행으로 만들어
사용자 편집·상태·사용이력이 통째로 끊긴다.
착수 전 상의 3갈래 — 코덱스(gptapp.py ask)·계획 서브에이전트(Fable)·web-designer.
- 코덱스 3지적 중 「입력순서와 표시순서를 분리 보존」이 이 화면의 뼈대가 됐다.
- Fable 계획이 치명적 결함을 잡아냈다 — 양식 24행이 품목행이 아니라 캡노트 행이라는 것(아래 함정).
내 자체검사는 그걸 통과했었다(B열 품명만 봤다). 11분 무응답이라 생존감시가 STALL 을 물었지만
스톨이 아니라 긴 추론이었다 — 끊지 않고 기다린 게 맞았다.
- web-designer 는 코드 정독만 했다(테스트서버 미접속). 처방은 거의 그대로 반영했고,
미리보기·인쇄는 뺐다(아래 「뺀 것」).
결정
| 결정 | 고른 것 | 이유 |
|---|---|---|
| 정렬 | 입력순(seq)을 저장하고 화면 표시만 바꾼다 |
정렬을 저장에 반영하면 되돌릴 수단이 없다. 버튼은 업체별 정렬 ↔ 입력순으로 토글 하나 |
| 업체 경계 | 그룹 머리행(좌 4px 라인 + 연한 배경 + 건수) | 발주서 정리 탭 tr.blocksep 과 같은 표기. 업체 칸은 지우지 않는다(오분류 검증용) |
| 개별 출력 | 그룹 머리행 우측 「이 업체만 출력」 | 업체 목록 패널을 따로 두는 건 과설계. 정렬 전에는 버튼이 없다 = 「정렬해 확인하고 뽑아라」가 자연스러운 흐름 |
| 출력 시점 | 전체·개별 둘 다 저장 → 다운로드 | 화면과 엑셀이 어긋나지 않는다. 개별이 전체의 그 시트와 셀 단위로 같음을 검사로 박았다 |
| 자동완성 | 과거 부자재 이력(orderpaper 부자재 파생 S + 자기 저장분) |
경첩·레일처럼 늘 같은 품목을 다시 타이핑하지 않는다. 품명을 고르면 규격이 비었을 때만 따라 들어온다 |
| 목록 UI | droplist.js 한 벌 |
datalist·네이티브 select 금지. 공용 파일은 안 고치고 이 화면에서만 13px 로 덮었다(26.9.14 rules 선례 그대로) |
| 붙여넣기 | 탭·개행 텍스트를 여러 행으로 분배 | 「섞어서 빠르게」의 절반은 엑셀에서 긁어오는 것이다 |
함정 (새로 확립)
- 🔴양식 24행은 품목행이 아니라 캡노트 행이다(
C24:H24병합 = 「현장명, 상품 내역 표기 바랍니다」). 거기 규격·발주량·입고일을 쓰면 openpyxl 이 예외도 없이 값을 버린다. → 품목행은[8..23] + [25]= 17행. ⛒검사는 품명(B열)이 아니라 «규격·수량»까지 봐야 한다 — B24 는 병합 밖이라 멀쩡히 써져서 통과했었다. - 🔴pymysql 집계값(
SUM/COUNT)은DECIMAL이라jsonify가 못 싼다 → 500. 테스트서버 첫 배포에서/suggest가 그걸로 죽었다. 집계는ORDER BY에만 쓰고 응답에서 뺐다. - ⚠
copy_worksheet는 인쇄설정·페이지나눔을 안 옮긴다. A4·가로맞춤·여백을 손으로 복사하고row_breaks는 블록마다 다시 넣는다. - ⚠블록 복제는 «채우기 전에» 끝낸다. 병합을 건 뒤에는 MergedCell 이라
value대입이 read-only 로 막힌다. - ⚠입고처가 바뀌면 블록을 나눈다 — 26행 입고처가 블록당 한 칸이라, 섞이면 뒷 항목 입고처가 앞 것으로 조용히 찍힌다.
- ⚠메뉴 행이 없으면 일반 계정만 403 이다(super_admin 은 통과) →
ensure_tables()가 메뉴행까지 넣는다.
구현
- 신규 14파일 =
C:\dev\pfs\api\pfs\sub_orderpaper\(controller/service/dao/model/sub_orderpaper_excel.py/assets/sub_order_template.xlsx) ·C:\dev\pfs\web\templates\basic\field\field_sub_orderpaper.html·C:\dev\pfs\web\static\js\sub_orderpaper.js(ES module) ·C:\dev\pfs\migrations\patch50_sub_orderpaper.sql - 수정 2 =
C:\dev\pfs\api\config.py(4줄) ·C:\dev\pfs\web\pfs\basic\controller\field_controller.py(7줄) - 로컬 도구(gitignore) =
C:\dev\pfs\scripts\_sub_order_template_build.py(템플릿 재현) ·_sub_orderpaper_menu_seed.py·_sub_orderpaper_e2e_test.py·_sub_orderpaper_ui_check.py·_sub_suggest_probe.py - 테이블 =
sub_orderpaper+sub_orderpaper_item(ensure_tables()멱등 자가생성). 업체 마스터는 AL발주서select_vendor_master()를 그대로 재사용(거래처 실시간 — 스냅샷 금지, 26.8.19 QA).
검증
- 엑셀 단위
_demo()— 캡노트 보호 · 블록 복제 · 수식 보존 · 시트명 3규칙 · 입고처 분리 · 개별출력 ≡ 전체출력 셀 단위 · 업체 없는 항목만이면 ValueError. PASS - 템플릿 빌더 verify — 1탭 · 병합 85 · 데이터유효성 0 · 24행 캡노트 보존. PASS
- 테스트서버 API E2E 21항목(
_sub_orderpaper_e2e_test.py) — ALL PASS (섞어 저장 → 재조회가 입력순 그대로 · seq 0..4 · 업체 3곳 = 시트 3장 · 개별 = 1장 · 24행 캡노트 그대로 · 17번째가 25행에 규격·수량까지 · 18번째가 2블록 37행) - 테스트서버 화면 Playwright 16항목(
_sub_orderpaper_ui_check.py) — ALL PASS (datalist 0 · 자체 드롭리스트 팝업 · 드롭리스트·그리드 13px · 정렬 후 업체끼리 묶임 · 입력순 복귀 · 개별출력 버튼 3 · 마지막 행 Enter = 행 추가 · 콘솔 에러 0) - 배포 = master
dddfdc3→7867849→cca900f→ 운영caf0357(선별 포워드, blob 해시 5개 대조 일치).field_controller.py는 master 통째로 안 가져왔다 — 다른 세션의 worktray 라우트가 섞여 있어 내 7줄만 넣었다.
뺀 것 · 남은 것
- 화면 미리보기·인쇄·팩스 = 뺐다. 엑셀 양식을 HTML 로 다시 그리는 미러가 필요한데(AL발주서는 수백 줄), 엑셀을 열어 인쇄하면 되는 일이다. 페페가 화면에서 바로 인쇄하고 싶다 하면 그때 넣는다.
- 입고 지연 경고·입고 추적 = 뺐다. 이 화면은 작성·출력까지. 추적은 기존 자재관리 몫.
- ⏳페페 판단 = 이 화면이 만든 발주서를 입고 체크(intake-check) 파이프라인에 태울지.
태우려면
orderpaper하류와 어떻게 이을지부터 정해야 한다. - ⏳전역 사안 =
al-btn.green(#0d9488) 흰 글자 대비 3.75:1 로 기준 미달. 이 화면만#0f766e로 진하게 썼다. AL발주서까지 맞추려면 페페가 전역으로 한 번에 정해야 한다(이 화면만 고치면 색이 갈린다). - ⏳페페 육안 = 운영
/field/sub-orderpaper에서 실제 현장 하나를 섞어 입력 → 정렬 → 전체·개별 엑셀.