pfs 유사작업 매뉴얼 (playbook)
새 pfs 작업 시 여기서 유사작업을 찾아 절차 + 참고 포인터만 펼쳐 정보수집 후 진행한다. 불변 사실(경로·포트·인증·위험규칙)은
handoff.md"확정 사실", 함정은traps.md, 스키마는schema-estimate.md.
작업유형 → 참고 포인터 인덱스 (빠른 진입)
- 발주서정리/입고일정 UI 변경 → handoff.md(해당 기능 NOW) · 세션
session-2026-06-*-orderpaper-*· 이 playbook §1 - 운영 배포 → 이 playbook §2 · handoff.md "확정 사실"(deploy-live 규칙)
- RDS 컬럼 추가/조회 → 이 playbook §3 · handoff.md "확정 사실"(SSM·*_user 패턴)
- Playwright 화면 검증 → 이 playbook §4 · 메모리 [[browser-automation-login-default]]
- 견적서/엑셀 → schema-estimate.md · 세션
session-*-estimate-* - 견적서(양식) xlsx → 본건+추가건 견적 등록 → 이 playbook §7(도어발주서 경로와 다르다 — 입력이 이미 확정가 견적서)
- 도어발주서 → 추가건 견적 등록(엑셀→PFS API) → 스킬 문서
C:\dev\order-automation\견적등록_스킬.md(시트구조·수집규칙[GROUP=1줄·부품접음·유상만]·등록 메커니즘[POST /api/auth/login→POST /api/estimate/, 세션 regist_user, 응답{code,message}]·실행build_and_register.py) · 이력=추가1~6=견적 712/713/716/717/720/737(부모 711·field_no 2098; 추가5=20p 수동포함 엣지케이스, 추가6=23~27p 5줄build_and_register_add6.py·p27 재생산행 U무표기=미청구 skip) · 금강 부산에코 = 견적 753(부모 735·field_no 2142=MH 본건. 발주서는 AS 현장 2163 이지만 견적은 본건에 붙인다·1~9p 47줄build_and_register_kk_add1.py·타입란=상차일 3그룹·비고=p{페이지} / 사유·상차일은 M4number_format으로만 식별·부품판정은 숫자접두 아닌 바 부재명 집합) - worklog 미연결 현장 자동연결 → 이 playbook §6 · handoff NOW(2026-06-22 worklog 자동연결) · 메모리 [[worklog-autolink-inferred-sites]]
§1 발주서정리/입고일정(orderpaper) UI 변경
- 파일: 메인=
web/templates/basic/field/field_orderpaper.html(템플릿 — 캐시버스터 불요), 입고일정 탭=web/static/js,css/orderpaper_schedule.js,css(정적 —?v=캐시버스터 필수, base.html/포함부 bump). - 데이터: 단일소스
/api/orderpaper/all-items(select_all_items) →OP_ALL. 동기화안전 사용자편집은*_user컬럼(material_type/receive_date/item_receive_date/note/qty/…)·SELECT는COALESCE(xxx_user, xxx). - 절차: ①현재 구현은 handoff NOW + 직전 세션문서로 파악 ②동일 패턴(예: 인라인 편집=note/qty 미러) 재사용 ③테스트 push→검증→운영(§2) ④세션문서·handoff 갱신.
- 참고: 폐기/되돌린 구현은 백업 브랜치
wip-discarded-*에서git show로 재참조.
§2 운영 배포 절차 (master→deploy-live)
- master에 push(테스트 자동배포) →
gh run watch <id> --exit-status로 확인. - 운영은 페페가 "운영 배포해줘" 명시 시에만.
git checkout deploy-live→git merge master→ 충돌 시 master 버전 채택(orderpaper류) → push. - 강제푸시·
reset --hard도구 차단 → 일반 머지/커밋만. 되돌림은 포워드 복원(파일을 목표 커밋으로 checkout 후 일반 커밋). - 배포 후 운영 Playwright 검증(§4) → handoff/세션 갱신 → notify-step/done.
- 참고: deploy-live↔master 순수차이=
migrations/create_dispatch_tables.sql(무해). - 미반영 커밋 세기(2026-07-27): 운영은 cherry-pick 이력이라 해시가 달라deploy-live..master원시 개수는 과대계상(89건→실제 20건). patch-id 비교로 세라:git log --no-merges --cherry-pick --right-only origin/deploy-live...origin/master→ 그 결과도git diff --stat origin/deploy-live origin/master와 대조해 diff 0인 것 제외. - 선별 배포(일부 커밋만): merge는 원치 않는 커밋까지 끌고 온다 → 파일단위 포워드 반영git checkout deploy-live→git checkout origin/master -- <files>→ 그룹별 커밋(위험 낮은 순: 스크립트→백엔드 파서→웹UI) → push 1회(그룹마다 push하면 운영 재시작 N회). 전제 = 대상 파일이 제외할 커밋에 걸치지 않을 것(git log ... -- <file>로 확인). - 관리 페이지: 미반영 커밋 현황·배포 체크리스트 =C:\dev\devplan\pfs.html(devplan 허브 pfs 카드). 배포하면 그 파일의PENDING/DONE배열을 갱신한다.
§3 RDS 컬럼 추가/조회 (SSM 경유)
- 운영 직결 불가 → EC2(SSM) 경유.
python scripts/ssm_run.py <local.py> <remote.py>(env: 테스트/home/ubuntu/env/pfs-test.env, 운영/home/ubuntu/env/pfs-live.env). - 읽기(SELECT)=자유, 쓰기(ALTER/UPDATE)=위험작업·승인 후. 멱등 스크립트(존재 확인 후 ADD). 동기화안전 편집은
xxx_user varchar NULL AFTER xxx패턴. - 출력 cp949 깨짐 →
PYTHONIOENCODING=utf-8 PYTHONUTF8=1+ em-dash 회피.
§4 Playwright 화면 검증
- 로그인:
C:\dev\.secrets\pfs_login.env(키PFS_TEST_/PFS_PROD_ URL·ID·PW) 읽어 폼 자동입력→로그인. 세션 만료 잦음(재로그인). 비번 출력·커밋 금지. - 검증은
browser_evaluate로 computed style·DOM·API 라운드트립 실측. 데이터 없는 동작은 메모리상 임시변경(비저장)으로 재현. - 참고: 상세 = 메모리 [[browser-automation-login-default]].
§5 알림(broker)
/notify-step(다단계)·/notify-done(완료). Bash+json.dumps(ensure_ascii=False).encode('utf-8')+urllib(PowerShell 한글깨짐 회피). 민감정보 금지.
§7 견적서(양식) xlsx → 본건+추가건 등록 (2026-07-30 신설)
- 입력 = NAS
2026현장\견적\<월>\<업체>\*.xlsx(견적서생성기 산출 = PFS 출력양식과 동일 구조). 도어발주서 경로(§0 스킬)와 달리 단가가 이미 고객 확정가다. - NAS 마운트: 한글 공유명이라
net use·heredoc은 cp949로 깨져 오류67 →pfs\orderpaper-sync\_mount_nas.py(win32wnet) 실행 후 UNC 직접 읽기. 로컬 사본으로 복사해data_only=True로 읽는다. - 시트 판독: 블록 주기 45행 · 본문
base+17부터 20행. p1 = 요약블록(타입행들 + 시공비 + 물류비 + 기타부가비), p2~ = 타입별 상세블록(+소계행). 매핑은 추측 금지 —api/pfs/estimate/service/estimate_print_service.py의_render_summary_body/_render_detail_item셀 주소에서 역산한다(요약과 상세의 컬럼이 다르다. 상세 =rules.md🆕). 섹션헤더 =A{r}:E{r}병합행(품목 아님, 비수집) / 품명칸 다행 병합(A74:B76) = 1품명 다규격 → 행마다 견적줄 + 품명은 앵커 상속. - 등록 순서 = ①본건(
parent_estimate_no='') → ②추가건(parent_estimate_no=<본건no>) → ③extra/bulk-save. 본건이 없는 현장이면 본건은 빈 컨테이너(금액 0)로 만든다(추가건은 부모가 필수). - 필드 규약:
type_name= p1 요약행 이름 그대로 ·dimension1=300(→자수 1.0 →amount = 단가×수량) ·field_type='K'·price_div='pr'·price_option='10'·part_mode='all'· 마진은rules.md🆕(master 0 + detail NULL).total_amount= 품목합 + 시공비 + 물류비 + extras,margin_total_amount= 동일값(서버는 계산 안 하고 받은 값을 저장한다 —_normalize_master). - 검증 4단: ①빌드 시 금액 교차검증(품목합 == p2 공급가액 == p1 타입행 / +기타 == p1 합계) ②
VERIFY=1로 현장·중복 사전조회 ③등록 후GET /api/estimate/{no}전 줄 대조 ④GET /api/estimate/print/excel/{본건no}실제 다운로드 → 원본 엑셀과 셀 대조(최종 관문). - 선례 =
build_and_register_shinyoung_add1.py(신영 광주 챔피언스시티 MH → 본건 740·추가1 741, 13줄 1,177,000). 세션 =session-2026-07-30-shinyoung-add1-estimate.md.
§6 worklog 추정현장 자동연결 (표준규칙 — 페페 지시 2026-06-22 "앞으로도")
- 표준규칙: worklog 작업을 할 때마다 미연결(field_no NULL) 묶음 중 현장이 추정된 것은 자동연결하고, 추정 안 된 건 그대로 유지한다(모호·약한 매칭은 건드리지 않음).
- 실행(가장 간단): 운영 러너
python scripts/_wl_autolink_run.py prod dry 80로 먼저 dry-run 검토 → 연결예정 목록이 타당하면... prod apply 80적용(롤백로그scripts/_wl_autolink_rollback_prod.json자동저장). 테스트는test. env 로그인(비번 미출력). UI로는 worklog 페이지🔗 추정 현장 자동연결버튼(미리보기→적용). - 연결 게이트(보수적):
POST /api/worklog/auto-link {dry_run,threshold=80}. 점수≥80 & 최상위 후보 유일 & 정규화현장명≥3자 & 부분일치 커버리지≥0.5(_MIN_COVER)일 때만 연결. 정확일치(100)는 무조건, "(추정)" 주석붙은 강한부분일치(80·고커버)도 연결. lowcover/ambiguous/weak/tooshort/none은 보류. - 불변/주의: 적용은 배포된 API 경유만(이 PC aws/SSM 미복구 — RDS 직결 불가). 추정 로직 출처=프론트
field_worklog.htmlwlSimScore(서버 포팅=worklog_service._wl_sim_score). 게이트 변경 시 단위테스트pfs/scripts/test_worklog_autolink_infer.py갱신. 코드 위치=api/pfs/worklog/{dao,service,controller}. - 진행중/대기중(active) 현장 우선 (페페 지시 2026-06-22): 자동연결 게이트는 동점(모호)일 때
_is_active_field(상차일 최근 180일/미래=활성)로 winners 중 active가 정확히 1개면 그것으로 확정. 즉 완료된 옛 현장 대신 진행중 현장으로 모호성 해소. (active 후보가 0개·복수면 그대로 모호=skip.) - 맥락파악 매핑 (auto-link로 안 잡히는 나머지 — 절차): 문자열 게이트로 안 되는 모호/약한 묶음은 맥락(통화 발췌·연락처·작업내용) 으로 LLM 판단. 절차: ① 미연결 묶음 + 각 항목(
/api/worklog/items의work_name·excerpt·note[오디오출처=업체/담당]·status) 수집 ② 묶음별 현장후보 shortlist(업체/토큰 매칭, active 표시) 생성 ③ 서브에이전트 청크 분할로 판단(업체·연락처 일치 1순위 + 지역은 내용으로 + active 우선 + 배차/배송/거래처 등 비-현장 skip + 모호 skip) ④ high/med-active만PATCH /api/worklog/site-field {old_site_name,new_field_no,new_site_name}로 연결 + 롤백로그 ⑤ 비활성 타겟·후보없음·모호(활성 복수)는 유지. 스크립트 패턴 =pfs/scripts/_wl_mapping_input.json빌더·_wl_chunk_*·_wl_decision_*·_wl_semantic_rollback_prod.json. 이력=2026-06-22 26묶음 49항목 연결(미연결 154→128).